Fixed NULL ptr refernece that could occur if a client sets negotiation
[yazproxy-moved-to-github.git] / src / yaz-proxy.cpp
1 /* $Id: yaz-proxy.cpp,v 1.31 2005-06-10 17:54:11 adam Exp $
2    Copyright (c) 1998-2005, Index Data.
3
4 This file is part of the yaz-proxy.
5
6 YAZ proxy is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 YAZ proxy is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with YAZ proxy; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #ifdef WIN32
23 #define HAVE_SYS_STAT_H 1
24 #define HAVE_SYS_TYPES_H 1
25 #endif
26
27 #if HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #if HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif
33 #if HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36 #if HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39
40 #include <assert.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include <fcntl.h>
44
45 #include <yaz/srw.h>
46 #include <yaz/marcdisp.h>
47 #include <yaz/yaz-iconv.h>
48 #include <yaz/log.h>
49 #include <yaz/diagbib1.h>
50 #include "proxyp.h"
51 #include <yaz/pquery.h>
52 #include <yaz/otherinfo.h>
53 #include <yaz/charneg.h>
54 #include "msg-thread.h"
55
56 using namespace yazpp_1;
57
58 class Auth_Msg : public IMsg_Thread {
59 public:
60     int m_ret;
61     void destroy();
62     IMsg_Thread *handle();
63     void result();
64     Yaz_Proxy *m_proxy;
65     Z_APDU *m_apdu;
66 };
67
68 IMsg_Thread *Auth_Msg::handle()
69 {
70     yaz_log(YLOG_LOG, "Auth_Msg:handle begin");
71     m_ret = m_proxy->handle_authentication(m_apdu);
72     yaz_log(YLOG_LOG, "Auth_Msg:handle end");
73     return this;
74 }
75
76 void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret)
77 {
78     if (ret == 0)
79     {
80         Z_APDU *apdu_reject = zget_APDU(odr_encode(), Z_APDU_initResponse);
81         *apdu_reject->u.initResponse->result = 0;
82         send_to_client(apdu_reject);
83         shutdown();
84     }
85     else
86         handle_incoming_Z_PDU_2(apdu);
87 }
88
89 void Auth_Msg::result()
90 {
91     yaz_log(YLOG_LOG, "Auth_Msg:result");
92     m_proxy->result_authentication(m_apdu, m_ret);
93     delete this;
94 }
95
96 static const char *apdu_name(Z_APDU *apdu)
97 {
98     switch (apdu->which)
99     {
100     case Z_APDU_initRequest:
101         return "initRequest";
102     case Z_APDU_initResponse:
103         return "initResponse";
104     case Z_APDU_searchRequest:
105         return "searchRequest";
106     case Z_APDU_searchResponse:
107         return "searchResponse";
108     case Z_APDU_presentRequest:
109         return "presentRequest";
110     case Z_APDU_presentResponse:
111         return "presentResponse";
112     case Z_APDU_deleteResultSetRequest:
113         return "deleteResultSetRequest";
114     case Z_APDU_deleteResultSetResponse:
115         return "deleteResultSetResponse";
116     case Z_APDU_scanRequest:
117         return "scanRequest";
118     case Z_APDU_scanResponse:
119         return "scanResponse";
120     case Z_APDU_sortRequest:
121         return "sortRequest";
122     case Z_APDU_sortResponse:
123         return "sortResponse";
124     case Z_APDU_extendedServicesRequest:
125         return "extendedServicesRequest";
126     case Z_APDU_extendedServicesResponse:
127         return "extendedServicesResponse";
128     case Z_APDU_close:
129         return "close";
130     }
131     return "other";
132 }
133
134 static const char *gdu_name(Z_GDU *gdu)
135 {
136     switch(gdu->which)
137     {
138     case Z_GDU_Z3950:
139         return apdu_name(gdu->u.z3950);
140     case Z_GDU_HTTP_Request:
141         return "HTTP Request";
142     case Z_GDU_HTTP_Response:
143         return "HTTP Response";
144     }
145     return "Unknown request/response";
146 }
147
148 Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
149                      ISocketObservable *the_socket_observable,
150                      Yaz_Proxy *parent) 
151     :
152     Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
153 {
154     m_PDU_Observable = the_PDU_Observable;
155     m_socket_observable = the_socket_observable;
156     m_client = 0;
157     m_parent = parent;
158     m_clientPool = 0;
159     m_seqno = 1;
160     m_keepalive_limit_bw = 500000;
161     m_keepalive_limit_pdu = 1000;
162     m_proxyTarget = 0;
163     m_default_target = 0;
164     m_proxy_negotiation_charset = 0;
165     m_proxy_negotiation_lang = 0;
166     m_charset_converter = new Yaz_CharsetConverter;
167     m_max_clients = 150;
168     m_log_mask = 0;
169     m_seed = time(0);
170     m_client_idletime = 600;
171     m_target_idletime = 600;
172     m_optimize = xstrdup ("1");
173     strcpy(m_session_str, "0 ");
174     m_session_no = 0;
175     m_bytes_sent = 0;
176     m_bytes_recv = 0;
177     m_bw_hold_PDU = 0;
178     m_bw_max = 0;
179     m_pdu_max = 0;
180     m_max_record_retrieve = 0;
181     m_reconfig_flag = 0;
182     m_config_fname = 0;
183     m_request_no = 0;
184     m_invalid_session = 0;
185     m_referenceId = 0;
186     m_referenceId_mem = nmem_create();
187     m_config = 0;
188     m_marcxml_mode = none;
189     m_stylesheet_xsp = 0;
190     m_stylesheet_nprl = 0;
191     m_s2z_stylesheet = 0;
192     m_s2z_database = 0;
193     m_schema = 0;
194     m_backend_type = 0;
195     m_backend_charset = 0;
196     m_frontend_type = 0;
197     m_initRequest_apdu = 0;
198     m_initRequest_mem = 0;
199     m_initRequest_preferredMessageSize = 0;
200     m_initRequest_maximumRecordSize = 0;
201     m_initRequest_options = 0;
202     m_initRequest_version = 0;
203     m_initRequest_oi_negotiation_charsets = 0;
204     m_initRequest_oi_negotiation_num_charsets = 0;
205     m_initRequest_oi_negotiation_langs = 0;
206     m_initRequest_oi_negotiation_num_langs = 0;
207     m_initRequest_oi_negotiation_selected = 0;
208     m_apdu_invalid_session = 0;
209     m_mem_invalid_session = 0;
210     m_s2z_odr_init = 0;
211     m_s2z_odr_search = 0;
212     m_s2z_init_apdu = 0;
213     m_s2z_search_apdu = 0;
214     m_s2z_present_apdu = 0;
215     m_http_keepalive = 0;
216     m_http_version = 0;
217     m_soap_ns = 0;
218     m_s2z_packing = Z_SRW_recordPacking_string;
219 #if HAVE_GETTIMEOFDAY
220     m_time_tv = xmalloc(sizeof(struct timeval));
221     struct timeval *tv = (struct timeval *) m_time_tv;
222     tv->tv_sec = 0;
223     tv->tv_usec = 0;
224 #else
225     m_time_tv = 0;
226 #endif
227     m_usemarcon_ini_stage1 = 0;
228     m_usemarcon_ini_stage2 = 0;
229     m_usemarcon = new Yaz_usemarcon();
230     if (!m_parent)
231         low_socket_open();
232     m_my_thread = 0;
233 }
234
235 Yaz_Proxy::~Yaz_Proxy()
236 {
237     yaz_log(YLOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
238             m_bytes_sent, m_bytes_recv);
239     nmem_destroy(m_initRequest_mem);
240     nmem_destroy(m_mem_invalid_session);
241     nmem_destroy(m_referenceId_mem);
242
243     xfree(m_proxyTarget);
244     xfree(m_default_target);
245     xfree(m_proxy_negotiation_charset);
246     xfree(m_proxy_negotiation_lang);
247     delete m_charset_converter;
248     xfree(m_optimize);
249
250 #if HAVE_XSLT
251     if (m_stylesheet_xsp)
252         xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
253 #endif
254     xfree (m_time_tv);
255
256     xfree (m_schema);
257     xfree (m_backend_type);
258     xfree (m_backend_charset);
259     xfree (m_usemarcon_ini_stage1);
260     xfree (m_usemarcon_ini_stage2);
261     delete m_usemarcon;
262     if (m_s2z_odr_init)
263         odr_destroy(m_s2z_odr_init);
264     if (m_s2z_odr_search)
265         odr_destroy(m_s2z_odr_search);
266     if (!m_parent)
267         low_socket_close();
268     if (!m_parent)
269         delete m_my_thread;
270     delete m_config;
271 }
272
273 void Yaz_Proxy::set_debug_mode(int mode)
274 {
275     m_debug_mode = mode;
276 }
277
278 int Yaz_Proxy::set_config(const char *config)
279 {
280     delete m_config;
281     m_config = new Yaz_ProxyConfig();
282     xfree(m_config_fname);
283     m_config_fname = xstrdup(config);
284     int r = m_config->read_xml(config);
285     if (!r)
286         m_config->get_generic_info(&m_log_mask, &m_max_clients);
287     return r;
288 }
289
290 void Yaz_Proxy::set_default_target(const char *target)
291 {
292     xfree (m_default_target);
293     m_default_target = 0;
294     if (target)
295         m_default_target = (char *) xstrdup (target);
296 }
297
298 void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang)
299 {
300     yaz_log(YLOG_LOG, "%sSet the proxy negotiation: charset to '%s', "
301         "language to '%s'", m_session_str, charset?charset:"none",
302         lang?lang:"none");
303     xfree (m_proxy_negotiation_charset);
304     xfree (m_proxy_negotiation_lang);
305     m_proxy_negotiation_charset = m_proxy_negotiation_lang = 0;
306     if (charset)
307         m_proxy_negotiation_charset = (char *) xstrdup (charset);
308     if (lang)
309         m_proxy_negotiation_lang = (char *) xstrdup (lang);
310 }
311
312 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
313 {
314     if (m_parent)
315         return m_parent->check_reconfigure();
316
317     Yaz_ProxyConfig *cfg = m_config;
318     if (m_reconfig_flag)
319     {
320         yaz_log(YLOG_LOG, "reconfigure");
321         yaz_log_reopen();
322         if (m_config_fname && cfg)
323         {
324             yaz_log(YLOG_LOG, "reconfigure config %s", m_config_fname);
325             int r = cfg->read_xml(m_config_fname);
326             if (r)
327                 yaz_log(YLOG_WARN, "reconfigure failed");
328             else
329             {
330                 m_log_mask = 0;
331                 cfg->get_generic_info(&m_log_mask, &m_max_clients);
332             }
333         }
334         else
335             yaz_log(YLOG_LOG, "reconfigure");
336         m_reconfig_flag = 0;
337     }
338     return cfg;
339 }
340
341 IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable
342                                         *the_PDU_Observable, int fd)
343 {
344     check_reconfigure();
345     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable,
346                                          m_socket_observable, this);
347     new_proxy->m_config = 0;
348     new_proxy->m_config_fname = 0;
349     new_proxy->timeout(m_client_idletime);
350     new_proxy->m_target_idletime = m_target_idletime;
351     new_proxy->set_default_target(m_default_target);
352     new_proxy->m_max_clients = m_max_clients;
353     new_proxy->m_log_mask = m_log_mask;
354     new_proxy->set_APDU_log(get_APDU_log());
355     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
356         new_proxy->set_APDU_yazlog(1);
357     else
358         new_proxy->set_APDU_yazlog(0);
359     sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
360     m_session_no++;
361     yaz_log (YLOG_LOG, "%sNew session %s", new_proxy->m_session_str,
362              the_PDU_Observable->getpeername());
363     new_proxy->set_proxy_negotiation(m_proxy_negotiation_charset,
364         m_proxy_negotiation_lang);
365     // create thread object the first time we get an incoming connection
366     if (!m_my_thread)
367         m_my_thread = new Msg_Thread(m_socket_observable);
368     new_proxy->m_my_thread = m_my_thread;
369     return new_proxy;
370 }
371
372 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
373 {
374     int oid[OID_SIZE];
375     Z_OtherInformationUnit *oi;
376     struct oident ent;
377     ent.proto = PROTO_Z3950;
378     ent.oclass = CLASS_USERINFO;
379     ent.value = (oid_value) VAL_COOKIE;
380     assert (oid_ent_to_oid (&ent, oid));
381
382     if (oid_ent_to_oid (&ent, oid) && 
383         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
384         oi->which == Z_OtherInfo_characterInfo)
385         return oi->information.characterInfo;
386     return 0;
387 }
388 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
389 {
390     int oid[OID_SIZE];
391     Z_OtherInformationUnit *oi;
392     struct oident ent;
393     ent.proto = PROTO_Z3950;
394     ent.oclass = CLASS_USERINFO;
395     ent.value = (oid_value) VAL_PROXY;
396     if (oid_ent_to_oid (&ent, oid) &&
397         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
398         oi->which == Z_OtherInfo_characterInfo)
399         return oi->information.characterInfo;
400     return 0;
401 }
402 const char *Yaz_Proxy::load_balance(const char **url)
403 {
404     int zurl_in_use[MAX_ZURL_PLEX];
405     int zurl_in_spare[MAX_ZURL_PLEX];
406     Yaz_ProxyClient *c;
407     int i;
408
409     for (i = 0; i<MAX_ZURL_PLEX; i++)
410     {
411         zurl_in_use[i] = 0;
412         zurl_in_spare[i] = 0;
413     }
414     for (c = m_parent->m_clientPool; c; c = c->m_next)
415     {
416         for (i = 0; url[i]; i++)
417             if (!strcmp(url[i], c->get_hostname()))
418             {
419                 zurl_in_use[i]++;
420                 if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
421                     zurl_in_spare[i]++;
422             }
423     }
424     int min_use = 100000;
425     int spare_for_min = 0;
426     int max_spare = 0;
427     const char *ret_min = 0;
428     const char *ret_spare = 0;
429     for (i = 0; url[i]; i++)
430     {
431         yaz_log(YLOG_DEBUG, "%szurl=%s use=%d spare=%d",
432                 m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
433         if (min_use > zurl_in_use[i])
434         {
435             ret_min = url[i];
436             min_use = zurl_in_use[i];
437             spare_for_min = zurl_in_spare[i];
438         }
439         if (max_spare < zurl_in_spare[i]) 
440         {
441             ret_spare = url[i];
442             max_spare = zurl_in_spare[i];
443         }
444     }
445     return ret_min;
446 }
447
448 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
449                                        const char *proxy_host)
450 {
451     assert (m_parent);
452     Yaz_Proxy *parent = m_parent;
453     Yaz_ProxyClient *c = m_client;
454     
455     if (!m_proxyTarget)
456     {
457         const char *url[MAX_ZURL_PLEX];
458         Yaz_ProxyConfig *cfg = check_reconfigure();
459         if (proxy_host)
460         {
461             if (parent && parent->m_debug_mode)
462             {
463                 // only to be enabled for debugging...
464                 if (!strcmp(proxy_host, "stop"))
465                     exit(0);
466             }
467             xfree(m_default_target);
468             m_default_target = xstrdup(proxy_host);
469         }
470         proxy_host = m_default_target;
471         int client_idletime = -1;
472         const char *cql2rpn_fname = 0;
473         const char *negotiation_charset = 0;
474         const char *negotiation_lang = 0;
475         const char *query_charset = 0;
476         url[0] = m_default_target;
477         url[1] = 0;
478         if (cfg)
479         {
480             int pre_init = 0;
481             cfg->get_target_info(proxy_host, url, &m_bw_max,
482                                  &m_pdu_max, &m_max_record_retrieve,
483                                  &m_target_idletime, &client_idletime,
484                                  &parent->m_max_clients,
485                                  &m_keepalive_limit_bw,
486                                  &m_keepalive_limit_pdu,
487                                  &pre_init,
488                                  &cql2rpn_fname,
489                                  &negotiation_charset,
490                                  &negotiation_lang,
491                                  &query_charset);
492         }
493         if (client_idletime != -1)
494         {
495             m_client_idletime = client_idletime;
496             timeout(m_client_idletime);
497         }
498         if (cql2rpn_fname)
499             m_cql2rpn.set_pqf_file(cql2rpn_fname);
500         if (negotiation_charset || negotiation_lang)
501         {
502             set_proxy_negotiation(negotiation_charset,
503                 negotiation_lang);
504         }
505         m_charset_converter->set_target_query_charset(query_charset);
506         if (!url[0])
507         {
508             yaz_log(YLOG_LOG, "%sNo default target", m_session_str);
509             return 0;
510         }
511         // we don't handle multiplexing for cookie session, so we just
512         // pick the first one in this case (anonymous users will be able
513         // to use any backend)
514         if (cookie && *cookie)
515             m_proxyTarget = (char*) xstrdup(url[0]);
516         else
517             m_proxyTarget = (char*) xstrdup(load_balance(url));
518     }
519     if (cookie && *cookie)
520     {   // search in sessions with a cookie
521         for (c = parent->m_clientPool; c; c = c->m_next)
522         {
523             assert (c->m_prev);
524             assert (*c->m_prev == c);
525             if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
526                 !strcmp(m_proxyTarget, c->get_hostname()))
527             {
528                 // Found it in cache
529                 // The following handles "cancel"
530                 // If connection is busy (waiting for PDU) and
531                 // we have an initRequest we can safely do re-open
532                 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
533                 {
534                     yaz_log (YLOG_LOG, "%s REOPEN target=%s", m_session_str,
535                              c->get_hostname());
536                     c->close();
537                     c->m_init_flag = 0;
538                     
539                     c->m_last_ok = 0;
540                     c->m_cache.clear();
541                     c->m_last_resultCount = 0;
542                     c->m_sr_transform = 0;
543                     c->m_waiting = 0;
544                     c->m_resultSetStartPoint = 0;
545                     c->m_target_idletime = m_target_idletime;
546                     if (c->client(m_proxyTarget))
547                     {
548                         delete c;
549                         return 0;
550                     }
551                     c->timeout(30); 
552                 }
553                 c->m_seqno = parent->m_seqno;
554                 if (c->m_server && c->m_server != this)
555                     c->m_server->m_client = 0;
556                 c->m_server = this;
557                 (parent->m_seqno)++;
558                 yaz_log (YLOG_DEBUG, "get_client 1 %p %p", this, c);
559                 return c;
560             }
561         }
562     }
563     else if (!c &&
564             apdu->which == Z_APDU_initRequest &&
565             apdu->u.initRequest->idAuthentication == 0 &&
566             !ODR_MASK_GET(apdu->u.initRequest->options, Z_Options_negotiationModel))
567     {
568         // anonymous sessions without cookie.
569         // if authentication is set it is NOT anonymous se we can't share them.
570         // If charset and lang negotiation is use it is NOT anonymous session too.
571         for (c = parent->m_clientPool; c; c = c->m_next)
572         {
573             assert(c->m_prev);
574             assert(*c->m_prev == c);
575             if (c->m_server == 0 && c->m_cookie == 0 && 
576                 c->m_waiting == 0 && 
577                 !strcmp(m_proxyTarget, c->get_hostname()))
578             {
579                 // found it in cache
580                 yaz_log (YLOG_LOG, "%sREUSE %d %s",
581                          m_session_str, parent->m_seqno, c->get_hostname());
582                 
583                 c->m_seqno = parent->m_seqno;
584                 assert(c->m_server == 0);
585                 c->m_server = this;
586
587                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
588                     c->set_APDU_yazlog(1);
589                 else
590                     c->set_APDU_yazlog(0);
591
592                 (parent->m_seqno)++;
593                 
594                 parent->pre_init();
595                 
596                 return c;
597             }
598         }
599     }
600     if (!m_client)
601     {
602         if (apdu->which != Z_APDU_initRequest)
603         {
604             yaz_log (YLOG_LOG, "%sno init request as first PDU", m_session_str);
605             return 0;
606         }
607         Z_InitRequest *initRequest = apdu->u.initRequest;
608
609         if (initRequest->idAuthentication)
610         {
611             // the client uses authentication. We set the keepalive PDU
612             // to 0 so we don't cache it in releaseClient
613             m_keepalive_limit_pdu = 0;
614         }
615         // go through list of clients - and find the lowest/oldest one.
616         Yaz_ProxyClient *c_min = 0;
617         int min_seq = -1;
618         int no_of_clients = 0;
619         if (parent->m_clientPool)
620             yaz_log (YLOG_DEBUG, "Existing sessions");
621         for (c = parent->m_clientPool; c; c = c->m_next)
622         {
623             yaz_log (YLOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
624                                c->m_waiting, c->get_hostname(),
625                                c->m_cookie ? c->m_cookie : "");
626             no_of_clients++;
627             if (min_seq < 0 || c->m_seqno < min_seq)
628             {
629                 min_seq = c->m_seqno;
630                 c_min = c;
631             }
632         }
633         if (no_of_clients >= parent->m_max_clients)
634         {
635             c = c_min;
636             if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
637             {
638                 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Destroy %d",
639                          m_session_str, parent->m_max_clients, c->m_seqno);
640                 if (c->m_server && c->m_server != this)
641                     delete c->m_server;
642                 c->m_server = 0;
643             }
644             else
645             {
646                 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
647                          m_session_str, parent->m_max_clients,
648                          c->m_seqno, parent->m_seqno, c->get_hostname());
649                 xfree (c->m_cookie);
650                 c->m_cookie = 0;
651                 if (cookie)
652                     c->m_cookie = xstrdup(cookie);
653                 c->m_seqno = parent->m_seqno;
654                 if (c->m_server && c->m_server != this)
655                 {
656                     c->m_server->m_client = 0;
657                     delete c->m_server;
658                 }
659                 (parent->m_seqno)++;
660                 c->m_target_idletime = m_target_idletime;
661                 c->timeout(m_target_idletime);
662                 
663                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
664                     c->set_APDU_yazlog(1);
665                 else
666                     c->set_APDU_yazlog(0);
667
668                 return c;
669             }
670         }
671         else
672         {
673             yaz_log (YLOG_LOG, "%sNEW %d %s",
674                      m_session_str, parent->m_seqno, m_proxyTarget);
675             c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
676             c->m_next = parent->m_clientPool;
677             if (c->m_next)
678                 c->m_next->m_prev = &c->m_next;
679             parent->m_clientPool = c;
680             c->m_prev = &parent->m_clientPool;
681         }
682
683         xfree (c->m_cookie);
684         c->m_cookie = 0;
685         if (cookie)
686             c->m_cookie = xstrdup(cookie);
687
688         c->m_seqno = parent->m_seqno;
689         c->m_init_flag = 0;
690         c->m_last_resultCount = 0;
691         c->m_last_ok = 0;
692         c->m_cache.clear();
693         c->m_sr_transform = 0;
694         c->m_waiting = 0;
695         c->m_resultSetStartPoint = 0;
696         (parent->m_seqno)++;
697         if (c->client(m_proxyTarget))
698         {
699             delete c;
700             return 0;
701         }
702         c->m_target_idletime = m_target_idletime;
703         c->timeout(30);
704
705         if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
706             c->set_APDU_yazlog(1);
707         else
708             c->set_APDU_yazlog(0);
709     }
710     yaz_log (YLOG_DEBUG, "get_client 3 %p %p", this, c);
711     return c;
712 }
713
714 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
715 {
716     int i;
717     for (i = 0; i<num; i++)
718     {
719         oident *ent;
720         Z_DefaultDiagFormat *r;
721         Z_DiagRec *p = pp[i];
722         if (p->which != Z_DiagRec_defaultFormat)
723         {
724             yaz_log(YLOG_LOG, "%sError no diagnostics", m_session_str);
725             return;
726         }
727         else
728             r = p->u.defaultFormat;
729         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
730             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
731             yaz_log(YLOG_LOG, "%sError unknown diagnostic set", m_session_str);
732         switch (r->which)
733         {
734         case Z_DefaultDiagFormat_v2Addinfo:
735             yaz_log(YLOG_LOG, "%sError %d %s:%s",
736                     m_session_str,
737                     *r->condition, diagbib1_str(*r->condition),
738                     r->u.v2Addinfo);
739             break;
740         case Z_DefaultDiagFormat_v3Addinfo:
741             yaz_log(YLOG_LOG, "%sError %d %s:%s",
742                     m_session_str,
743                     *r->condition, diagbib1_str(*r->condition),
744                     r->u.v3Addinfo);
745             break;
746         }
747     }
748 }
749
750 int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
751 {
752     if (!m_stylesheet_xsp || p->num_records <= 0)
753         return 0;  /* no XSLT to be done ... */
754
755     m_stylesheet_offset = 0;
756     m_stylesheet_nprl = p;
757     m_stylesheet_apdu = apdu;
758     timeout(0);
759     return 1;
760 }
761
762 void Yaz_Proxy::convert_xsl_delay()
763 {
764 #if HAVE_XSLT
765     Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
766     if (npr->which == Z_NamePlusRecord_databaseRecord)
767     {
768         Z_External *r = npr->u.databaseRecord;
769         if (r->which == Z_External_octet)
770         {
771 #if 0
772             fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
773 #endif
774             xmlDocPtr res, doc = xmlParseMemory(
775                 (char*) r->u.octet_aligned->buf,
776                 r->u.octet_aligned->len);
777
778             
779             yaz_log(YLOG_LOG, "%sXSLT convert %d",
780                     m_session_str, m_stylesheet_offset);
781             res = xsltApplyStylesheet((xsltStylesheetPtr) m_stylesheet_xsp,
782                                       doc, 0);
783
784             if (res)
785             {
786                 xmlChar *out_buf;
787                 int out_len;
788                 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
789                 
790                 m_stylesheet_nprl->records[m_stylesheet_offset]->
791                     u.databaseRecord = 
792                     z_ext_record(odr_encode(), VAL_TEXT_XML,
793                                  (char*) out_buf, out_len);
794                 xmlFree(out_buf);
795                 xmlFreeDoc(res);
796             }
797
798             xmlFreeDoc(doc);
799         }
800     }
801 #endif
802     m_stylesheet_offset++;
803     if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
804     {
805         m_stylesheet_nprl = 0;
806 #if HAVE_XSLT
807         if (m_stylesheet_xsp)
808             xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
809 #endif
810         m_stylesheet_xsp = 0;
811         timeout(m_client_idletime);
812         send_PDU_convert(m_stylesheet_apdu);
813     }
814     else
815         timeout(0);
816 }
817
818 void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
819 {
820     if (m_frontend_type != VAL_NONE)
821     {
822         int i;
823         for (i = 0; i < p->num_records; i++)
824         {
825             Z_NamePlusRecord *npr = p->records[i];
826             if (npr->which == Z_NamePlusRecord_databaseRecord)
827             {
828                 Z_External *r = npr->u.databaseRecord;
829                 if (r->which == Z_External_octet)
830                 {
831 #if HAVE_USEMARCON
832                     if (m_usemarcon_ini_stage1 && *m_usemarcon_ini_stage1)
833                     {
834                         if (!m_usemarcon->m_stage1)
835                         {
836                             m_usemarcon->m_stage1 = new CDetails();
837                         }
838                         m_usemarcon->m_stage1->SetIniFileName(m_usemarcon_ini_stage1);
839                         m_usemarcon->m_stage1->SetMarcRecord((char*) r->u.octet_aligned->buf, r->u.octet_aligned->len);
840                         int res = m_usemarcon->m_stage1->Start();
841                         if (res == 0)
842                         {
843                             char *converted;
844                             int convlen;
845                             m_usemarcon->m_stage1->GetMarcRecord(converted, convlen);
846                             if (m_usemarcon_ini_stage2 && *m_usemarcon_ini_stage2)
847                             {
848                                 if (!m_usemarcon->m_stage2)
849                                 {
850                                     m_usemarcon->m_stage2 = new CDetails();
851                                 }
852                                 m_usemarcon->m_stage2->SetIniFileName(m_usemarcon_ini_stage2);
853                                 m_usemarcon->m_stage2->SetMarcRecord(converted, convlen);
854                                 res = m_usemarcon->m_stage2->Start();
855                                 if (res == 0)
856                                 {
857                                     free(converted);
858                                     m_usemarcon->m_stage2->GetMarcRecord(converted, convlen);
859                                 }
860                                 else
861                                 {
862                                     yaz_log(YLOG_LOG, "%sUSEMARCON stage 2 error %d", m_session_str, res);
863                                 }
864                             }
865                             npr->u.databaseRecord =
866                                 z_ext_record(odr_encode(),
867                                              m_frontend_type,
868                                              converted,
869                                              strlen(converted));
870                             free(converted);
871                         }
872                         else
873                         {
874                             yaz_log(YLOG_LOG, "%sUSEMARCON stage 1 error %d", m_session_str, res);
875                         }
876                         continue;
877                     }
878 #endif
879 /* HAVE_USEMARCON */
880                     npr->u.databaseRecord =
881                         z_ext_record(odr_encode(),
882                                      m_frontend_type,
883                                      (char*) r->u.octet_aligned->buf,
884                                      r->u.octet_aligned->len);
885                 }
886             }
887         }
888     }
889 }
890
891 void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
892                                         const char *backend_charset)
893 {
894     yaz_log(YLOG_LOG, "%sconvert_to_marc", m_session_str);
895     int sel =   m_charset_converter->get_client_charset_selected();
896     const char *client_record_charset =
897         m_charset_converter->get_client_query_charset();
898     if (sel && backend_charset && client_record_charset &&
899         strcmp(backend_charset, client_record_charset))
900     {
901         int i;
902         yaz_iconv_t cd = yaz_iconv_open(client_record_charset,
903                                         backend_charset);
904         yaz_marc_t mt = yaz_marc_create();
905         yaz_marc_xml(mt, YAZ_MARC_ISO2709);
906         yaz_marc_iconv(mt, cd);
907         for (i = 0; i < p->num_records; i++)
908         {
909             Z_NamePlusRecord *npr = p->records[i];
910             if (npr->which == Z_NamePlusRecord_databaseRecord)
911             {
912                 Z_External *r = npr->u.databaseRecord;
913                 oident *ent = oid_getentbyoid(r->direct_reference);
914                 if (!ent || ent->value == VAL_NONE)
915                     continue;
916
917                 if (ent->value == VAL_SUTRS)
918                 {
919                     WRBUF w = wrbuf_alloc();
920
921                     wrbuf_iconv_write(w, cd,  (char*) r->u.octet_aligned->buf,
922                                       r->u.octet_aligned->len);
923                     npr->u.databaseRecord =
924                         z_ext_record(odr_encode(), ent->value, wrbuf_buf(w),
925                                      wrbuf_len(w));
926                     wrbuf_free(w, 1);
927                 }
928                 else if (ent->value == VAL_TEXT_XML)
929                 {
930                     ;
931                 }
932                 else if (r->which == Z_External_octet)
933                 {
934                     int rlen;
935                     char *result;
936                     if (yaz_marc_decode_buf(mt,
937                                             (char*) r->u.octet_aligned->buf,
938                                             r->u.octet_aligned->len,
939                                             &result, &rlen))
940                     {
941                         npr->u.databaseRecord =
942                             z_ext_record(odr_encode(), ent->value, result, rlen);
943                         yaz_log(YLOG_LOG, "%sRecoding MARC record",
944                                 m_session_str);
945                     }
946                 }
947             }
948         }
949         if (cd)
950             yaz_iconv_close(cd);
951         yaz_marc_destroy(mt);
952     }
953     else
954     {
955         yaz_log(YLOG_LOG, "%sSkipping marc convert", m_session_str);
956     }
957 }
958
959 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
960                                    const char *backend_charset)
961 {
962     int i;
963     if (!backend_charset)
964         backend_charset = "MARC-8";
965     yaz_iconv_t cd = yaz_iconv_open("UTF-8", backend_charset);
966     yaz_marc_t mt = yaz_marc_create();
967     yaz_marc_xml(mt, YAZ_MARC_MARCXML);
968     yaz_marc_iconv(mt, cd);
969     for (i = 0; i < p->num_records; i++)
970     {
971         Z_NamePlusRecord *npr = p->records[i];
972         if (npr->which == Z_NamePlusRecord_databaseRecord)
973         {
974             Z_External *r = npr->u.databaseRecord;
975             if (r->which == Z_External_OPAC)
976             {
977                 WRBUF w = wrbuf_alloc();
978
979                 yaz_display_OPAC(w, r->u.opac, 0);
980                 npr->u.databaseRecord = z_ext_record(
981                     odr_encode(), VAL_TEXT_XML,
982                     wrbuf_buf(w), wrbuf_len(w)
983                     );
984                 wrbuf_free(w, 1);
985             }
986             else if (r->which == Z_External_octet)
987             {
988                 int rlen;
989                 char *result;
990                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
991                                         r->u.octet_aligned->len,
992                                         &result, &rlen))
993                 {
994                     npr->u.databaseRecord =
995                         z_ext_record(odr_encode(), VAL_TEXT_XML, result, rlen);
996                 }
997             }
998         }
999     }
1000     if (cd)
1001         yaz_iconv_close(cd);
1002     yaz_marc_destroy(mt);
1003 }
1004
1005 void Yaz_Proxy::logtime()
1006 {
1007 #if HAVE_GETTIMEOFDAY
1008     struct timeval *tv = (struct timeval*) m_time_tv;
1009     if (tv->tv_sec)
1010     {
1011         struct timeval tv1;
1012         gettimeofday(&tv1, 0);
1013         long diff = (tv1.tv_sec - tv->tv_sec)*1000000 +
1014             (tv1.tv_usec - tv->tv_usec);
1015         if (diff >= 0)
1016             yaz_log(YLOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
1017                     diff/1000000, (diff/1000)%1000);
1018     }
1019     tv->tv_sec = 0;
1020     tv->tv_usec = 0;
1021 #endif
1022 }
1023
1024 int Yaz_Proxy::send_http_response(int code)
1025 {
1026     ODR o = odr_encode();
1027     Z_GDU *gdu = z_get_HTTP_Response(o, code);
1028     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
1029     if (m_http_version)
1030         hres->version = odr_strdup(o, m_http_version);
1031     if (m_http_keepalive)
1032         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1033     else
1034         timeout(0);
1035     
1036     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1037     {
1038         yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1039                  gdu_name(gdu));
1040     }
1041     int len;
1042     int r = send_GDU(gdu, &len);
1043     m_bytes_sent += len;
1044     m_bw_stat.add_bytes(len);
1045     logtime();
1046     return r;
1047 }
1048
1049 int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
1050 {
1051     ODR o = odr_encode();
1052     const char *ctype = "text/xml";
1053     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
1054     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
1055     if (m_http_version)
1056         hres->version = odr_strdup(o, m_http_version);
1057     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1058     if (m_http_keepalive)
1059         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1060     else
1061         timeout(0);
1062
1063     static Z_SOAP_Handler soap_handlers[2] = {
1064 #if HAVE_XSLT
1065         {"http://www.loc.gov/zing/srw/", 0,
1066          (Z_SOAP_fun) yaz_srw_codec},
1067 #endif
1068         {0, 0, 0}
1069     };
1070     
1071     Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
1072     soap_package->which = Z_SOAP_generic;
1073     soap_package->u.generic = 
1074         (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
1075     soap_package->u.generic->no = 0;
1076     soap_package->u.generic->ns = soap_handlers[0].ns;
1077     soap_package->u.generic->p = (void *) srw_pdu;
1078     soap_package->ns = m_soap_ns;
1079     z_soap_codec_enc_xsl(o, &soap_package,
1080                          &hres->content_buf, &hres->content_len,
1081                          soap_handlers, 0, m_s2z_stylesheet);
1082     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1083     {
1084         yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1085                  gdu_name(gdu));
1086     }
1087     int len;
1088     int r = send_GDU(gdu, &len);
1089     m_bytes_sent += len;
1090     m_bw_stat.add_bytes(len);
1091     logtime();
1092     return r;
1093 }
1094
1095 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
1096 {
1097     ODR o = odr_encode();
1098     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
1099     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1100
1101     srw_res->num_diagnostics = 1;
1102     srw_res->diagnostics = (Z_SRW_diagnostic *)
1103         odr_malloc(o, sizeof(*srw_res->diagnostics));
1104     yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
1105     return send_srw_response(srw_pdu);
1106 }
1107
1108 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
1109                              Z_DefaultDiagFormat *ddf)
1110 {
1111     int bib1_code = *ddf->condition;
1112     if (bib1_code == 109)
1113         return 404;
1114     srw_res->num_diagnostics = 1;
1115     srw_res->diagnostics = (Z_SRW_diagnostic *)
1116         odr_malloc(o, sizeof(*srw_res->diagnostics));
1117     yaz_mk_std_diagnostic(o, srw_res->diagnostics,
1118                           yaz_diag_bib1_to_srw(*ddf->condition), 
1119                           ddf->u.v2Addinfo);
1120     return 0;
1121 }
1122
1123 int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
1124 {
1125     ODR o = odr_encode();
1126     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
1127     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1128
1129     srw_res->numberOfRecords = odr_intdup (o, hits);
1130     if (records && records->which == Z_Records_DBOSD)
1131     {
1132         srw_res->num_records =
1133             records->u.databaseOrSurDiagnostics->num_records;
1134         int i;
1135         srw_res->records = (Z_SRW_record *)
1136             odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
1137         for (i = 0; i < srw_res->num_records; i++)
1138         {
1139             Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
1140             if (npr->which != Z_NamePlusRecord_databaseRecord)
1141             {
1142                 srw_res->records[i].recordSchema = "diagnostic";
1143                 srw_res->records[i].recordPacking = m_s2z_packing;
1144                 srw_res->records[i].recordData_buf = "67";
1145                 srw_res->records[i].recordData_len = 2;
1146                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1147                 continue;
1148             }
1149             Z_External *r = npr->u.databaseRecord;
1150             oident *ent = oid_getentbyoid(r->direct_reference);
1151             if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
1152             {
1153                 srw_res->records[i].recordSchema = m_schema;
1154                 srw_res->records[i].recordPacking = m_s2z_packing;
1155                 srw_res->records[i].recordData_buf = (char*) 
1156                     r->u.octet_aligned->buf;
1157                 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
1158                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1159             }
1160             else
1161             {
1162                 srw_res->records[i].recordSchema = "diagnostic";
1163                 srw_res->records[i].recordPacking = m_s2z_packing;
1164                 srw_res->records[i].recordData_buf = "67";
1165                 srw_res->records[i].recordData_len = 2;
1166                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1167             }
1168         }
1169     }
1170     if (records && records->which == Z_Records_NSD)
1171     {
1172         int http_code;
1173         http_code = z_to_srw_diag(odr_encode(), srw_res,
1174                                    records->u.nonSurrogateDiagnostic);
1175         if (http_code)
1176             return send_http_response(http_code);
1177     }
1178     return send_srw_response(srw_pdu);
1179     
1180 }
1181
1182 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
1183                                         int num_diagnostics)
1184 {
1185     Yaz_ProxyConfig *cfg = check_reconfigure();
1186     if (cfg)
1187     {
1188         int len;
1189         char *b = cfg->get_explain_doc(odr_encode(), 0 /* target */,
1190                                        m_s2z_database, &len);
1191         if (b)
1192         {
1193             Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
1194             Z_SRW_explainResponse *er = res->u.explain_response;
1195
1196             er->record.recordData_buf = b;
1197             er->record.recordData_len = len;
1198             er->record.recordPacking = m_s2z_packing;
1199             er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
1200
1201             er->diagnostics = diagnostics;
1202             er->num_diagnostics = num_diagnostics;
1203             return send_srw_response(res);
1204         }
1205     }
1206     return send_http_response(404);
1207 }
1208
1209 int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
1210 {
1211     if (m_http_version)
1212     {
1213         if (apdu->which == Z_APDU_initResponse)
1214         {
1215             Z_InitResponse *res = apdu->u.initResponse;
1216             if (*res->result == 0)
1217             {
1218                 send_to_srw_client_error(3, 0);
1219             }
1220             else if (!m_s2z_search_apdu)
1221             {
1222                 send_srw_explain_response(0, 0);
1223             }
1224             else
1225             {
1226                 handle_incoming_Z_PDU(m_s2z_search_apdu);
1227             }
1228         }
1229         else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
1230         {
1231             m_s2z_search_apdu = 0;
1232             Z_SearchResponse *res = apdu->u.searchResponse;
1233             m_s2z_hit_count = *res->resultCount;
1234             if (res->records && res->records->which == Z_Records_NSD)
1235             {
1236                 send_to_srw_client_ok(0, res->records, 1);
1237             }
1238             else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
1239             {
1240                 // adjust 
1241                 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
1242                 
1243                 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
1244                 {
1245                     if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
1246                         > m_s2z_hit_count)
1247                         *pr->numberOfRecordsRequested =
1248                             1 + m_s2z_hit_count - *pr->resultSetStartPoint;
1249                 }
1250                 handle_incoming_Z_PDU(m_s2z_present_apdu);
1251             }
1252             else
1253             {
1254                 m_s2z_present_apdu = 0;
1255                 send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
1256             }
1257         }
1258         else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
1259         {
1260             int start = 
1261                 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
1262
1263             m_s2z_present_apdu = 0;
1264             Z_PresentResponse *res = apdu->u.presentResponse;
1265             send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
1266         }
1267     }
1268     else
1269     {
1270         int len = 0;
1271         if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1272             yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1273                      apdu_name(apdu));
1274         int r = send_Z_PDU(apdu, &len);
1275         m_bytes_sent += len;
1276         m_bw_stat.add_bytes(len);
1277         logtime();
1278         return r;
1279     }
1280     return 0;
1281 }
1282
1283 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
1284 {
1285     int kill_session = 0;
1286     Z_ReferenceId **new_id = get_referenceIdP(apdu);
1287
1288     if (new_id)
1289         *new_id = m_referenceId;
1290     
1291     if (apdu->which == Z_APDU_searchResponse)
1292     {
1293         Z_SearchResponse *sr = apdu->u.searchResponse;
1294         Z_Records *p = sr->records;
1295         if (p && p->which == Z_Records_NSD)
1296         {
1297             Z_DiagRec dr, *dr_p = &dr;
1298             dr.which = Z_DiagRec_defaultFormat;
1299             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1300
1301             *sr->searchStatus = 0;
1302             display_diagrecs(&dr_p, 1);
1303         }
1304         else
1305         {
1306             if (p && p->which == Z_Records_DBOSD)
1307             {
1308                 if (m_backend_type
1309 #if HAVE_USEMARCON
1310                     || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
1311 #endif
1312                     )
1313                     convert_to_frontend_type(p->u.databaseOrSurDiagnostics);
1314                 if (m_marcxml_mode == marcxml)
1315                     convert_to_marcxml(p->u.databaseOrSurDiagnostics,
1316                                        m_backend_charset);
1317                 else
1318                     convert_records_charset(p->u.databaseOrSurDiagnostics,
1319                                             m_backend_charset);
1320                 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1321                     return 0;
1322                     
1323             }
1324             if (sr->resultCount)
1325             {
1326                 yaz_log(YLOG_LOG, "%s%d hits", m_session_str,
1327                         *sr->resultCount);
1328                 if (*sr->resultCount < 0)
1329                 {
1330                     m_invalid_session = 1;
1331                     kill_session = 1;
1332
1333                     *sr->searchStatus = 0;
1334                     sr->records =
1335                         create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
1336                     *sr->resultCount = 0;
1337                 }
1338             }
1339         }
1340     }
1341     else if (apdu->which == Z_APDU_presentResponse)
1342     {
1343         Z_PresentResponse *sr = apdu->u.presentResponse;
1344         Z_Records *p = sr->records;
1345         if (p && p->which == Z_Records_NSD)
1346         {
1347             Z_DiagRec dr, *dr_p = &dr;
1348             dr.which = Z_DiagRec_defaultFormat;
1349             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1350             if (*sr->presentStatus == Z_PresentStatus_success)
1351                 *sr->presentStatus = Z_PresentStatus_failure;
1352             display_diagrecs(&dr_p, 1);
1353         }
1354         if (p && p->which == Z_Records_DBOSD)
1355         {
1356             if (m_backend_type 
1357 #if HAVE_USEMARCON
1358                 || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
1359 #endif
1360                 )
1361                 convert_to_frontend_type(p->u.databaseOrSurDiagnostics);
1362             if (m_marcxml_mode == marcxml)
1363                 convert_to_marcxml(p->u.databaseOrSurDiagnostics,
1364                                    m_backend_charset);
1365             else
1366                 convert_records_charset(p->u.databaseOrSurDiagnostics,
1367                                         m_backend_charset);
1368             if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1369                 return 0;
1370         }
1371     }
1372     else if (apdu->which == Z_APDU_initResponse)
1373     {
1374         //Get and check negotiation record
1375         //from init response.
1376         handle_charset_lang_negotiation(apdu);
1377         
1378         if (m_initRequest_options)
1379         {
1380             Z_Options *nopt = 
1381                 (Odr_bitmask *)odr_malloc(odr_encode(),
1382                                           sizeof(Odr_bitmask));
1383             ODR_MASK_ZERO(nopt);
1384
1385             int i;
1386             for (i = 0; i<24; i++)
1387                 if (ODR_MASK_GET(m_initRequest_options, i) &&
1388                     ODR_MASK_GET(apdu->u.initResponse->options, i))
1389                     ODR_MASK_SET(nopt, i);
1390             apdu->u.initResponse->options = nopt;           
1391         }
1392         if (m_initRequest_version)
1393         {
1394             Z_ProtocolVersion *nopt = 
1395                 (Odr_bitmask *)odr_malloc(odr_encode(),
1396                                           sizeof(Odr_bitmask));
1397             ODR_MASK_ZERO(nopt);
1398
1399             int i;
1400             for (i = 0; i<8; i++)
1401                 if (ODR_MASK_GET(m_initRequest_version, i) &&
1402                     ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
1403                     ODR_MASK_SET(nopt, i);
1404             apdu->u.initResponse->protocolVersion = nopt;           
1405         }
1406         apdu->u.initResponse->preferredMessageSize =
1407             odr_intdup(odr_encode(),
1408                        m_client->m_initResponse_preferredMessageSize >
1409                        m_initRequest_preferredMessageSize ?
1410                        m_initRequest_preferredMessageSize :
1411                        m_client->m_initResponse_preferredMessageSize);
1412         apdu->u.initResponse->maximumRecordSize =
1413             odr_intdup(odr_encode(),
1414                        m_client->m_initResponse_maximumRecordSize >
1415                        m_initRequest_maximumRecordSize ?
1416                        m_initRequest_maximumRecordSize :
1417                        m_client->m_initResponse_maximumRecordSize);
1418     }
1419     
1420     int r = send_PDU_convert(apdu);
1421     if (r)
1422         return r;
1423     if (kill_session)
1424     {
1425         delete m_client;
1426         m_client = 0;
1427         m_parent->pre_init();
1428     }
1429     return r;
1430 }
1431
1432 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
1433 {
1434     int len = 0;
1435     const char *apdu_name_tmp = apdu_name(apdu);
1436     int r = send_Z_PDU(apdu, &len);
1437     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
1438         yaz_log (YLOG_LOG, "%sSending %s to %s %d bytes",
1439                  get_session_str(),
1440                  apdu_name_tmp, get_hostname(), len);
1441     m_bytes_sent += len;
1442     return r;
1443 }
1444
1445 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
1446 {
1447     if (apdu->which == Z_APDU_presentRequest)
1448     {
1449         Z_PresentRequest *pr = apdu->u.presentRequest;
1450         int toget = *pr->numberOfRecordsRequested;
1451         int start = *pr->resultSetStartPoint;
1452
1453         yaz_log(YLOG_LOG, "%sPresent %s %d+%d", m_session_str,
1454                 pr->resultSetId, start, toget);
1455
1456         if (*m_parent->m_optimize == '0')
1457             return apdu;
1458
1459         if (!m_client->m_last_resultSetId)
1460         {
1461             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1462             new_apdu->u.presentResponse->records =
1463                 create_nonSurrogateDiagnostics(odr_encode(), 30,
1464                                                pr->resultSetId);
1465             send_to_client(new_apdu);
1466             return 0;
1467         }
1468         if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
1469         {
1470             if (start+toget-1 > m_client->m_last_resultCount)
1471             {
1472                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1473                 new_apdu->u.presentResponse->records =
1474                     create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
1475                 send_to_client(new_apdu);
1476                 return 0;
1477             }
1478             Z_NamePlusRecordList *npr;
1479 #if 0
1480             yaz_log(YLOG_LOG, "%sCache lookup %d+%d syntax=%s",
1481                     m_session_str, start, toget, yaz_z3950oid_to_str(
1482                         pr->preferredRecordSyntax, &oclass));
1483 #endif
1484             if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
1485                                           pr->preferredRecordSyntax,
1486                                           pr->recordComposition))
1487             {
1488                 yaz_log (YLOG_LOG, "%sReturned cached records for present request", 
1489                          m_session_str);
1490                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1491                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
1492                 
1493                 new_apdu->u.presentResponse->numberOfRecordsReturned
1494                     = odr_intdup(odr_encode(), toget);
1495                                                                  
1496                 new_apdu->u.presentResponse->records = (Z_Records*)
1497                     odr_malloc(odr_encode(), sizeof(Z_Records));
1498                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
1499                 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
1500                 new_apdu->u.presentResponse->nextResultSetPosition =
1501                     odr_intdup(odr_encode(), start+toget);
1502
1503                 send_to_client(new_apdu);
1504                 return 0;
1505             }
1506         }
1507     }
1508
1509     if (apdu->which != Z_APDU_searchRequest)
1510         return apdu;
1511     Z_SearchRequest *sr = apdu->u.searchRequest;
1512     Yaz_Z_Query *this_query = new Yaz_Z_Query;
1513     Yaz_Z_Databases this_databases;
1514
1515     this_databases.set(sr->num_databaseNames, (const char **)
1516                        sr->databaseNames);
1517     
1518     this_query->set_Z_Query(sr->query);
1519
1520     char query_str[120];
1521     this_query->print(query_str, sizeof(query_str)-1);
1522     yaz_log(YLOG_LOG, "%sSearch %s", m_session_str, query_str);
1523
1524     if (*m_parent->m_optimize != '0' &&
1525         m_client->m_last_ok && m_client->m_last_query &&
1526         m_client->m_last_query->match(this_query) &&
1527         !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
1528         m_client->m_last_databases.match(this_databases))
1529     {
1530         delete this_query;
1531         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
1532             m_client->m_last_resultCount < *sr->largeSetLowerBound)
1533         {
1534             Z_NamePlusRecordList *npr;
1535             int toget = *sr->mediumSetPresentNumber;
1536             Z_RecordComposition *comp = 0;
1537
1538             if (toget > m_client->m_last_resultCount)
1539                 toget = m_client->m_last_resultCount;
1540             
1541             if (sr->mediumSetElementSetNames)
1542             {
1543                 comp = (Z_RecordComposition *)
1544                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1545                 comp->which = Z_RecordComp_simple;
1546                 comp->u.simple = sr->mediumSetElementSetNames;
1547             }
1548  
1549             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1550                                           sr->preferredRecordSyntax, comp))
1551             {
1552                 yaz_log (YLOG_LOG, "%sReturned cached records for medium set",
1553                          m_session_str);
1554                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1555                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1556                 new_apdu->u.searchResponse->resultCount =
1557                     &m_client->m_last_resultCount;
1558                 
1559                 new_apdu->u.searchResponse->numberOfRecordsReturned
1560                     = odr_intdup(odr_encode(), toget);
1561                                                         
1562                 new_apdu->u.searchResponse->presentStatus =
1563                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1564                 new_apdu->u.searchResponse->records = (Z_Records*)
1565                     odr_malloc(odr_encode(), sizeof(Z_Records));
1566                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1567                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1568                 new_apdu->u.searchResponse->nextResultSetPosition =
1569                     odr_intdup(odr_encode(), toget+1);
1570                 send_to_client(new_apdu);
1571                 return 0;
1572             }
1573             else
1574             {
1575                 // medium Set
1576                 // send present request (medium size)
1577                 yaz_log (YLOG_LOG, "%sOptimizing search for medium set",
1578                          m_session_str);
1579
1580                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1581                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1582                 pr->referenceId = sr->referenceId;
1583                 pr->resultSetId = sr->resultSetName;
1584                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1585                 *pr->numberOfRecordsRequested = toget;
1586                 pr->recordComposition = comp;
1587                 m_client->m_sr_transform = 1;
1588                 return new_apdu;
1589             }
1590         }
1591         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
1592             m_client->m_last_resultCount <= 0)
1593         {
1594             // large set. Return pseudo-search response immediately
1595             yaz_log (YLOG_LOG, "%sOptimizing search for large set",
1596                      m_session_str);
1597             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1598             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1599             new_apdu->u.searchResponse->resultCount =
1600                 &m_client->m_last_resultCount;
1601             send_to_client(new_apdu);
1602             return 0;
1603         }
1604         else
1605         {
1606             Z_NamePlusRecordList *npr;
1607             int toget = m_client->m_last_resultCount;
1608             Z_RecordComposition *comp = 0;
1609             // small set
1610             // send a present request (small set)
1611             
1612             if (sr->smallSetElementSetNames)
1613             {
1614                 comp = (Z_RecordComposition *)
1615                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1616                 comp->which = Z_RecordComp_simple;
1617                 comp->u.simple = sr->smallSetElementSetNames;
1618             }
1619
1620             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1621                                           sr->preferredRecordSyntax, comp))
1622             {
1623                 yaz_log (YLOG_LOG, "%sReturned cached records for small set",
1624                          m_session_str);
1625                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1626                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1627                 new_apdu->u.searchResponse->resultCount =
1628                     &m_client->m_last_resultCount;
1629                 
1630                 new_apdu->u.searchResponse->numberOfRecordsReturned
1631                     = odr_intdup(odr_encode(), toget);
1632                                                                  
1633                 new_apdu->u.searchResponse->presentStatus =
1634                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1635                 new_apdu->u.searchResponse->records = (Z_Records*)
1636                     odr_malloc(odr_encode(), sizeof(Z_Records));
1637                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1638                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1639                 new_apdu->u.searchResponse->nextResultSetPosition =
1640                     odr_intdup(odr_encode(), toget+1);
1641                 send_to_client(new_apdu);
1642                 return 0;
1643             }
1644             else
1645             {
1646                 yaz_log (YLOG_LOG, "%sOptimizing search for small set",
1647                          m_session_str);
1648                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1649                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1650                 pr->referenceId = sr->referenceId;
1651                 pr->resultSetId = sr->resultSetName;
1652                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1653                 *pr->numberOfRecordsRequested = toget;
1654                 pr->recordComposition = comp;
1655                 m_client->m_sr_transform = 1;
1656                 return new_apdu;
1657             }
1658         }
1659     }
1660     else  // query doesn't match
1661     {
1662         delete m_client->m_last_query;
1663         m_client->m_last_query = this_query;
1664         m_client->m_last_ok = 0;
1665         m_client->m_cache.clear();
1666         m_client->m_resultSetStartPoint = 0;
1667
1668         xfree (m_client->m_last_resultSetId);
1669         m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
1670
1671         m_client->m_last_databases.set(sr->num_databaseNames,
1672                                        (const char **) sr->databaseNames);
1673     }
1674     return apdu;
1675 }
1676
1677
1678 void Yaz_Proxy::inc_request_no()
1679 {
1680     char *cp = strchr(m_session_str, ' ');
1681     m_request_no++;
1682     if (cp)
1683         sprintf(cp+1, "%d ", m_request_no);
1684 }
1685
1686 void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
1687 {
1688     inc_request_no();
1689
1690     m_bytes_recv += len;
1691     
1692     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1693         yaz_log (YLOG_LOG, "%sReceiving %s from client %d bytes",
1694                  m_session_str, gdu_name(apdu), len);
1695
1696     if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
1697         shutdown();
1698
1699     m_bw_stat.add_bytes(len);
1700     m_pdu_stat.add_bytes(1);
1701
1702 #if HAVE_GETTIMEOFDAY
1703     gettimeofday((struct timeval *) m_time_tv, 0);
1704 #endif
1705
1706     int bw_total = m_bw_stat.get_total();
1707     int pdu_total = m_pdu_stat.get_total();
1708
1709     int reduce = 0;
1710     if (m_bw_max)
1711     {
1712         if (bw_total > m_bw_max)
1713         {
1714             reduce = (bw_total/m_bw_max);
1715         }
1716     }
1717     if (m_pdu_max)
1718     {
1719         if (pdu_total > m_pdu_max)
1720         {
1721             int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
1722             reduce = (reduce > nreduce) ? reduce : nreduce;
1723         }
1724     }
1725     m_http_version = 0;
1726     if (reduce)  
1727     {
1728         yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1729                 m_session_str, reduce, bw_total, pdu_total,
1730                 m_bw_max, m_pdu_max);
1731         
1732         m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
1733         timeout(reduce);       // call us reduce seconds later
1734     }
1735     else if (apdu->which == Z_GDU_Z3950)
1736         handle_incoming_Z_PDU(apdu->u.z3950);
1737     else if (apdu->which == Z_GDU_HTTP_Request)
1738         handle_incoming_HTTP(apdu->u.HTTP_Request);
1739 }
1740
1741 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
1742 {
1743     if (m_max_record_retrieve)
1744     {
1745         if (apdu->which == Z_APDU_presentRequest)
1746         {
1747             Z_PresentRequest *pr = apdu->u.presentRequest;
1748             if (pr->numberOfRecordsRequested && 
1749                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
1750                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
1751         }
1752     }
1753 }
1754 void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
1755 {
1756     if (apdu->which == Z_APDU_initRequest)
1757     {
1758         yaz_log(YLOG_LOG, "%shandle_charset_lang_negotiation", 
1759                 m_session_str);
1760         if (m_initRequest_options &&
1761             !ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel) &&
1762             (m_proxy_negotiation_charset || m_proxy_negotiation_lang))
1763         {
1764             // There is no negotiation proposal from
1765             // client's side. OK. The proxy negotiation
1766             // in use, only.
1767             Z_InitRequest *initRequest = apdu->u.initRequest;
1768             Z_OtherInformation **otherInfo;  
1769             Z_OtherInformationUnit *oi;
1770             get_otherInfoAPDU(apdu, &otherInfo);
1771             oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
1772             if (oi)
1773             {
1774                 ODR_MASK_SET(initRequest->options,
1775                     Z_Options_negotiationModel);
1776                 oi->which = Z_OtherInfo_externallyDefinedInfo;    
1777                 oi->information.externallyDefinedInfo =
1778                 yaz_set_proposal_charneg(odr_encode(),
1779                     (const char**)&m_proxy_negotiation_charset,
1780                     m_proxy_negotiation_charset ? 1:0,
1781                     (const char**)&m_proxy_negotiation_lang,
1782                     m_proxy_negotiation_lang ? 1:0,
1783                     1);
1784             }
1785         }
1786         else if (m_initRequest_options &&
1787                  ODR_MASK_GET(m_initRequest_options,
1788                               Z_Options_negotiationModel) &&
1789                  m_charset_converter->get_target_query_charset())
1790         {
1791             yaz_log(YLOG_LOG, "%sManaged charset negotiation: charset=%s",
1792                     m_session_str,
1793                     m_charset_converter->get_target_query_charset());
1794             Z_InitRequest *initRequest = apdu->u.initRequest;
1795             Z_CharSetandLanguageNegotiation *negotiation =
1796                 yaz_get_charneg_record (initRequest->otherInfo);        
1797             if (negotiation &&
1798                 negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
1799             {
1800                 NMEM nmem = nmem_create();
1801                 char **charsets = 0;
1802                 int num_charsets = 0;
1803                 char **langs = 0;
1804                 int num_langs = 0;
1805                 int selected = 0;
1806                 yaz_get_proposal_charneg (nmem, negotiation,
1807                                           &charsets, &num_charsets,
1808                                           &langs, &num_langs, &selected);
1809                 int i;
1810                 for (i = 0; i<num_charsets; i++)
1811                     yaz_log(YLOG_LOG, "%scharset %s", m_session_str,
1812                             charsets[i]);
1813                 for (i = 0; i<num_langs; i++)
1814                     yaz_log(YLOG_LOG, "%slang %s", m_session_str,
1815                             langs[i]);
1816
1817                 const char *t_charset =
1818                     m_charset_converter->get_target_query_charset();
1819                 // sweep through charsets and pick the first supported
1820                 // conversion
1821                 for (i = 0; i<num_charsets; i++)
1822                 {
1823                     const char *c_charset = charsets[i];
1824                     if (!odr_set_charset(odr_decode(), t_charset, c_charset))
1825                         break;
1826                 }
1827                 if (i != num_charsets)
1828                 {
1829                     // got one .. set up ODR for reverse direction
1830                     const char *c_charset = charsets[i];
1831                     odr_set_charset(odr_encode(), c_charset, t_charset);
1832                     m_charset_converter->set_client_query_charset(c_charset);
1833                     m_charset_converter->set_client_charset_selected(selected);
1834                 }
1835                 nmem_destroy(nmem);
1836                 ODR_MASK_CLEAR(m_initRequest_options, 
1837                                Z_Options_negotiationModel);
1838                 yaz_del_charneg_record(&initRequest->otherInfo);
1839             }
1840             else
1841             {
1842                 yaz_log(YLOG_WARN, "%sUnable to decode charset package",
1843                         m_session_str);
1844             }
1845         }
1846     }
1847     else if (apdu->which == Z_APDU_initResponse)
1848     {
1849         Z_InitResponse *initResponse = apdu->u.initResponse;    
1850         Z_OtherInformation **otherInfo;  
1851         
1852         if (ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel))
1853         {
1854             char *charset = 0;
1855             char *lang = 0;
1856             int selected = 0;
1857             
1858             get_otherInfoAPDU(apdu, &otherInfo);
1859             
1860             if (!otherInfo && !(*otherInfo))
1861                 return;
1862             
1863             Z_CharSetandLanguageNegotiation *charneg =
1864                 yaz_get_charneg_record(*otherInfo);
1865                 
1866             if (!charneg)
1867                 return;
1868                                 
1869             yaz_get_response_charneg(m_referenceId_mem, charneg,
1870                 &charset, &lang, &selected);
1871                                 
1872             yaz_log(YLOG_LOG, "%sAccepted charset - '%s' and lang - '%s'",
1873                 m_session_str, (charset)?charset:"none", (lang)?lang:"none");
1874                 
1875             if (m_initRequest_options &&
1876                 ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel))
1877             {
1878                 yaz_log(YLOG_LOG, "%sClient's negotiation record in use",
1879                     m_session_str);          
1880             }
1881             else if (m_proxy_negotiation_charset || m_proxy_negotiation_lang)
1882             {
1883                 // negotiation-charset, negotiation-lang
1884                 // elements of config file in use.
1885
1886                 yaz_log(YLOG_LOG, "%sProxy's negotiation record in use",
1887                     m_session_str);
1888
1889                 // clear negotiation option.
1890                 ODR_MASK_CLEAR(initResponse->options, Z_Options_negotiationModel);
1891                 
1892                 // Delete negotiation (charneg-3) entry.
1893                 yaz_del_charneg_record(otherInfo);
1894             }
1895         }
1896         else
1897         {
1898             if (m_proxy_negotiation_charset || m_proxy_negotiation_lang)
1899             {
1900                 yaz_log(YLOG_LOG, "%sTarget did not honor negotiation",
1901                         m_session_str);
1902             }
1903             else if (m_charset_converter->get_client_query_charset())
1904             {
1905                 Z_OtherInformation **otherInfo;  
1906                 Z_OtherInformationUnit *oi;
1907                 get_otherInfoAPDU(apdu, &otherInfo);
1908                 oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
1909                 if (oi)
1910                 {
1911                     ODR_MASK_SET(initResponse->options,
1912                                  Z_Options_negotiationModel);
1913                     ODR_MASK_SET(m_initRequest_options,
1914                                  Z_Options_negotiationModel);
1915
1916                     oi->which = Z_OtherInfo_externallyDefinedInfo;    
1917                     oi->information.externallyDefinedInfo =
1918                         yaz_set_response_charneg(
1919                             odr_encode(),
1920                             m_charset_converter->get_client_query_charset(),
1921                             0 /* no lang */,
1922                             m_charset_converter->get_client_charset_selected());
1923                 }
1924             }
1925         }
1926     }
1927 }
1928
1929 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
1930                                                      int error,
1931                                                      const char *addinfo)
1932 {
1933     Z_Records *rec = (Z_Records *)
1934         odr_malloc (odr, sizeof(*rec));
1935     int *err = (int *)
1936         odr_malloc (odr, sizeof(*err));
1937     Z_DiagRec *drec = (Z_DiagRec *)
1938         odr_malloc (odr, sizeof(*drec));
1939     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1940         odr_malloc (odr, sizeof(*dr));
1941     *err = error;
1942     rec->which = Z_Records_NSD;
1943     rec->u.nonSurrogateDiagnostic = dr;
1944     dr->diagnosticSetId =
1945         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1946     dr->condition = err;
1947     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1948     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1949     return rec;
1950 }
1951
1952 Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
1953 {
1954     if (apdu->which == Z_APDU_searchRequest &&
1955         apdu->u.searchRequest->query &&
1956         apdu->u.searchRequest->query->which == Z_Query_type_104 &&
1957         apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
1958     {
1959         Z_RPNQuery *rpnquery = 0;
1960         Z_SearchRequest *sr = apdu->u.searchRequest;
1961         char *addinfo = 0;
1962         
1963         yaz_log(YLOG_LOG, "%sCQL: %s", m_session_str,
1964                 sr->query->u.type_104->u.cql);
1965
1966         int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
1967                                           &rpnquery, odr_encode(),
1968                                           &addinfo);
1969         if (r == -3)
1970             yaz_log(YLOG_LOG, "%sNo CQL to RPN table", m_session_str);
1971         else if (r)
1972         {
1973             yaz_log(YLOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
1974             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1975
1976             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1977             new_apdu->u.searchResponse->records =
1978                 create_nonSurrogateDiagnostics(odr_encode(),
1979                                                yaz_diag_srw_to_bib1(r),
1980                                                addinfo);
1981             *new_apdu->u.searchResponse->searchStatus = 0;
1982
1983             send_to_client(new_apdu);
1984
1985             return 0;
1986         }
1987         else
1988         {
1989             sr->query->which = Z_Query_type_1;
1990             sr->query->u.type_1 = rpnquery;
1991         }
1992         return apdu;
1993     }
1994     return apdu;
1995 }
1996
1997 Z_APDU *Yaz_Proxy::handle_target_charset_conversion(Z_APDU *apdu)
1998 {
1999     if (apdu->which == Z_APDU_searchRequest &&
2000         apdu->u.searchRequest->query)
2001     {
2002         if (apdu->u.searchRequest->query->which == Z_Query_type_1
2003             || apdu->u.searchRequest->query->which == Z_Query_type_101)
2004         {
2005             if (m_http_version)
2006                 m_charset_converter->set_client_query_charset("UTF-8");
2007             Z_RPNQuery *rpnquery = apdu->u.searchRequest->query->u.type_1;
2008             m_charset_converter->convert_type_1(rpnquery, odr_encode());
2009         }
2010     }
2011     return apdu;
2012 }
2013
2014
2015 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
2016 {
2017     if (apdu->which == Z_APDU_searchRequest)
2018     {
2019         Z_SearchRequest *sr = apdu->u.searchRequest;
2020         int err = 0;
2021         char *addinfo = 0;
2022
2023         Yaz_ProxyConfig *cfg = check_reconfigure();
2024         if (cfg)
2025             err = cfg->check_query(odr_encode(), m_default_target,
2026                                    sr->query, &addinfo);
2027         if (err)
2028         {
2029             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2030
2031             new_apdu->u.searchResponse->referenceId = sr->referenceId;
2032             new_apdu->u.searchResponse->records =
2033                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2034             *new_apdu->u.searchResponse->searchStatus = 0;
2035
2036             send_to_client(new_apdu);
2037
2038             return 0;
2039         }
2040     }
2041     return apdu;
2042 }
2043
2044 int Yaz_Proxy::handle_authentication(Z_APDU *apdu)
2045 {
2046     if (apdu->which != Z_APDU_initRequest)
2047         return 1;  // pass if no init request
2048     Z_InitRequest *req = apdu->u.initRequest;
2049
2050     Yaz_ProxyConfig *cfg = check_reconfigure();
2051     if (!cfg)
2052         return 1;  // pass if no config
2053
2054     int ret;
2055     if (req->idAuthentication == 0)
2056     {
2057         ret = cfg->client_authentication(m_default_target, 0, 0, 0);
2058     }
2059     else if (req->idAuthentication->which == Z_IdAuthentication_idPass)
2060     {
2061         ret = cfg->client_authentication(m_default_target,
2062             req->idAuthentication->u.idPass->userId,
2063             req->idAuthentication->u.idPass->groupId,
2064             req->idAuthentication->u.idPass->password);
2065     }
2066     else if (req->idAuthentication->which == Z_IdAuthentication_open)
2067     {
2068         char user[64], pass[64];
2069         *user = '\0';
2070         *pass = '\0';
2071         sscanf(req->idAuthentication->u.open, "%63[^/]/%63s", user, pass);
2072         ret = cfg->client_authentication(m_default_target, user, 0, pass);
2073     }
2074     else
2075         ret = cfg->client_authentication(m_default_target, 0, 0, 0);
2076
2077     cfg->target_authentication(m_default_target, odr_encode(), req);
2078
2079     return ret;
2080 }
2081
2082 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
2083 {
2084     m_marcxml_mode = none;
2085     if (apdu->which == Z_APDU_searchRequest)
2086     {
2087         Z_SearchRequest *sr = apdu->u.searchRequest;
2088         int err = 0;
2089         char *addinfo = 0;
2090         Yaz_ProxyConfig *cfg = check_reconfigure();
2091
2092         Z_RecordComposition rc_temp, *rc = 0;
2093         if (sr->smallSetElementSetNames)
2094         {
2095             rc_temp.which = Z_RecordComp_simple;
2096             rc_temp.u.simple = sr->smallSetElementSetNames;
2097             rc = &rc_temp;
2098         }
2099
2100         if (sr->preferredRecordSyntax)
2101         {
2102             struct oident *ent;
2103             ent = oid_getentbyoid(sr->preferredRecordSyntax);
2104             m_frontend_type = ent->value;
2105         }
2106         else
2107             m_frontend_type = VAL_NONE;
2108
2109         char *stylesheet_name = 0;
2110         if (cfg)
2111             err = cfg->check_syntax(odr_encode(),
2112                                     m_default_target,
2113                                     sr->preferredRecordSyntax, rc,
2114                                     &addinfo, &stylesheet_name, &m_schema,
2115                                     &m_backend_type, &m_backend_charset,
2116                                     &m_usemarcon_ini_stage1,
2117                                     &m_usemarcon_ini_stage2);
2118         if (stylesheet_name)
2119         {
2120             m_parent->low_socket_close();
2121
2122 #if HAVE_XSLT
2123             if (m_stylesheet_xsp)
2124                 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
2125             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
2126                                                        stylesheet_name);
2127 #endif
2128             m_stylesheet_offset = 0;
2129             xfree(stylesheet_name);
2130
2131             m_parent->low_socket_open();
2132         }
2133         if (err == -1)
2134         {
2135             sr->smallSetElementSetNames = 0;
2136             sr->mediumSetElementSetNames = 0;
2137             m_marcxml_mode = marcxml;
2138             if (m_backend_type)
2139             {
2140                 
2141                 sr->preferredRecordSyntax =
2142                     yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
2143                                         m_backend_type);
2144             }
2145             else
2146                 sr->preferredRecordSyntax =
2147                     yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
2148                                            VAL_USMARC);
2149         }
2150         else if (err)
2151         {
2152             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2153             
2154             new_apdu->u.searchResponse->referenceId = sr->referenceId;
2155             new_apdu->u.searchResponse->records =
2156                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2157             *new_apdu->u.searchResponse->searchStatus = 0;
2158             
2159             send_to_client(new_apdu);
2160             
2161             return 0;
2162         }
2163         else if (m_backend_type)
2164         {
2165             sr->preferredRecordSyntax =
2166                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
2167         }
2168     }
2169     else if (apdu->which == Z_APDU_presentRequest)
2170     {
2171         Z_PresentRequest *pr = apdu->u.presentRequest;
2172         int err = 0;
2173         char *addinfo = 0;
2174         Yaz_ProxyConfig *cfg = check_reconfigure();
2175
2176         if (pr->preferredRecordSyntax)
2177         {
2178             struct oident *ent;
2179             ent = oid_getentbyoid(pr->preferredRecordSyntax);
2180             m_frontend_type = ent->value;
2181         }
2182         else
2183             m_frontend_type = VAL_NONE;
2184
2185         char *stylesheet_name = 0;
2186         if (cfg)
2187             err = cfg->check_syntax(odr_encode(), m_default_target,
2188                                     pr->preferredRecordSyntax,
2189                                     pr->recordComposition,
2190                                     &addinfo, &stylesheet_name, &m_schema,
2191                                     &m_backend_type, &m_backend_charset,
2192                                     &m_usemarcon_ini_stage1,
2193                                     &m_usemarcon_ini_stage2
2194                                     );
2195         if (stylesheet_name)
2196         {
2197             m_parent->low_socket_close();
2198
2199 #if HAVE_XSLT
2200             if (m_stylesheet_xsp)
2201                 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
2202             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
2203                                                        stylesheet_name);
2204 #endif
2205             m_stylesheet_offset = 0;
2206             xfree(stylesheet_name);
2207
2208             m_parent->low_socket_open();
2209         }
2210         if (err == -1)
2211         {
2212             pr->recordComposition = 0;
2213             m_marcxml_mode = marcxml;
2214             if (m_backend_type)
2215             {
2216                 
2217                 pr->preferredRecordSyntax =
2218                     yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
2219                                         m_backend_type);
2220             }
2221             else
2222                 pr->preferredRecordSyntax =
2223                     yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
2224                                            VAL_USMARC);
2225         }
2226         else if (err)
2227         {
2228             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
2229             
2230             new_apdu->u.presentResponse->referenceId = pr->referenceId;
2231             new_apdu->u.presentResponse->records =
2232                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2233             *new_apdu->u.presentResponse->presentStatus =
2234                 Z_PresentStatus_failure;
2235             
2236             send_to_client(new_apdu);
2237             
2238             return 0;
2239         }
2240         else if (m_backend_type)
2241         {
2242             pr->preferredRecordSyntax =
2243                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
2244         }
2245     }
2246     return apdu;
2247 }
2248
2249 Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
2250 {
2251     if (!schema)
2252         return 0;
2253     Z_ElementSetNames *esn = (Z_ElementSetNames *)
2254         odr_malloc(o, sizeof(Z_ElementSetNames));
2255     esn->which = Z_ElementSetNames_generic;
2256     esn->u.generic = odr_strdup(o, schema);
2257     return esn;
2258 }
2259
2260 void Yaz_Proxy::srw_get_client(const char *db, const char **backend_db)
2261 {
2262     const char *t = 0;
2263     Yaz_ProxyConfig *cfg = check_reconfigure();
2264     if (cfg)
2265         t = cfg->get_explain_name(db, backend_db);
2266
2267     if (m_client && m_default_target && t && strcmp(m_default_target, t))
2268     {
2269         releaseClient();
2270     }
2271     
2272     if (t)
2273     {
2274         xfree(m_default_target);
2275         m_default_target = xstrdup(t);
2276     }
2277 }
2278
2279 int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
2280 {
2281     struct stat sbuf;
2282     if (strcmp(hreq->method, "GET"))
2283         return 0;
2284     if (hreq->path[0] != '/')
2285     {
2286         yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
2287         return 0;
2288     }
2289     const char *cp = hreq->path;
2290     while (*cp)
2291     {
2292         if (*cp == '/' && strchr("/.", cp[1]))
2293         {
2294             yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
2295             return 0;
2296         }
2297         cp++;
2298     }
2299     const char *fname = hreq->path+1;
2300     if (stat(fname, &sbuf))
2301     {
2302         yaz_log(YLOG_WARN|YLOG_ERRNO, "%s: stat failed", fname);
2303         return 0;
2304     }
2305     if ((sbuf.st_mode & S_IFMT) != S_IFREG)
2306     {
2307         yaz_log(YLOG_WARN, "%s: not a regular file", fname);
2308         return 0;
2309     }
2310     if (sbuf.st_size > (off_t) 1000000)
2311     {
2312         yaz_log(YLOG_WARN, "%s: too large for transfer", fname);
2313         return 0;
2314     }
2315     
2316     ODR o = odr_encode();
2317     Yaz_ProxyConfig *cfg = check_reconfigure();
2318     const char *ctype = cfg->check_mime_type(fname);
2319     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
2320     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
2321     if (m_http_version)
2322         hres->version = odr_strdup(o, m_http_version);
2323     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
2324     if (m_http_keepalive)
2325         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
2326     else
2327         timeout(0);
2328
2329     hres->content_len = sbuf.st_size;
2330     hres->content_buf = (char*) odr_malloc(o, hres->content_len);
2331     FILE *f = fopen(fname, "rb");
2332     if (f)
2333     {
2334         fread(hres->content_buf, 1, hres->content_len, f);
2335         fclose(f);
2336     }
2337     else
2338     {
2339         return 0;
2340     }
2341     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
2342     {
2343         yaz_log (YLOG_LOG, "%sSending file %s to client", m_session_str,
2344                  fname);
2345     }
2346     int len;
2347     send_GDU(gdu, &len);
2348     return 1;
2349 }
2350         
2351 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
2352 {
2353     if (m_s2z_odr_init)
2354     {
2355         odr_destroy(m_s2z_odr_init);
2356         m_s2z_odr_init = 0;
2357     }
2358     if (m_s2z_odr_search)
2359     {
2360         odr_destroy(m_s2z_odr_search);
2361         m_s2z_odr_search = 0;
2362     }
2363
2364     m_http_keepalive = 0;
2365     m_http_version = 0;
2366     if (!strcmp(hreq->version, "1.0")) 
2367     {
2368         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
2369         if (v && !strcmp(v, "Keep-Alive"))
2370             m_http_keepalive = 1;
2371         else
2372             m_http_keepalive = 0;
2373         m_http_version = "1.0";
2374     }
2375     else
2376     {
2377         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
2378         if (v && !strcmp(v, "close"))
2379             m_http_keepalive = 0;
2380         else
2381             m_http_keepalive = 1;
2382         m_http_version = "1.1";
2383     }
2384
2385     Z_SRW_PDU *srw_pdu = 0;
2386     Z_SOAP *soap_package = 0;
2387     char *charset = 0;
2388     Z_SRW_diagnostic *diagnostic = 0;
2389     int num_diagnostic = 0;
2390
2391     if (file_access(hreq))
2392     {
2393         return;
2394     }
2395     else if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
2396                             &charset) == 0
2397              || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
2398                                &charset, &diagnostic, &num_diagnostic) == 0)
2399     {
2400         m_s2z_odr_init = odr_createmem(ODR_ENCODE);
2401         m_s2z_odr_search = odr_createmem(ODR_ENCODE);
2402         m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
2403         m_s2z_init_apdu = 0;
2404         m_s2z_search_apdu = 0;
2405         m_s2z_present_apdu = 0;
2406
2407         m_s2z_stylesheet = 0;
2408         
2409         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
2410         {
2411             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
2412
2413             const char *backend_db = srw_req->database;
2414             srw_get_client(srw_req->database, &backend_db);
2415
2416             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
2417             // recordXPath unsupported.
2418             if (srw_req->recordXPath)
2419             {
2420                 yaz_add_srw_diagnostic(odr_decode(),
2421                                        &diagnostic, &num_diagnostic,
2422                                        72, 0);
2423             }
2424             // sort unsupported
2425             if (srw_req->sort_type != Z_SRW_sort_type_none)
2426             {
2427                 yaz_add_srw_diagnostic(odr_decode(),
2428                                        &diagnostic, &num_diagnostic,
2429                                        80, 0);
2430             }
2431             // save stylesheet
2432             if (srw_req->stylesheet)
2433                 m_s2z_stylesheet =
2434                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
2435                                               
2436             // set packing for response records ..
2437             if (srw_req->recordPacking &&
2438                 !strcmp(srw_req->recordPacking, "xml"))
2439                 m_s2z_packing = Z_SRW_recordPacking_XML;
2440             else
2441                 m_s2z_packing = Z_SRW_recordPacking_string;
2442
2443             if (num_diagnostic)
2444             {
2445                 Z_SRW_PDU *srw_pdu =
2446                     yaz_srw_get(odr_encode(),
2447                                 Z_SRW_searchRetrieve_response);
2448                 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
2449                 
2450                 srw_res->diagnostics = diagnostic;
2451                 srw_res->num_diagnostics = num_diagnostic;
2452                 send_srw_response(srw_pdu);
2453                 return;
2454             }
2455
2456             // prepare search PDU
2457             m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
2458                                           Z_APDU_searchRequest);
2459             Z_SearchRequest *z_searchRequest =
2460                 m_s2z_search_apdu->u.searchRequest;
2461
2462             z_searchRequest->num_databaseNames = 1;
2463             z_searchRequest->databaseNames = (char**)
2464                 odr_malloc(m_s2z_odr_search, sizeof(char *));
2465             z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
2466                                                            backend_db);
2467             
2468             // query transformation
2469             Z_Query *query = (Z_Query *)
2470                 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
2471             z_searchRequest->query = query;
2472             
2473             if (srw_req->query_type == Z_SRW_query_type_cql)
2474             {
2475                 Z_External *ext = (Z_External *) 
2476                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
2477                 ext->direct_reference = 
2478                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
2479                 ext->indirect_reference = 0;
2480                 ext->descriptor = 0;
2481                 ext->which = Z_External_CQL;
2482                 ext->u.cql = srw_req->query.cql;
2483                 
2484                 query->which = Z_Query_type_104;
2485                 query->u.type_104 =  ext;
2486             }
2487             else if (srw_req->query_type == Z_SRW_query_type_pqf)
2488             {
2489                 Z_RPNQuery *RPNquery;
2490                 YAZ_PQF_Parser pqf_parser;
2491                 
2492                 pqf_parser = yaz_pqf_create ();
2493                 
2494                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
2495                                           srw_req->query.pqf);
2496                 if (!RPNquery)
2497                 {
2498                     const char *pqf_msg;
2499                     size_t off;
2500                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
2501                     yaz_log(YLOG_LOG, "%*s^\n", off+4, "");
2502                     yaz_log(YLOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
2503                     
2504                     send_to_srw_client_error(10, 0);
2505                     return;
2506                 }
2507                 query->which = Z_Query_type_1;
2508                 query->u.type_1 =  RPNquery;
2509                 
2510                 yaz_pqf_destroy (pqf_parser);
2511             }
2512             else
2513             {
2514                 send_to_srw_client_error(7, "query");
2515                 return;
2516             }
2517
2518             // present
2519             m_s2z_present_apdu = 0;
2520             int max = 0;
2521             if (srw_req->maximumRecords)
2522                 max = *srw_req->maximumRecords;
2523             int start = 1;
2524             if (srw_req->startRecord)
2525                 start = *srw_req->startRecord;
2526             if (max > 0)
2527             {
2528                 // Some backend, such as Voyager doesn't honor piggyback
2529                 // So we use present always (0 &&).
2530                 if (0 && start <= 1)  // Z39.50 piggyback
2531                 {
2532                     *z_searchRequest->smallSetUpperBound = max;
2533                     *z_searchRequest->mediumSetPresentNumber = max;
2534                     *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
2535
2536                     z_searchRequest->preferredRecordSyntax =
2537                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
2538                                                VAL_TEXT_XML);
2539                     if (srw_req->recordSchema)
2540                     {
2541                         z_searchRequest->smallSetElementSetNames =
2542                             z_searchRequest->mediumSetElementSetNames =
2543                             mk_esn_from_schema(m_s2z_odr_search,
2544                                                srw_req->recordSchema);
2545                     }
2546                 }
2547                 else   // Z39.50 present
2548                 {
2549                     m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
2550                                                    Z_APDU_presentRequest);
2551                     Z_PresentRequest *z_presentRequest = 
2552                         m_s2z_present_apdu->u.presentRequest;
2553                     *z_presentRequest->resultSetStartPoint = start;
2554                     *z_presentRequest->numberOfRecordsRequested = max;
2555                     z_presentRequest->preferredRecordSyntax =
2556                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
2557                                                VAL_TEXT_XML);
2558                     if (srw_req->recordSchema)
2559                     {
2560                         z_presentRequest->recordComposition =
2561                             (Z_RecordComposition *)
2562                             odr_malloc(m_s2z_odr_search,
2563                                        sizeof(Z_RecordComposition));
2564                         z_presentRequest->recordComposition->which = 
2565                             Z_RecordComp_simple;                    
2566                         z_presentRequest->recordComposition->u.simple =
2567                             mk_esn_from_schema(m_s2z_odr_search,
2568                                                srw_req->recordSchema);
2569                     }
2570                 }
2571             }
2572             if (!m_client)
2573             {
2574                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
2575                                             Z_APDU_initRequest);
2576                 
2577                 // prevent m_initRequest_apdu memory from being grabbed
2578                 // in Yaz_Proxy::handle_incoming_Z_PDU
2579                 m_initRequest_apdu = m_s2z_init_apdu;
2580                 handle_incoming_Z_PDU(m_s2z_init_apdu);
2581                 return;
2582             }
2583             else
2584             {
2585                 handle_incoming_Z_PDU(m_s2z_search_apdu);
2586                 return;
2587             }
2588         }
2589         else if (srw_pdu->which == Z_SRW_explain_request)
2590         {
2591             Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
2592
2593             const char *backend_db = srw_req->database;
2594             srw_get_client(srw_req->database, &backend_db);
2595
2596             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
2597
2598             // save stylesheet
2599             if (srw_req->stylesheet)
2600                 m_s2z_stylesheet =
2601                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
2602
2603             if (srw_req->recordPacking &&
2604                 !strcmp(srw_req->recordPacking, "xml"))
2605                 m_s2z_packing = Z_SRW_recordPacking_XML;
2606             else
2607                 m_s2z_packing = Z_SRW_recordPacking_string;
2608
2609             if (num_diagnostic)
2610             {
2611                 send_srw_explain_response(diagnostic, num_diagnostic);
2612                 return;
2613             }
2614
2615             if (!m_client)
2616             {
2617                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
2618                                             Z_APDU_initRequest);
2619                 
2620                 // prevent m_initRequest_apdu memory from being grabbed
2621                 // in Yaz_Proxy::handle_incoming_Z_PDU
2622                 m_initRequest_apdu = m_s2z_init_apdu;
2623                 handle_incoming_Z_PDU(m_s2z_init_apdu);
2624             }
2625             else
2626                 send_srw_explain_response(0, 0);
2627             return;
2628         }
2629         else if (srw_pdu->which == Z_SRW_scan_request)
2630         {
2631             m_s2z_database = odr_strdup(m_s2z_odr_init,
2632                                         srw_pdu->u.scan_request->database);
2633
2634             yaz_add_srw_diagnostic(odr_decode(),
2635                                    &diagnostic, &num_diagnostic,
2636                                    4, "scan");
2637             Z_SRW_PDU *srw_pdu =
2638                 yaz_srw_get(odr_encode(),
2639                             Z_SRW_scan_response);
2640             Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
2641             
2642             srw_res->diagnostics = diagnostic;
2643             srw_res->num_diagnostics = num_diagnostic;
2644             send_srw_response(srw_pdu);
2645             return;
2646         }
2647         else
2648         {
2649             m_s2z_database = 0;
2650
2651             send_to_srw_client_error(4, 0);
2652         }
2653     }
2654     send_http_response(400);
2655 }
2656
2657 void Yaz_Proxy::handle_init(Z_APDU *apdu)
2658 {
2659     Z_OtherInformation **oi;
2660     get_otherInfoAPDU(apdu, &oi);
2661
2662     if (apdu->u.initRequest->implementationId)
2663         yaz_log(YLOG_LOG, "%simplementationId: %s",
2664                 m_session_str, apdu->u.initRequest->implementationId);
2665     if (apdu->u.initRequest->implementationName)
2666         yaz_log(YLOG_LOG, "%simplementationName: %s",
2667                 m_session_str, apdu->u.initRequest->implementationName);
2668     if (apdu->u.initRequest->implementationVersion)
2669         yaz_log(YLOG_LOG, "%simplementationVersion: %s",
2670                 m_session_str, apdu->u.initRequest->implementationVersion);
2671     if (m_initRequest_apdu == 0)
2672     {
2673         if (m_initRequest_mem)
2674             nmem_destroy(m_initRequest_mem);
2675         
2676         m_initRequest_apdu = apdu;
2677         m_initRequest_mem = odr_extract_mem(odr_decode());
2678         
2679         m_initRequest_preferredMessageSize = *apdu->u.initRequest->
2680             preferredMessageSize;
2681         *apdu->u.initRequest->preferredMessageSize = 1024*1024;
2682         m_initRequest_maximumRecordSize = *apdu->u.initRequest->
2683             maximumRecordSize;
2684         *apdu->u.initRequest->maximumRecordSize = 1024*1024;
2685
2686         Z_CharSetandLanguageNegotiation *charSetandLangRecord =
2687             yaz_get_charneg_record(*oi);
2688         
2689         // Save proposal charsets and langs.
2690         if (ODR_MASK_GET(apdu->u.initRequest->options,
2691                          Z_Options_negotiationModel) 
2692             && charSetandLangRecord)
2693         {
2694             
2695             yaz_get_proposal_charneg(m_referenceId_mem,
2696                                      charSetandLangRecord,
2697                                      &m_initRequest_oi_negotiation_charsets,
2698                                      &m_initRequest_oi_negotiation_num_charsets,
2699                                      &m_initRequest_oi_negotiation_langs,
2700                                      &m_initRequest_oi_negotiation_num_langs,
2701                                      &m_initRequest_oi_negotiation_selected);
2702             
2703             for (int i = 0; i<m_initRequest_oi_negotiation_num_charsets; i++)
2704             {
2705                 yaz_log(YLOG_LOG, "%scharacters set proposal: %s",
2706                         m_session_str,(m_initRequest_oi_negotiation_charsets[i])?
2707                         m_initRequest_oi_negotiation_charsets[i]:"none");
2708             }
2709             for (int i=0; i<m_initRequest_oi_negotiation_num_langs; i++)
2710             {
2711                 yaz_log(YLOG_LOG, "%slanguages proposal: %s",
2712                         m_session_str, (m_initRequest_oi_negotiation_langs[i])?
2713                         m_initRequest_oi_negotiation_langs[i]:"none");
2714             }
2715             yaz_log(YLOG_LOG, "%sselected proposal: %d (boolean)",
2716                     m_session_str, m_initRequest_oi_negotiation_selected);
2717         }       
2718         // save init options for the response..
2719         m_initRequest_options = apdu->u.initRequest->options;
2720         
2721         apdu->u.initRequest->options = 
2722             (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2723                                        sizeof(Odr_bitmask));
2724         ODR_MASK_ZERO(apdu->u.initRequest->options);
2725         int i;
2726         for (i = 0; i<= 24; i++)
2727             ODR_MASK_SET(apdu->u.initRequest->options, i);
2728         // check negotiation option
2729         if (!ODR_MASK_GET(m_initRequest_options,
2730                           Z_Options_negotiationModel))
2731         {
2732             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2733                            Z_Options_negotiationModel);
2734         }
2735         ODR_MASK_CLEAR(apdu->u.initRequest->options,
2736                        Z_Options_concurrentOperations);
2737         // make new version
2738         m_initRequest_version = apdu->u.initRequest->protocolVersion;
2739         apdu->u.initRequest->protocolVersion = 
2740             (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2741                                        sizeof(Odr_bitmask));
2742         ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
2743         
2744         for (i = 0; i<= 8; i++)
2745             ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
2746     }
2747     if (m_client->m_init_flag)
2748     {
2749         if (handle_init_response_for_invalid_session(apdu))
2750             return;
2751         if (m_client->m_initResponse)
2752         {
2753             Z_APDU *apdu2 = m_client->m_initResponse;
2754             apdu2->u.initResponse->otherInfo = 0;
2755             if (m_client->m_cookie && *m_client->m_cookie)
2756                 set_otherInformationString(apdu2, VAL_COOKIE, 1,
2757                                            m_client->m_cookie);
2758             apdu2->u.initResponse->referenceId =
2759                 apdu->u.initRequest->referenceId;
2760             apdu2->u.initResponse->options = m_client->m_initResponse_options;
2761             apdu2->u.initResponse->protocolVersion = 
2762                 m_client->m_initResponse_version;
2763             
2764             send_to_client(apdu2);
2765             return;
2766         }
2767     }
2768     m_client->m_init_flag = 1;
2769     
2770     Auth_Msg *m = new Auth_Msg;
2771     m->m_apdu = apdu;
2772     m->m_proxy = this;
2773     m_my_thread->put(m);
2774 }
2775
2776 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
2777 {
2778     Z_ReferenceId **refid = get_referenceIdP(apdu);
2779     nmem_reset(m_referenceId_mem);
2780     if (refid && *refid)
2781     {
2782         m_referenceId = (Z_ReferenceId *)
2783             nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
2784         m_referenceId->len = m_referenceId->size = (*refid)->len;
2785         m_referenceId->buf = (unsigned char *)
2786             nmem_malloc(m_referenceId_mem, (*refid)->len);
2787         memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
2788     }
2789     else
2790         m_referenceId = 0;
2791
2792     if (!m_client && m_invalid_session)
2793     {
2794         m_apdu_invalid_session = apdu;
2795         m_mem_invalid_session = odr_extract_mem(odr_decode());
2796         apdu = m_initRequest_apdu;
2797     }
2798     
2799     // Determine our client.
2800     Z_OtherInformation **oi;
2801     get_otherInfoAPDU(apdu, &oi);
2802     m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
2803     if (!m_client)
2804     {
2805         if (m_http_version)
2806         {
2807             send_http_response(404);
2808             return;
2809         }
2810         else
2811         {
2812             delete this;
2813             return;
2814         }
2815     }
2816     
2817     m_client->m_server = this;
2818
2819     if (apdu->which == Z_APDU_initRequest)
2820         handle_init(apdu);
2821     else
2822         handle_incoming_Z_PDU_2(apdu);
2823 }
2824
2825 void Yaz_Proxy::handle_incoming_Z_PDU_2(Z_APDU *apdu)
2826 {
2827     handle_max_record_retrieve(apdu);
2828     
2829     if (apdu)
2830         apdu = handle_syntax_validation(apdu);
2831
2832     if (apdu)
2833         apdu = handle_query_transformation(apdu);
2834
2835     if (apdu)
2836         apdu = handle_target_charset_conversion(apdu);
2837
2838     if (apdu)
2839         apdu = handle_query_validation(apdu);
2840
2841     if (apdu)
2842         apdu = result_set_optimize(apdu);
2843
2844     if (!apdu)
2845     {
2846         m_client->timeout(m_target_idletime);  // mark it active even 
2847         // though we didn't use it
2848         return;
2849     }
2850     // Add otherInformation entry in APDU if
2851     // negotiation is in use.
2852     if (apdu)
2853         handle_charset_lang_negotiation(apdu);
2854
2855     // delete other info construct completely if 0 elements
2856     Z_OtherInformation **oi;
2857     get_otherInfoAPDU(apdu, &oi);
2858     if (oi && *oi && (*oi)->num_elements == 0)
2859         *oi = 0;
2860
2861     if (apdu->which == Z_APDU_presentRequest &&
2862         m_client->m_resultSetStartPoint == 0)
2863     {
2864         Z_PresentRequest *pr = apdu->u.presentRequest;
2865         m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
2866         m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
2867     } else {
2868         m_client->m_resultSetStartPoint = 0;
2869     }
2870     if (m_client->send_to_target(apdu) < 0)
2871     {
2872         delete m_client;
2873         m_client = 0;
2874         delete this;
2875     }
2876     else
2877         m_client->m_waiting = 1;
2878 }
2879
2880 void Yaz_Proxy::connectNotify()
2881 {
2882 }
2883
2884 void Yaz_Proxy::releaseClient()
2885 {
2886     xfree(m_proxyTarget);
2887     m_proxyTarget = 0;
2888     m_invalid_session = 0;
2889     // only keep if keep_alive flag is set...
2890     if (m_client && 
2891         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
2892         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
2893         m_client->m_waiting == 0)
2894     {
2895         yaz_log(YLOG_LOG, "%sShutdown (client to proxy) keepalive %s",
2896                  m_session_str,
2897                  m_client->get_hostname());
2898         yaz_log(YLOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
2899                 m_session_str, m_client->m_pdu_recv,
2900                 m_client->m_bytes_sent + m_client->m_bytes_recv,
2901                 m_keepalive_limit_bw, m_keepalive_limit_pdu);
2902         assert (m_client->m_waiting != 2);
2903         // Tell client (if any) that no server connection is there..
2904         m_client->m_server = 0;
2905         m_client = 0;
2906     }
2907     else if (m_client)
2908     {
2909         yaz_log (YLOG_LOG, "%sShutdown (client to proxy) close %s",
2910                  m_session_str,
2911                  m_client->get_hostname());
2912         assert (m_client->m_waiting != 2);
2913         delete m_client;
2914         m_client = 0;
2915     }
2916     else if (!m_parent)
2917     {
2918         yaz_log (YLOG_LOG, "%sshutdown (client to proxy) bad state",
2919                  m_session_str);
2920         assert (m_parent);
2921     }
2922     else 
2923     {
2924         yaz_log (YLOG_LOG, "%sShutdown (client to proxy)",
2925                  m_session_str);
2926     }
2927     if (m_parent)
2928         m_parent->pre_init();
2929 }
2930
2931 void Yaz_Proxy::shutdown()
2932 {
2933     releaseClient();
2934     delete this;
2935 }
2936
2937 const char *Yaz_ProxyClient::get_session_str() 
2938 {
2939     if (!m_server)
2940         return "0 ";
2941     return m_server->get_session_str();
2942 }
2943
2944 void Yaz_ProxyClient::shutdown()
2945 {
2946     yaz_log (YLOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
2947              get_hostname());
2948     delete m_server;
2949     delete this;
2950 }
2951
2952 void Yaz_Proxy::failNotify()
2953 {
2954     inc_request_no();
2955     yaz_log (YLOG_LOG, "%sConnection closed by client",
2956              get_session_str());
2957     shutdown();
2958 }
2959
2960 void Yaz_ProxyClient::failNotify()
2961 {
2962     if (m_server)
2963         m_server->inc_request_no();
2964     yaz_log (YLOG_LOG, "%sConnection closed by target %s", 
2965              get_session_str(), get_hostname());
2966     shutdown();
2967 }
2968
2969 void Yaz_ProxyClient::connectNotify()
2970 {
2971     const char *s = get_session_str();
2972     const char *h = get_hostname();
2973     yaz_log (YLOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
2974              m_target_idletime);
2975     timeout(m_target_idletime);
2976     if (!m_server)
2977         pre_init_client();
2978 }
2979
2980 IPDU_Observer *Yaz_ProxyClient::sessionNotify(IPDU_Observable
2981                                               *the_PDU_Observable, int fd)
2982 {
2983     return new Yaz_ProxyClient(the_PDU_Observable, 0);
2984 }
2985
2986 Yaz_ProxyClient::~Yaz_ProxyClient()
2987 {
2988     if (m_prev)
2989         *m_prev = m_next;
2990     if (m_next)
2991         m_next->m_prev = m_prev;
2992     m_waiting = 2;     // for debugging purposes only.
2993     odr_destroy(m_init_odr);
2994     delete m_last_query;
2995     xfree (m_last_resultSetId);
2996     xfree (m_cookie);
2997 }
2998
2999 void Yaz_ProxyClient::pre_init_client()
3000 {
3001     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
3002     Z_InitRequest *req = apdu->u.initRequest;
3003     
3004     int i;
3005     for (i = 0; i<= 24; i++)
3006         ODR_MASK_SET(req->options, i);
3007     ODR_MASK_CLEAR(apdu->u.initRequest->options,
3008                    Z_Options_negotiationModel);
3009     ODR_MASK_CLEAR(apdu->u.initRequest->options,
3010                    Z_Options_concurrentOperations);
3011     for (i = 0; i<= 10; i++)
3012         ODR_MASK_SET(req->protocolVersion, i);
3013
3014     if (send_to_target(apdu) < 0)
3015     {
3016         delete this;
3017     }
3018     else
3019     {
3020         m_waiting = 1;
3021         m_init_flag = 1;
3022     }
3023 }
3024
3025 void Yaz_Proxy::pre_init()
3026 {
3027     int i;
3028     const char *name = 0;
3029     const char *zurl_in_use[MAX_ZURL_PLEX];
3030     int limit_bw, limit_pdu, limit_req;
3031     int target_idletime, client_idletime;
3032     int max_clients;
3033     int keepalive_limit_bw, keepalive_limit_pdu;
3034     int pre_init;
3035     const char *cql2rpn = 0;
3036     const char *authentication = 0;
3037     const char *negotiation_charset = 0;
3038     const char *negotiation_lang = 0;
3039
3040     Yaz_ProxyConfig *cfg = check_reconfigure();
3041
3042     zurl_in_use[0] = 0;
3043
3044     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
3045         set_APDU_yazlog(1);
3046     else
3047         set_APDU_yazlog(0);
3048
3049     for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
3050                                           &limit_bw, &limit_pdu, &limit_req,
3051                                           &target_idletime, &client_idletime,
3052                                           &max_clients, 
3053                                           &keepalive_limit_bw,
3054                                           &keepalive_limit_pdu,
3055                                           &pre_init,
3056                                           &cql2rpn,
3057                                           &authentication,
3058                                           &negotiation_charset,
3059                                           &negotiation_lang,
3060                                           0) ; i++)
3061     {
3062         if (pre_init)
3063         {
3064             int j;
3065             for (j = 0; zurl_in_use[j]; j++)
3066             {
3067                 Yaz_ProxyClient *c;
3068                 int spare = 0;
3069                 int spare_waiting = 0;
3070                 int in_use = 0;
3071                 int other = 0;
3072                 for (c = m_clientPool; c; c = c->m_next)
3073                 {
3074                     if (!strcmp(zurl_in_use[j], c->get_hostname()))
3075                     {
3076                         if (c->m_cookie == 0)
3077                         {
3078                             if (c->m_server == 0)
3079                                 if (c->m_waiting)
3080                                     spare_waiting++;
3081                                 else
3082                                     spare++;
3083                             else
3084                                 in_use++;
3085                         }
3086                         else
3087                             other++;
3088                     }
3089                 }
3090                 yaz_log(YLOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
3091                         "sparew=%d preinit=%d",m_session_str,
3092                         name, zurl_in_use[j], in_use, other,
3093                         spare, spare_waiting, pre_init);
3094                 if (spare + spare_waiting < pre_init)
3095                 {
3096                     c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
3097                     c->m_next = m_clientPool;
3098                     if (c->m_next)
3099                         c->m_next->m_prev = &c->m_next;
3100                     m_clientPool = c;
3101                     c->m_prev = &m_clientPool;
3102                     
3103                     if (m_log_mask & PROXY_LOG_APDU_SERVER)
3104                         c->set_APDU_yazlog(1);
3105                     else
3106                         c->set_APDU_yazlog(0);
3107
3108                     if (c->client(zurl_in_use[j]))
3109                     {
3110                         timeout(60);
3111                         delete c;
3112                         return;
3113                     }
3114                     c->timeout(30);
3115                     c->m_waiting = 1;
3116                     c->m_target_idletime = target_idletime;
3117                     c->m_seqno = m_seqno++;
3118                 }
3119             }
3120         }
3121     }
3122 }
3123
3124 void Yaz_Proxy::timeoutNotify()
3125 {
3126     if (m_parent)
3127     {
3128         if (m_bw_hold_PDU)
3129         {
3130             timeout(m_client_idletime);
3131             Z_GDU *apdu = m_bw_hold_PDU;
3132             m_bw_hold_PDU = 0;
3133             
3134             if (apdu->which == Z_GDU_Z3950)
3135                 handle_incoming_Z_PDU(apdu->u.z3950);
3136             else if (apdu->which == Z_GDU_HTTP_Request)
3137                 handle_incoming_HTTP(apdu->u.HTTP_Request);
3138         }
3139         else if (m_stylesheet_nprl)
3140             convert_xsl_delay();
3141         else
3142         {
3143             inc_request_no();
3144
3145             yaz_log (YLOG_LOG, "%sTimeout (client to proxy)", m_session_str);
3146             shutdown();
3147         }
3148     }
3149     else
3150     {
3151         timeout(600);
3152         pre_init();
3153     }
3154 }
3155
3156 void Yaz_Proxy::markInvalid()
3157 {
3158     m_client = 0;
3159     m_invalid_session = 1;
3160 }
3161
3162 void Yaz_ProxyClient::timeoutNotify()
3163 {
3164     if (m_server)
3165         m_server->inc_request_no();
3166
3167     yaz_log (YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
3168              get_hostname());
3169     m_waiting = 1;
3170     m_root->pre_init();
3171     if (m_server && m_init_flag)
3172     {
3173         // target timed out in a session that was properly initialized
3174         // server object stay alive but we mark it as invalid so it
3175         // gets initialized again
3176         m_server->markInvalid();
3177         m_server = 0;
3178     }
3179     shutdown();
3180 }
3181
3182 Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
3183                                  Yaz_Proxy *parent) :
3184     Z_Assoc (the_PDU_Observable)
3185 {
3186     m_cookie = 0;
3187     m_next = 0;
3188     m_prev = 0;
3189     m_init_flag = 0;
3190     m_last_query = 0;
3191     m_last_resultSetId = 0;
3192     m_last_resultCount = 0;
3193     m_last_ok = 0;
3194     m_sr_transform = 0;
3195     m_waiting = 0;
3196     m_init_odr = odr_createmem (ODR_DECODE);
3197     m_initResponse = 0;
3198     m_initResponse_options = 0;
3199     m_initResponse_version = 0;
3200     m_initResponse_preferredMessageSize = 0;
3201     m_initResponse_maximumRecordSize = 0;
3202     m_resultSetStartPoint = 0;
3203     m_bytes_sent = m_bytes_recv = 0;
3204     m_pdu_recv = 0;
3205     m_server = 0;
3206     m_seqno = 0;
3207     m_target_idletime = 600;
3208     m_root = parent;
3209 }
3210
3211 const char *Yaz_Proxy::option(const char *name, const char *value)
3212 {
3213     if (!strcmp (name, "optimize")) {
3214         if (value) {
3215             xfree (m_optimize); 
3216             m_optimize = xstrdup (value);
3217         }
3218         return m_optimize;
3219     }
3220     return 0;
3221 }
3222
3223 void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
3224 {
3225
3226 }
3227
3228 void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
3229 {
3230     if (apdu->which == Z_GDU_Z3950)
3231         recv_Z_PDU(apdu->u.z3950, len);
3232     else if (apdu->which == Z_GDU_HTTP_Response)
3233         recv_HTTP_response(apdu->u.HTTP_Response, len);
3234     else
3235         shutdown();
3236 }
3237
3238 int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
3239 {
3240     if (!m_invalid_session)
3241         return 0;
3242     m_invalid_session = 0;
3243     handle_incoming_Z_PDU(m_apdu_invalid_session);
3244     assert (m_mem_invalid_session);
3245     nmem_destroy(m_mem_invalid_session);
3246     m_mem_invalid_session = 0;
3247     return 1;
3248 }
3249
3250 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
3251 {
3252     m_bytes_recv += len;
3253
3254     m_pdu_recv++;
3255     m_waiting = 0;
3256     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
3257         yaz_log (YLOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
3258                  apdu_name(apdu), get_hostname(), len);
3259     if (apdu->which == Z_APDU_initResponse)
3260     {
3261         if (!m_server)  // if this is a pre init session , check for more
3262             m_root->pre_init();
3263         NMEM nmem = odr_extract_mem (odr_decode());
3264         odr_reset (m_init_odr);
3265         nmem_transfer (m_init_odr->mem, nmem);
3266         m_initResponse = apdu;
3267         m_initResponse_options = apdu->u.initResponse->options;
3268         m_initResponse_version = apdu->u.initResponse->protocolVersion;
3269         m_initResponse_preferredMessageSize = 
3270             *apdu->u.initResponse->preferredMessageSize;
3271         m_initResponse_maximumRecordSize = 
3272             *apdu->u.initResponse->maximumRecordSize;
3273
3274         Z_InitResponse *ir = apdu->u.initResponse;
3275         char *im0 = ir->implementationName;
3276         
3277         char *im1 = (char*) 
3278             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
3279         *im1 = '\0';
3280         if (im0)
3281         {
3282             strcat(im1, im0);
3283             strcat(im1, " ");
3284         }
3285         strcat(im1, "(YAZ Proxy)");
3286         ir->implementationName = im1;
3287
3288         nmem_destroy (nmem);
3289
3290         if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
3291             return;
3292     }
3293     if (apdu->which == Z_APDU_searchResponse)
3294     {
3295         Z_SearchResponse *sr = apdu->u.searchResponse;
3296         m_last_resultCount = *sr->resultCount;
3297         int status = *sr->searchStatus;
3298         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
3299         {
3300             m_last_ok = 1;
3301             
3302             if (sr->records && sr->records->which == Z_Records_DBOSD)
3303             {
3304                 m_cache.add(odr_decode(),
3305                             sr->records->u.databaseOrSurDiagnostics, 1,
3306                             *sr->resultCount);
3307             }
3308         }
3309     }
3310     if (apdu->which == Z_APDU_presentResponse)
3311     {
3312         Z_PresentResponse *pr = apdu->u.presentResponse;
3313         if (m_sr_transform)
3314         {
3315             m_sr_transform = 0;
3316             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
3317             Z_SearchResponse *sr = new_apdu->u.searchResponse;
3318             sr->referenceId = pr->referenceId;
3319             *sr->resultCount = m_last_resultCount;
3320             sr->records = pr->records;
3321             sr->nextResultSetPosition = pr->nextResultSetPosition;
3322             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
3323             apdu = new_apdu;
3324         }
3325         if (pr->records && 
3326             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
3327         {
3328             m_cache.add(odr_decode(),
3329                         pr->records->u.databaseOrSurDiagnostics,
3330                         m_resultSetStartPoint, -1);
3331             m_resultSetStartPoint = 0;
3332         }
3333     }
3334     if (m_cookie)
3335         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
3336     if (m_server)
3337     {
3338         m_server->send_to_client(apdu);
3339     }
3340     if (apdu->which == Z_APDU_close)
3341     {
3342         shutdown();
3343     }
3344 }
3345
3346 void Yaz_Proxy::low_socket_close()
3347 {
3348 #if WIN32
3349 #else
3350     int i;
3351     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
3352         if  (m_lo_fd[i] >= 0)
3353             ::close(m_lo_fd[i]);
3354 #endif
3355 }
3356
3357 void Yaz_Proxy::low_socket_open()
3358 {
3359 #if WIN32
3360 #else
3361     int i;
3362     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
3363         m_lo_fd[i] = open("/dev/null", O_RDONLY);
3364 #endif
3365 }
3366
3367 int Yaz_Proxy::server(const char *addr)
3368 {
3369     int r = Z_Assoc::server(addr);
3370     if (!r)
3371     {
3372         yaz_log(YLOG_LOG, "%sStarted proxy " 
3373 #ifdef VERSION
3374             VERSION 
3375 #endif
3376             " on %s", m_session_str, addr);
3377         timeout(1);
3378     }
3379     return r;
3380 }
3381