Simplify use of iconv for MARC records
[yazpp-moved-to-github.git] / src / yaz-proxy.cpp
1 /*
2  * Copyright (c) 1998-2004, Index Data.
3  * See the file LICENSE for details.
4  * 
5  * $Id: yaz-proxy.cpp,v 1.109 2004-03-15 22:49:13 adam Exp $
6  */
7
8 #include <unistd.h>
9 #include <assert.h>
10 #include <time.h>
11 #include <sys/types.h>
12 #include <fcntl.h>
13
14 #include <yaz/srw.h>
15 #include <yaz/marcdisp.h>
16 #include <yaz/yaz-iconv.h>
17 #include <yaz/log.h>
18 #include <yaz/diagbib1.h>
19 #include <yaz++/proxy.h>
20 #include <yaz/pquery.h>
21
22 static const char *apdu_name(Z_APDU *apdu)
23 {
24     switch (apdu->which)
25     {
26     case Z_APDU_initRequest:
27         return "initRequest";
28     case Z_APDU_initResponse:
29         return "initResponse";
30     case Z_APDU_searchRequest:
31         return "searchRequest";
32     case Z_APDU_searchResponse:
33         return "searchResponse";
34     case Z_APDU_presentRequest:
35         return "presentRequest";
36     case Z_APDU_presentResponse:
37         return "presentResponse";
38     case Z_APDU_deleteResultSetRequest:
39         return "deleteResultSetRequest";
40     case Z_APDU_deleteResultSetResponse:
41         return "deleteResultSetResponse";
42     case Z_APDU_scanRequest:
43         return "scanRequest";
44     case Z_APDU_scanResponse:
45         return "scanResponse";
46     case Z_APDU_sortRequest:
47         return "sortRequest";
48     case Z_APDU_sortResponse:
49         return "sortResponse";
50     case Z_APDU_extendedServicesRequest:
51         return "extendedServicesRequest";
52     case Z_APDU_extendedServicesResponse:
53         return "extendedServicesResponse";
54     case Z_APDU_close:
55         return "close";
56     }
57     return "other";
58 }
59
60 static const char *gdu_name(Z_GDU *gdu)
61 {
62     switch(gdu->which)
63     {
64     case Z_GDU_Z3950:
65         return apdu_name(gdu->u.z3950);
66     case Z_GDU_HTTP_Request:
67         return "HTTP Request";
68     case Z_GDU_HTTP_Response:
69         return "HTTP Response";
70     }
71     return "Unknown request/response";
72 }
73
74 Yaz_Proxy::Yaz_Proxy(IYaz_PDU_Observable *the_PDU_Observable,
75                      Yaz_Proxy *parent) :
76     Yaz_Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
77 {
78     m_PDU_Observable = the_PDU_Observable;
79     m_client = 0;
80     m_parent = parent;
81     m_clientPool = 0;
82     m_seqno = 1;
83     m_keepalive_limit_bw = 500000;
84     m_keepalive_limit_pdu = 1000;
85     m_proxyTarget = 0;
86     m_default_target = 0;
87     m_proxy_authentication = 0;
88     m_max_clients = 150;
89     m_log_mask = 0;
90     m_seed = time(0);
91     m_client_idletime = 600;
92     m_target_idletime = 600;
93     m_optimize = xstrdup ("1");
94     strcpy(m_session_str, "0 ");
95     m_session_no=0;
96     m_bytes_sent = 0;
97     m_bytes_recv = 0;
98     m_bw_hold_PDU = 0;
99     m_bw_max = 0;
100     m_pdu_max = 0;
101     m_max_record_retrieve = 0;
102     m_reconfig_flag = 0;
103     m_config_fname = 0;
104     m_request_no = 0;
105     m_invalid_session = 0;
106     m_referenceId = 0;
107     m_referenceId_mem = nmem_create();
108     m_config = 0;
109     m_marcxml_flag = 0;
110     m_stylesheet_xsp = 0;
111     m_stylesheet_nprl = 0;
112     m_s2z_stylesheet = 0;
113     m_s2z_database = 0;
114     m_schema = 0;
115     m_initRequest_apdu = 0;
116     m_initRequest_mem = 0;
117     m_initRequest_preferredMessageSize = 0;
118     m_initRequest_maximumRecordSize = 0;
119     m_initRequest_options = 0;
120     m_initRequest_version = 0;
121     m_apdu_invalid_session = 0;
122     m_mem_invalid_session = 0;
123     m_s2z_odr_init = 0;
124     m_s2z_odr_search = 0;
125     m_s2z_init_apdu = 0;
126     m_s2z_search_apdu = 0;
127     m_s2z_present_apdu = 0;
128     m_http_keepalive = 0;
129     m_http_version = 0;
130     m_soap_ns = 0;
131     m_s2z_packing = Z_SRW_recordPacking_string;
132     m_time_tv.tv_sec = 0;
133     m_time_tv.tv_usec = 0;
134     if (!m_parent)
135         low_socket_open();
136 }
137
138 Yaz_Proxy::~Yaz_Proxy()
139 {
140     yaz_log(LOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
141             m_bytes_sent, m_bytes_recv);
142     nmem_destroy(m_initRequest_mem);
143     nmem_destroy(m_mem_invalid_session);
144     nmem_destroy(m_referenceId_mem);
145
146     xfree (m_proxyTarget);
147     xfree (m_default_target);
148     xfree (m_proxy_authentication);
149     xfree (m_optimize);
150
151     if (m_stylesheet_xsp)
152         xsltFreeStylesheet(m_stylesheet_xsp);
153
154     xfree (m_schema);
155     if (m_s2z_odr_init)
156         odr_destroy(m_s2z_odr_init);
157     if (m_s2z_odr_search)
158         odr_destroy(m_s2z_odr_search);
159     if (!m_parent)
160         low_socket_close();
161     delete m_config;
162 }
163
164 int Yaz_Proxy::set_config(const char *config)
165 {
166     delete m_config;
167     m_config = new Yaz_ProxyConfig();
168     xfree(m_config_fname);
169     m_config_fname = xstrdup(config);
170     int r = m_config->read_xml(config);
171     if (!r)
172         m_config->get_generic_info(&m_log_mask, &m_max_clients);
173     return r;
174 }
175
176 void Yaz_Proxy::set_default_target(const char *target)
177 {
178     xfree (m_default_target);
179     m_default_target = 0;
180     if (target)
181         m_default_target = (char *) xstrdup (target);
182 }
183
184 void Yaz_Proxy::set_proxy_authentication (const char *auth)
185 {
186     xfree (m_proxy_authentication);
187     m_proxy_authentication = 0;
188     if (auth)
189         m_proxy_authentication = (char *) xstrdup (auth);
190 }
191
192 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
193 {
194     if (m_parent)
195         return m_parent->check_reconfigure();
196
197     Yaz_ProxyConfig *cfg = m_config;
198     if (m_reconfig_flag)
199     {
200         yaz_log(LOG_LOG, "reconfigure");
201         yaz_log_reopen();
202         if (m_config_fname && cfg)
203         {
204             yaz_log(LOG_LOG, "reconfigure config %s", m_config_fname);
205             int r = cfg->read_xml(m_config_fname);
206             if (r)
207                 yaz_log(LOG_WARN, "reconfigure failed");
208             else
209             {
210                 m_log_mask = 0;
211                 cfg->get_generic_info(&m_log_mask, &m_max_clients);
212             }
213         }
214         else
215             yaz_log(LOG_LOG, "reconfigure");
216         m_reconfig_flag = 0;
217     }
218     return cfg;
219 }
220
221 IYaz_PDU_Observer *Yaz_Proxy::sessionNotify(IYaz_PDU_Observable
222                                             *the_PDU_Observable, int fd)
223 {
224     check_reconfigure();
225     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable, this);
226     new_proxy->m_config = 0;
227     new_proxy->m_config_fname = 0;
228     new_proxy->timeout(m_client_idletime);
229     new_proxy->m_target_idletime = m_target_idletime;
230     new_proxy->set_default_target(m_default_target);
231     new_proxy->m_max_clients = m_max_clients;
232     new_proxy->m_log_mask = m_log_mask;
233     new_proxy->set_APDU_log(get_APDU_log());
234     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
235         new_proxy->set_APDU_yazlog(1);
236     else
237         new_proxy->set_APDU_yazlog(0);
238     new_proxy->set_proxy_authentication(m_proxy_authentication);
239     sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
240     m_session_no++;
241     yaz_log (LOG_LOG, "%sNew session %s", new_proxy->m_session_str,
242              the_PDU_Observable->getpeername());
243     return new_proxy;
244 }
245
246 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
247 {
248     int oid[OID_SIZE];
249     Z_OtherInformationUnit *oi;
250     struct oident ent;
251     ent.proto = PROTO_Z3950;
252     ent.oclass = CLASS_USERINFO;
253     ent.value = (oid_value) VAL_COOKIE;
254     assert (oid_ent_to_oid (&ent, oid));
255
256     if (oid_ent_to_oid (&ent, oid) && 
257         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
258         oi->which == Z_OtherInfo_characterInfo)
259         return oi->information.characterInfo;
260     return 0;
261 }
262
263 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
264 {
265     int oid[OID_SIZE];
266     Z_OtherInformationUnit *oi;
267     struct oident ent;
268     ent.proto = PROTO_Z3950;
269     ent.oclass = CLASS_USERINFO;
270     ent.value = (oid_value) VAL_PROXY;
271     if (oid_ent_to_oid (&ent, oid) &&
272         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
273         oi->which == Z_OtherInfo_characterInfo)
274         return oi->information.characterInfo;
275     return 0;
276 }
277
278 const char *Yaz_Proxy::load_balance(const char **url)
279 {
280     int zurl_in_use[MAX_ZURL_PLEX];
281     int zurl_in_spare[MAX_ZURL_PLEX];
282     Yaz_ProxyClient *c;
283     int i;
284
285     for (i = 0; i<MAX_ZURL_PLEX; i++)
286     {
287         zurl_in_use[i] = 0;
288         zurl_in_spare[i] = 0;
289     }
290     for (c = m_parent->m_clientPool; c; c = c->m_next)
291     {
292         for (i = 0; url[i]; i++)
293             if (!strcmp(url[i], c->get_hostname()))
294             {
295                 zurl_in_use[i]++;
296                 if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
297                     zurl_in_spare[i]++;
298             }
299     }
300     int min_use = 100000;
301     int spare_for_min = 0;
302     int max_spare = 0;
303     const char *ret_min = 0;
304     const char *ret_spare = 0;
305     for (i = 0; url[i]; i++)
306     {
307         yaz_log(LOG_DEBUG, "%szurl=%s use=%d spare=%d",
308                 m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
309         if (min_use > zurl_in_use[i])
310         {
311             ret_min = url[i];
312             min_use = zurl_in_use[i];
313             spare_for_min = zurl_in_spare[i];
314         }
315         if (max_spare < zurl_in_spare[i]) 
316         {
317             ret_spare = url[i];
318             max_spare = zurl_in_spare[i];
319         }
320     }
321     // use the one with minimum connections if spare is > 3
322     if (spare_for_min > 3)
323         return ret_min;
324     // use one with most spares (if any)
325     if (max_spare > 0)
326         return ret_spare;
327     return ret_min;
328 }
329
330 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
331                                        const char *proxy_host)
332 {
333     assert (m_parent);
334     Yaz_Proxy *parent = m_parent;
335     Yaz_ProxyClient *c = m_client;
336     
337     if (!m_proxyTarget)
338     {
339         const char *url[MAX_ZURL_PLEX];
340         Yaz_ProxyConfig *cfg = check_reconfigure();
341         if (proxy_host)
342         {
343 #if 1
344 /* only to be enabled for debugging... */
345             if (!strcmp(proxy_host, "stop"))
346                 exit(0);
347 #endif
348             xfree(m_default_target);
349             m_default_target = xstrdup(proxy_host);
350             proxy_host = m_default_target;
351         }
352         int client_idletime = -1;
353         const char *cql2rpn_fname = 0;
354         url[0] = m_default_target;
355         url[1] = 0;
356         if (cfg)
357         {
358             int pre_init = 0;
359             cfg->get_target_info(proxy_host, url, &m_bw_max,
360                                  &m_pdu_max, &m_max_record_retrieve,
361                                  &m_target_idletime, &client_idletime,
362                                  &parent->m_max_clients,
363                                  &m_keepalive_limit_bw,
364                                  &m_keepalive_limit_pdu,
365                                  &pre_init,
366                                  &cql2rpn_fname);
367         }
368         if (client_idletime != -1)
369         {
370             m_client_idletime = client_idletime;
371             timeout(m_client_idletime);
372         }
373         if (cql2rpn_fname)
374             m_cql2rpn.set_pqf_file(cql2rpn_fname);
375         if (!url[0])
376         {
377             yaz_log(LOG_LOG, "%sNo default target", m_session_str);
378             return 0;
379         }
380         // we don't handle multiplexing for cookie session, so we just
381         // pick the first one in this case (anonymous users will be able
382         // to use any backend)
383         if (cookie && *cookie)
384             m_proxyTarget = (char*) xstrdup(url[0]);
385         else
386             m_proxyTarget = (char*) xstrdup(load_balance(url));
387     }
388     if (cookie && *cookie)
389     {   // search in sessions with a cookie
390         for (c = parent->m_clientPool; c; c = c->m_next)
391         {
392             assert (c->m_prev);
393             assert (*c->m_prev == c);
394             if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
395                 !strcmp(m_proxyTarget, c->get_hostname()))
396             {
397                 // Found it in cache
398                 // The following handles "cancel"
399                 // If connection is busy (waiting for PDU) and
400                 // we have an initRequest we can safely do re-open
401                 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
402                 {
403                     yaz_log (LOG_LOG, "%s REOPEN target=%s", m_session_str,
404                              c->get_hostname());
405                     c->close();
406                     c->m_init_flag = 0;
407                     
408                     c->m_last_ok = 0;
409                     c->m_cache.clear();
410                     c->m_last_resultCount = 0;
411                     c->m_sr_transform = 0;
412                     c->m_waiting = 0;
413                     c->m_resultSetStartPoint = 0;
414                     c->m_target_idletime = m_target_idletime;
415                     if (c->client(m_proxyTarget))
416                     {
417                         delete c;
418                         return 0;
419                     }
420                     c->timeout(30); 
421                 }
422                 c->m_seqno = parent->m_seqno;
423                 if (c->m_server && c->m_server != this)
424                     c->m_server->m_client = 0;
425                 c->m_server = this;
426                 (parent->m_seqno)++;
427                 yaz_log (LOG_DEBUG, "get_client 1 %p %p", this, c);
428                 return c;
429             }
430         }
431     }
432     else if (!c)
433     {
434         // don't have a client session yet. Search in session w/o cookie
435         for (c = parent->m_clientPool; c; c = c->m_next)
436         {
437             assert (c->m_prev);
438             assert (*c->m_prev == c);
439             if (c->m_server == 0 && c->m_cookie == 0 && 
440                 c->m_waiting == 0 &&
441                 !strcmp(m_proxyTarget, c->get_hostname()))
442             {
443                 // found it in cache
444                 yaz_log (LOG_LOG, "%sREUSE %d %s",
445                          m_session_str, parent->m_seqno, c->get_hostname());
446                 
447                 c->m_seqno = parent->m_seqno;
448                 assert(c->m_server == 0);
449                 c->m_server = this;
450
451                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
452                     c->set_APDU_yazlog(1);
453                 else
454                     c->set_APDU_yazlog(0);
455
456                 (parent->m_seqno)++;
457                 
458                 parent->pre_init();
459                 
460                 return c;
461             }
462         }
463     }
464     if (!m_client)
465     {
466         if (apdu->which != Z_APDU_initRequest)
467         {
468             yaz_log (LOG_LOG, "%sno init request as first PDU", m_session_str);
469             return 0;
470         }
471         Z_InitRequest *initRequest = apdu->u.initRequest;
472
473         if (!initRequest->idAuthentication)
474         {
475             if (m_proxy_authentication)
476             {
477                 initRequest->idAuthentication =
478                     (Z_IdAuthentication *)
479                     odr_malloc (odr_encode(),
480                                 sizeof(*initRequest->idAuthentication));
481                 initRequest->idAuthentication->which =
482                     Z_IdAuthentication_open;
483                 initRequest->idAuthentication->u.open =
484                     odr_strdup (odr_encode(), m_proxy_authentication);
485             }
486         }
487         // go through list of clients - and find the lowest/oldest one.
488         Yaz_ProxyClient *c_min = 0;
489         int min_seq = -1;
490         int no_of_clients = 0;
491         if (parent->m_clientPool)
492             yaz_log (LOG_DEBUG, "Existing sessions");
493         for (c = parent->m_clientPool; c; c = c->m_next)
494         {
495             yaz_log (LOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
496                                c->m_waiting, c->get_hostname(),
497                                c->m_cookie ? c->m_cookie : "");
498             no_of_clients++;
499             if (min_seq < 0 || c->m_seqno < min_seq)
500             {
501                 min_seq = c->m_seqno;
502                 c_min = c;
503             }
504         }
505         if (no_of_clients >= parent->m_max_clients)
506         {
507             c = c_min;
508             if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
509             {
510                 yaz_log (LOG_LOG, "%sMAXCLIENTS %d Destroy %d",
511                          m_session_str, parent->m_max_clients, c->m_seqno);
512                 if (c->m_server && c->m_server != this)
513                     delete c->m_server;
514                 c->m_server = 0;
515             }
516             else
517             {
518                 yaz_log (LOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
519                          m_session_str, parent->m_max_clients,
520                          c->m_seqno, parent->m_seqno, c->get_hostname());
521                 xfree (c->m_cookie);
522                 c->m_cookie = 0;
523                 if (cookie)
524                     c->m_cookie = xstrdup(cookie);
525                 c->m_seqno = parent->m_seqno;
526                 if (c->m_server && c->m_server != this)
527                 {
528                     c->m_server->m_client = 0;
529                     delete c->m_server;
530                 }
531                 (parent->m_seqno)++;
532                 c->m_target_idletime = m_target_idletime;
533                 c->timeout(m_target_idletime);
534                 
535                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
536                     c->set_APDU_yazlog(1);
537                 else
538                     c->set_APDU_yazlog(0);
539
540                 return c;
541             }
542         }
543         else
544         {
545             yaz_log (LOG_LOG, "%sNEW %d %s",
546                      m_session_str, parent->m_seqno, m_proxyTarget);
547             c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
548             c->m_next = parent->m_clientPool;
549             if (c->m_next)
550                 c->m_next->m_prev = &c->m_next;
551             parent->m_clientPool = c;
552             c->m_prev = &parent->m_clientPool;
553         }
554
555         xfree (c->m_cookie);
556         c->m_cookie = 0;
557         if (cookie)
558             c->m_cookie = xstrdup(cookie);
559
560         c->m_seqno = parent->m_seqno;
561         c->m_init_flag = 0;
562         c->m_last_resultCount = 0;
563         c->m_last_ok = 0;
564         c->m_cache.clear();
565         c->m_sr_transform = 0;
566         c->m_waiting = 0;
567         c->m_resultSetStartPoint = 0;
568         (parent->m_seqno)++;
569         if (c->client(m_proxyTarget))
570         {
571             delete c;
572             return 0;
573         }
574         c->m_target_idletime = m_target_idletime;
575         c->timeout(30);
576
577         if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
578             c->set_APDU_yazlog(1);
579         else
580             c->set_APDU_yazlog(0);
581     }
582     yaz_log (LOG_DEBUG, "get_client 3 %p %p", this, c);
583     return c;
584 }
585
586 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
587 {
588     int i;
589     for (i = 0; i<num; i++)
590     {
591         oident *ent;
592         Z_DefaultDiagFormat *r;
593         Z_DiagRec *p = pp[i];
594         if (p->which != Z_DiagRec_defaultFormat)
595         {
596             yaz_log(LOG_LOG, "%sError no diagnostics", m_session_str);
597             return;
598         }
599         else
600             r = p->u.defaultFormat;
601         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
602             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
603             yaz_log(LOG_LOG, "%sError unknown diagnostic set", m_session_str);
604         switch (r->which)
605         {
606         case Z_DefaultDiagFormat_v2Addinfo:
607             yaz_log(LOG_LOG, "%sError %d %s:%s",
608                     m_session_str,
609                     *r->condition, diagbib1_str(*r->condition),
610                     r->u.v2Addinfo);
611             break;
612         case Z_DefaultDiagFormat_v3Addinfo:
613             yaz_log(LOG_LOG, "%sError %d %s:%s",
614                     m_session_str,
615                     *r->condition, diagbib1_str(*r->condition),
616                     r->u.v3Addinfo);
617             break;
618         }
619     }
620 }
621
622 int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
623 {
624     if (!m_stylesheet_xsp || p->num_records <= 0)
625         return 0;  /* no XSLT to be done ... */
626
627     m_stylesheet_offset = 0;
628     m_stylesheet_nprl = p;
629     m_stylesheet_apdu = apdu;
630     timeout(0);
631     return 1;
632 }
633
634 void Yaz_Proxy::convert_xsl_delay()
635 {
636     Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
637     if (npr->which == Z_NamePlusRecord_databaseRecord)
638     {
639         Z_External *r = npr->u.databaseRecord;
640         if (r->which == Z_External_octet)
641         {
642             fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
643             xmlDocPtr res, doc = xmlParseMemory(
644                 (char*) r->u.octet_aligned->buf,
645                 r->u.octet_aligned->len);
646
647             
648             yaz_log(LOG_LOG, "%sXSLT convert %d",
649                     m_session_str, m_stylesheet_offset);
650             res = xsltApplyStylesheet(m_stylesheet_xsp, doc, 0);
651
652             if (res)
653             {
654                 xmlChar *out_buf;
655                 int out_len;
656                 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
657                 
658                 m_stylesheet_nprl->records[m_stylesheet_offset]->
659                     u.databaseRecord = 
660                     z_ext_record(odr_encode(), VAL_TEXT_XML,
661                                  (char*) out_buf, out_len);
662                 xmlFree(out_buf);
663                 xmlFreeDoc(res);
664             }
665
666             xmlFreeDoc(doc);
667         }
668     }
669     m_stylesheet_offset++;
670     if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
671     {
672         m_stylesheet_nprl = 0;
673         if (m_stylesheet_xsp)
674             xsltFreeStylesheet(m_stylesheet_xsp);
675         m_stylesheet_xsp = 0;
676         timeout(m_client_idletime);
677         send_PDU_convert(m_stylesheet_apdu);
678     }
679     else
680         timeout(0);
681 }
682
683 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
684 {
685     int i;
686
687     yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8");
688     yaz_marc_t mt = yaz_marc_create();
689     yaz_marc_xml(mt, YAZ_MARC_MARCXML);
690     yaz_marc_iconv(mt, cd);
691     for (i = 0; i < p->num_records; i++)
692     {
693         Z_NamePlusRecord *npr = p->records[i];
694         if (npr->which == Z_NamePlusRecord_databaseRecord)
695         {
696             Z_External *r = npr->u.databaseRecord;
697             if (r->which == Z_External_octet)
698             {
699                 int rlen;
700                 char *result;
701                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
702                                         r->u.octet_aligned->len,
703                                         &result, &rlen))
704                 {
705                     npr->u.databaseRecord = z_ext_record(odr_encode(),
706                                                          VAL_TEXT_XML,
707                                                          result, rlen);
708                 }
709             }
710         }
711     }
712     if (cd)
713         yaz_iconv_close(cd);
714     yaz_marc_destroy(mt);
715 }
716
717 void Yaz_Proxy::logtime()
718 {
719     if (m_time_tv.tv_sec)
720     {
721         struct timeval tv;
722         gettimeofday(&tv, 0);
723         long diff = (tv.tv_sec - m_time_tv.tv_sec)*1000000 +
724             (tv.tv_usec - m_time_tv.tv_usec);
725         if (diff >= 0)
726             yaz_log(LOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
727                     diff/1000000, (diff/1000)%1000);
728     }
729     m_time_tv.tv_sec = 0;
730     m_time_tv.tv_usec = 0;
731 }
732
733 int Yaz_Proxy::send_http_response(int code)
734 {
735     ODR o = odr_encode();
736     Z_GDU *gdu = z_get_HTTP_Response(o, code);
737     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
738     if (m_http_version)
739         hres->version = odr_strdup(o, m_http_version);
740     if (m_http_keepalive)
741         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
742     else
743         timeout(0);
744     
745     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
746     {
747         yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
748                  gdu_name(gdu));
749     }
750     int len;
751     int r = send_GDU(gdu, &len);
752     m_bytes_sent += len;
753     m_bw_stat.add_bytes(len);
754     logtime();
755     return r;
756 }
757
758 int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
759 {
760     ODR o = odr_encode();
761     const char *ctype = "text/xml";
762     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
763     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
764     if (m_http_version)
765         hres->version = odr_strdup(o, m_http_version);
766     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
767     if (m_http_keepalive)
768         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
769     else
770         timeout(0);
771
772     static Z_SOAP_Handler soap_handlers[2] = {
773 #if HAVE_XSLT
774         {"http://www.loc.gov/zing/srw/", 0,
775          (Z_SOAP_fun) yaz_srw_codec},
776 #endif
777         {0, 0, 0}
778     };
779     
780     Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
781     soap_package->which = Z_SOAP_generic;
782     soap_package->u.generic = 
783         (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
784     soap_package->u.generic->no = 0;
785     soap_package->u.generic->ns = soap_handlers[0].ns;
786     soap_package->u.generic->p = (void *) srw_pdu;
787     soap_package->ns = m_soap_ns;
788     z_soap_codec_enc_xsl(o, &soap_package,
789                          &hres->content_buf, &hres->content_len,
790                          soap_handlers, 0, m_s2z_stylesheet);
791     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
792     {
793         yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
794                  gdu_name(gdu));
795     }
796     int len;
797     int r = send_GDU(gdu, &len);
798     m_bytes_sent += len;
799     m_bw_stat.add_bytes(len);
800     logtime();
801     return r;
802 }
803
804 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
805 {
806     ODR o = odr_encode();
807     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
808     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
809
810     srw_res->num_diagnostics = 1;
811     srw_res->diagnostics = (Z_SRW_diagnostic *)
812         odr_malloc(o, sizeof(*srw_res->diagnostics));
813     yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
814     return send_srw_response(srw_pdu);
815 }
816
817 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
818                              Z_DefaultDiagFormat *ddf)
819 {
820     int bib1_code = *ddf->condition;
821     if (bib1_code == 109)
822         return 404;
823     srw_res->num_diagnostics = 1;
824     srw_res->diagnostics = (Z_SRW_diagnostic *)
825         odr_malloc(o, sizeof(*srw_res->diagnostics));
826     yaz_mk_std_diagnostic(o, srw_res->diagnostics,
827                           yaz_diag_bib1_to_srw(*ddf->condition), 
828                           ddf->u.v2Addinfo);
829     return 0;
830 }
831
832 int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
833 {
834     ODR o = odr_encode();
835     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
836     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
837
838     srw_res->numberOfRecords = odr_intdup (o, hits);
839     if (records && records->which == Z_Records_DBOSD)
840     {
841         srw_res->num_records =
842             records->u.databaseOrSurDiagnostics->num_records;
843         int i;
844         srw_res->records = (Z_SRW_record *)
845             odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
846         for (i = 0; i < srw_res->num_records; i++)
847         {
848             Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
849             if (npr->which != Z_NamePlusRecord_databaseRecord)
850             {
851                 srw_res->records[i].recordSchema = "diagnostic";
852                 srw_res->records[i].recordPacking = m_s2z_packing;
853                 srw_res->records[i].recordData_buf = "67";
854                 srw_res->records[i].recordData_len = 2;
855                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
856                 continue;
857             }
858             Z_External *r = npr->u.databaseRecord;
859             oident *ent = oid_getentbyoid(r->direct_reference);
860             if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
861             {
862                 srw_res->records[i].recordSchema = m_schema;
863                 srw_res->records[i].recordPacking = m_s2z_packing;
864                 srw_res->records[i].recordData_buf = (char*) 
865                     r->u.octet_aligned->buf;
866                 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
867                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
868             }
869             else
870             {
871                 srw_res->records[i].recordSchema = "diagnostic";
872                 srw_res->records[i].recordPacking = m_s2z_packing;
873                 srw_res->records[i].recordData_buf = "67";
874                 srw_res->records[i].recordData_len = 2;
875                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
876             }
877         }
878     }
879     if (records && records->which == Z_Records_NSD)
880     {
881         int http_code;
882         http_code = z_to_srw_diag(odr_encode(), srw_res,
883                                    records->u.nonSurrogateDiagnostic);
884         if (http_code)
885             return send_http_response(http_code);
886     }
887     return send_srw_response(srw_pdu);
888     
889 }
890
891 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
892                                         int num_diagnostics)
893 {
894     Yaz_ProxyConfig *cfg = check_reconfigure();
895     if (cfg)
896     {
897         int len;
898         char *b = cfg->get_explain(odr_encode(), 0 /* target */,
899                                    m_s2z_database, &len);
900         if (b)
901         {
902             Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
903             Z_SRW_explainResponse *er = res->u.explain_response;
904
905             er->record.recordData_buf = b;
906             er->record.recordData_len = len;
907             er->record.recordPacking = m_s2z_packing;
908             er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
909
910             er->diagnostics = diagnostics;
911             er->num_diagnostics = num_diagnostics;
912             return send_srw_response(res);
913         }
914     }
915     return send_http_response(404);
916 }
917
918 int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
919 {
920     if (m_http_version)
921     {
922         if (apdu->which == Z_APDU_initResponse)
923         {
924             Z_InitResponse *res = apdu->u.initResponse;
925             if (*res->result == 0)
926             {
927                 send_to_srw_client_error(3, 0);
928             }
929             else if (!m_s2z_search_apdu)
930             {
931                 send_srw_explain_response(0, 0);
932             }
933             else
934             {
935                 handle_incoming_Z_PDU(m_s2z_search_apdu);
936             }
937         }
938         else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
939         {
940             m_s2z_search_apdu = 0;
941             Z_SearchResponse *res = apdu->u.searchResponse;
942             m_s2z_hit_count = *res->resultCount;
943             if (res->records && res->records->which == Z_Records_NSD)
944             {
945                 send_to_srw_client_ok(0, res->records, 1);
946             }
947             else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
948             {
949                 // adjust 
950                 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
951                 
952                 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
953                 {
954                     if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
955                         > m_s2z_hit_count)
956                         *pr->numberOfRecordsRequested =
957                             1 + m_s2z_hit_count - *pr->resultSetStartPoint;
958                 }
959                 handle_incoming_Z_PDU(m_s2z_present_apdu);
960             }
961             else
962             {
963                 m_s2z_present_apdu = 0;
964                 send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
965             }
966         }
967         else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
968         {
969             int start = 
970                 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
971
972             m_s2z_present_apdu = 0;
973             Z_PresentResponse *res = apdu->u.presentResponse;
974             send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
975         }
976     }
977     else
978     {
979         int len = 0;
980         if (m_log_mask & PROXY_LOG_REQ_CLIENT)
981             yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
982                      apdu_name(apdu));
983         int r = send_Z_PDU(apdu, &len);
984         m_bytes_sent += len;
985         m_bw_stat.add_bytes(len);
986         logtime();
987         return r;
988     }
989     return 0;
990 }
991
992 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
993 {
994     int kill_session = 0;
995     Z_ReferenceId **new_id = get_referenceIdP(apdu);
996
997     if (new_id)
998         *new_id = m_referenceId;
999     
1000     if (apdu->which == Z_APDU_searchResponse)
1001     {
1002         Z_SearchResponse *sr = apdu->u.searchResponse;
1003         Z_Records *p = sr->records;
1004         if (p && p->which == Z_Records_NSD)
1005         {
1006             Z_DiagRec dr, *dr_p = &dr;
1007             dr.which = Z_DiagRec_defaultFormat;
1008             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1009
1010             *sr->searchStatus = 0;
1011             display_diagrecs(&dr_p, 1);
1012         }
1013         else
1014         {
1015             if (p && p->which == Z_Records_DBOSD)
1016             {
1017                 if (m_marcxml_flag)
1018                     convert_to_marcxml(p->u.databaseOrSurDiagnostics);
1019                 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1020                     return 0;
1021                     
1022             }
1023             if (sr->resultCount)
1024             {
1025                 yaz_log(LOG_LOG, "%s%d hits", m_session_str,
1026                         *sr->resultCount);
1027                 if (*sr->resultCount < 0)
1028                 {
1029                     m_invalid_session = 1;
1030                     kill_session = 1;
1031
1032                     *sr->searchStatus = 0;
1033                     sr->records =
1034                         create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
1035                     *sr->resultCount = 0;
1036                 }
1037             }
1038         }
1039     }
1040     else if (apdu->which == Z_APDU_presentResponse)
1041     {
1042         Z_PresentResponse *sr = apdu->u.presentResponse;
1043         Z_Records *p = sr->records;
1044         if (p && p->which == Z_Records_NSD)
1045         {
1046             Z_DiagRec dr, *dr_p = &dr;
1047             dr.which = Z_DiagRec_defaultFormat;
1048             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1049             if (*sr->presentStatus == Z_PresentStatus_success)
1050                 *sr->presentStatus = Z_PresentStatus_failure;
1051             display_diagrecs(&dr_p, 1);
1052         }
1053         if (p && p->which == Z_Records_DBOSD)
1054         {
1055             if (m_marcxml_flag)
1056                 convert_to_marcxml(p->u.databaseOrSurDiagnostics);
1057             if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1058                 return 0;
1059         }
1060     }
1061     else if (apdu->which == Z_APDU_initResponse)
1062     {
1063         if (m_initRequest_options)
1064         {
1065             Z_Options *nopt = 
1066                 (Odr_bitmask *)odr_malloc(odr_encode(),
1067                                           sizeof(Odr_bitmask));
1068             ODR_MASK_ZERO(nopt);
1069
1070             int i;
1071             for (i = 0; i<24; i++)
1072                 if (ODR_MASK_GET(m_initRequest_options, i) &&
1073                     ODR_MASK_GET(apdu->u.initResponse->options, i))
1074                     ODR_MASK_SET(nopt, i);
1075             apdu->u.initResponse->options = nopt;           
1076         }
1077         if (m_initRequest_version)
1078         {
1079             Z_ProtocolVersion *nopt = 
1080                 (Odr_bitmask *)odr_malloc(odr_encode(),
1081                                           sizeof(Odr_bitmask));
1082             ODR_MASK_ZERO(nopt);
1083
1084             int i;
1085             for (i = 0; i<8; i++)
1086                 if (ODR_MASK_GET(m_initRequest_version, i) &&
1087                     ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
1088                     ODR_MASK_SET(nopt, i);
1089             apdu->u.initResponse->protocolVersion = nopt;           
1090         }
1091         apdu->u.initResponse->preferredMessageSize =
1092             odr_intdup(odr_encode(),
1093                        m_client->m_initResponse_preferredMessageSize >
1094                        m_initRequest_preferredMessageSize ?
1095                        m_initRequest_preferredMessageSize :
1096                        m_client->m_initResponse_preferredMessageSize);
1097         apdu->u.initResponse->maximumRecordSize =
1098             odr_intdup(odr_encode(),
1099                        m_client->m_initResponse_maximumRecordSize >
1100                        m_initRequest_maximumRecordSize ?
1101                        m_initRequest_maximumRecordSize :
1102                        m_client->m_initResponse_maximumRecordSize);
1103     }
1104     int r = send_PDU_convert(apdu);
1105     if (r)
1106         return r;
1107     if (kill_session)
1108     {
1109         delete m_client;
1110         m_client = 0;
1111         m_parent->pre_init();
1112     }
1113     return r;
1114 }
1115
1116 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
1117 {
1118     int len = 0;
1119     const char *apdu_name_tmp = apdu_name(apdu);
1120     int r = send_Z_PDU(apdu, &len);
1121     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
1122         yaz_log (LOG_LOG, "%sSending %s to %s %d bytes",
1123                  get_session_str(),
1124                  apdu_name_tmp, get_hostname(), len);
1125     m_bytes_sent += len;
1126     return r;
1127 }
1128
1129 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
1130 {
1131     if (apdu->which == Z_APDU_presentRequest)
1132     {
1133         Z_PresentRequest *pr = apdu->u.presentRequest;
1134         int toget = *pr->numberOfRecordsRequested;
1135         int start = *pr->resultSetStartPoint;
1136
1137         yaz_log(LOG_LOG, "%sPresent %s %d+%d", m_session_str,
1138                 pr->resultSetId, start, toget);
1139
1140         if (*m_parent->m_optimize == '0')
1141             return apdu;
1142
1143         if (!m_client->m_last_resultSetId)
1144         {
1145             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1146             new_apdu->u.presentResponse->records =
1147                 create_nonSurrogateDiagnostics(odr_encode(), 30,
1148                                                pr->resultSetId);
1149             send_to_client(new_apdu);
1150             return 0;
1151         }
1152         if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
1153         {
1154             if (start+toget-1 > m_client->m_last_resultCount)
1155             {
1156                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1157                 new_apdu->u.presentResponse->records =
1158                     create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
1159                 send_to_client(new_apdu);
1160                 return 0;
1161             }
1162             Z_NamePlusRecordList *npr;
1163             if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
1164                                           pr->preferredRecordSyntax,
1165                                           pr->recordComposition))
1166             {
1167                 yaz_log (LOG_LOG, "%sReturned cached records for present request", 
1168                          m_session_str);
1169                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1170                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
1171                 
1172                 new_apdu->u.presentResponse->numberOfRecordsReturned
1173                     = odr_intdup(odr_encode(), toget);
1174                                                                  
1175                 new_apdu->u.presentResponse->records = (Z_Records*)
1176                     odr_malloc(odr_encode(), sizeof(Z_Records));
1177                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
1178                 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
1179                 new_apdu->u.presentResponse->nextResultSetPosition =
1180                     odr_intdup(odr_encode(), start+toget);
1181
1182                 send_to_client(new_apdu);
1183                 return 0;
1184             }
1185         }
1186     }
1187
1188     if (apdu->which != Z_APDU_searchRequest)
1189         return apdu;
1190     Z_SearchRequest *sr = apdu->u.searchRequest;
1191     Yaz_Z_Query *this_query = new Yaz_Z_Query;
1192     Yaz_Z_Databases this_databases;
1193
1194     this_databases.set(sr->num_databaseNames, (const char **)
1195                        sr->databaseNames);
1196     
1197     this_query->set_Z_Query(sr->query);
1198
1199     char query_str[120];
1200     this_query->print(query_str, sizeof(query_str)-1);
1201     yaz_log(LOG_LOG, "%sSearch %s", m_session_str, query_str);
1202
1203     if (*m_parent->m_optimize != '0' &&
1204         m_client->m_last_ok && m_client->m_last_query &&
1205         m_client->m_last_query->match(this_query) &&
1206         !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
1207         m_client->m_last_databases.match(this_databases))
1208     {
1209         delete this_query;
1210         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
1211             m_client->m_last_resultCount < *sr->largeSetLowerBound)
1212         {
1213             Z_NamePlusRecordList *npr;
1214             int toget = *sr->mediumSetPresentNumber;
1215             Z_RecordComposition *comp = 0;
1216
1217             if (toget > m_client->m_last_resultCount)
1218                 toget = m_client->m_last_resultCount;
1219             
1220             if (sr->mediumSetElementSetNames)
1221             {
1222                 comp = (Z_RecordComposition *)
1223                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1224                 comp->which = Z_RecordComp_simple;
1225                 comp->u.simple = sr->mediumSetElementSetNames;
1226             }
1227  
1228             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1229                                           sr->preferredRecordSyntax, comp))
1230             {
1231                 yaz_log (LOG_LOG, "%sReturned cached records for medium set",
1232                          m_session_str);
1233                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1234                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1235                 new_apdu->u.searchResponse->resultCount =
1236                     &m_client->m_last_resultCount;
1237                 
1238                 new_apdu->u.searchResponse->numberOfRecordsReturned
1239                     = odr_intdup(odr_encode(), toget);
1240                                                         
1241                 new_apdu->u.searchResponse->presentStatus =
1242                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1243                 new_apdu->u.searchResponse->records = (Z_Records*)
1244                     odr_malloc(odr_encode(), sizeof(Z_Records));
1245                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1246                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1247                 new_apdu->u.searchResponse->nextResultSetPosition =
1248                     odr_intdup(odr_encode(), toget+1);
1249                 send_to_client(new_apdu);
1250                 return 0;
1251             }
1252             else
1253             {
1254                 // medium Set
1255                 // send present request (medium size)
1256                 yaz_log (LOG_LOG, "%sOptimizing search for medium set",
1257                          m_session_str);
1258
1259                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1260                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1261                 pr->referenceId = sr->referenceId;
1262                 pr->resultSetId = sr->resultSetName;
1263                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1264                 *pr->numberOfRecordsRequested = toget;
1265                 pr->recordComposition = comp;
1266                 m_client->m_sr_transform = 1;
1267                 return new_apdu;
1268             }
1269         }
1270         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
1271             m_client->m_last_resultCount <= 0)
1272         {
1273             // large set. Return pseudo-search response immediately
1274             yaz_log (LOG_LOG, "%sOptimizing search for large set",
1275                      m_session_str);
1276             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1277             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1278             new_apdu->u.searchResponse->resultCount =
1279                 &m_client->m_last_resultCount;
1280             send_to_client(new_apdu);
1281             return 0;
1282         }
1283         else
1284         {
1285             Z_NamePlusRecordList *npr;
1286             int toget = m_client->m_last_resultCount;
1287             Z_RecordComposition *comp = 0;
1288             // small set
1289             // send a present request (small set)
1290             
1291             if (sr->smallSetElementSetNames)
1292             {
1293                 comp = (Z_RecordComposition *)
1294                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1295                 comp->which = Z_RecordComp_simple;
1296                 comp->u.simple = sr->smallSetElementSetNames;
1297             }
1298
1299             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1300                                           sr->preferredRecordSyntax, comp))
1301             {
1302                 yaz_log (LOG_LOG, "%sReturned cached records for small set",
1303                          m_session_str);
1304                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1305                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1306                 new_apdu->u.searchResponse->resultCount =
1307                     &m_client->m_last_resultCount;
1308                 
1309                 new_apdu->u.searchResponse->numberOfRecordsReturned
1310                     = odr_intdup(odr_encode(), toget);
1311                                                                  
1312                 new_apdu->u.searchResponse->presentStatus =
1313                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1314                 new_apdu->u.searchResponse->records = (Z_Records*)
1315                     odr_malloc(odr_encode(), sizeof(Z_Records));
1316                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1317                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1318                 new_apdu->u.searchResponse->nextResultSetPosition =
1319                     odr_intdup(odr_encode(), toget+1);
1320                 send_to_client(new_apdu);
1321                 return 0;
1322             }
1323             else
1324             {
1325                 yaz_log (LOG_LOG, "%sOptimizing search for small set",
1326                          m_session_str);
1327                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1328                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1329                 pr->referenceId = sr->referenceId;
1330                 pr->resultSetId = sr->resultSetName;
1331                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1332                 *pr->numberOfRecordsRequested = toget;
1333                 pr->recordComposition = comp;
1334                 m_client->m_sr_transform = 1;
1335                 return new_apdu;
1336             }
1337         }
1338     }
1339     else  // query doesn't match
1340     {
1341         delete m_client->m_last_query;
1342         m_client->m_last_query = this_query;
1343         m_client->m_last_ok = 0;
1344         m_client->m_cache.clear();
1345         m_client->m_resultSetStartPoint = 0;
1346
1347         xfree (m_client->m_last_resultSetId);
1348         m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
1349
1350         m_client->m_last_databases.set(sr->num_databaseNames,
1351                                        (const char **) sr->databaseNames);
1352     }
1353     return apdu;
1354 }
1355
1356
1357 void Yaz_Proxy::inc_request_no()
1358 {
1359     char *cp = strchr(m_session_str, ' ');
1360     m_request_no++;
1361     if (cp)
1362         sprintf(cp+1, "%d ", m_request_no);
1363 }
1364
1365 void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
1366 {
1367     inc_request_no();
1368
1369     m_bytes_recv += len;
1370     
1371     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
1372         yaz_log (LOG_DEBUG, "%sReceiving %s from client %d bytes",
1373                  m_session_str, gdu_name(apdu), len);
1374
1375     if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
1376         shutdown();
1377
1378     m_bw_stat.add_bytes(len);
1379     m_pdu_stat.add_bytes(1);
1380
1381     gettimeofday(&m_time_tv, 0);
1382
1383     int bw_total = m_bw_stat.get_total();
1384     int pdu_total = m_pdu_stat.get_total();
1385
1386     int reduce = 0;
1387     if (m_bw_max)
1388     {
1389         if (bw_total > m_bw_max)
1390         {
1391             reduce = (bw_total/m_bw_max);
1392         }
1393     }
1394     if (m_pdu_max)
1395     {
1396         if (pdu_total > m_pdu_max)
1397         {
1398             int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
1399             reduce = (reduce > nreduce) ? reduce : nreduce;
1400         }
1401     }
1402     if (reduce)  
1403     {
1404         yaz_log(LOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1405                 m_session_str, reduce, bw_total, pdu_total,
1406                 m_bw_max, m_pdu_max);
1407         
1408         m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
1409         timeout(reduce);       // call us reduce seconds later
1410     }
1411     else if (apdu->which == Z_GDU_Z3950)
1412         handle_incoming_Z_PDU(apdu->u.z3950);
1413     else if (apdu->which == Z_GDU_HTTP_Request)
1414         handle_incoming_HTTP(apdu->u.HTTP_Request);
1415 }
1416
1417 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
1418 {
1419     if (m_max_record_retrieve)
1420     {
1421         if (apdu->which == Z_APDU_presentRequest)
1422         {
1423             Z_PresentRequest *pr = apdu->u.presentRequest;
1424             if (pr->numberOfRecordsRequested && 
1425                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
1426                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
1427         }
1428     }
1429 }
1430
1431 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
1432                                                      int error,
1433                                                      const char *addinfo)
1434 {
1435     Z_Records *rec = (Z_Records *)
1436         odr_malloc (odr, sizeof(*rec));
1437     int *err = (int *)
1438         odr_malloc (odr, sizeof(*err));
1439     Z_DiagRec *drec = (Z_DiagRec *)
1440         odr_malloc (odr, sizeof(*drec));
1441     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1442         odr_malloc (odr, sizeof(*dr));
1443     *err = error;
1444     rec->which = Z_Records_NSD;
1445     rec->u.nonSurrogateDiagnostic = dr;
1446     dr->diagnosticSetId =
1447         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1448     dr->condition = err;
1449     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1450     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1451     return rec;
1452 }
1453
1454 Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
1455 {
1456     if (apdu->which == Z_APDU_searchRequest &&
1457         apdu->u.searchRequest->query &&
1458         apdu->u.searchRequest->query->which == Z_Query_type_104 &&
1459         apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
1460     {
1461         Z_RPNQuery *rpnquery = 0;
1462         Z_SearchRequest *sr = apdu->u.searchRequest;
1463         char *addinfo = 0;
1464         
1465         yaz_log(LOG_LOG, "%sCQL: %s", m_session_str,
1466                 sr->query->u.type_104->u.cql);
1467
1468         int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
1469                                           &rpnquery, odr_encode(),
1470                                           &addinfo);
1471         if (r == -3)
1472             yaz_log(LOG_LOG, "%sNo CQL to RPN table", m_session_str);
1473         else if (r)
1474         {
1475             yaz_log(LOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
1476             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1477
1478             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1479             new_apdu->u.searchResponse->records =
1480                 create_nonSurrogateDiagnostics(odr_encode(),
1481                                                yaz_diag_srw_to_bib1(r),
1482                                                addinfo);
1483             *new_apdu->u.searchResponse->searchStatus = 0;
1484
1485             send_to_client(new_apdu);
1486
1487             return 0;
1488         }
1489         else
1490         {
1491             sr->query->which = Z_Query_type_1;
1492             sr->query->u.type_1 = rpnquery;
1493         }
1494         return apdu;
1495     }
1496     return apdu;
1497 }
1498
1499 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
1500 {
1501     if (apdu->which == Z_APDU_searchRequest)
1502     {
1503         Z_SearchRequest *sr = apdu->u.searchRequest;
1504         int err = 0;
1505         char *addinfo = 0;
1506
1507         Yaz_ProxyConfig *cfg = check_reconfigure();
1508         if (cfg)
1509             err = cfg->check_query(odr_encode(), m_default_target,
1510                                    sr->query, &addinfo);
1511         if (err)
1512         {
1513             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1514
1515             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1516             new_apdu->u.searchResponse->records =
1517                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1518             *new_apdu->u.searchResponse->searchStatus = 0;
1519
1520             send_to_client(new_apdu);
1521
1522             return 0;
1523         }
1524     }
1525     return apdu;
1526 }
1527
1528 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
1529 {
1530     m_marcxml_flag = 0;
1531     if (apdu->which == Z_APDU_searchRequest)
1532     {
1533         Z_SearchRequest *sr = apdu->u.searchRequest;
1534         int err = 0;
1535         char *addinfo = 0;
1536         Yaz_ProxyConfig *cfg = check_reconfigure();
1537
1538         Z_RecordComposition rc_temp, *rc = 0;
1539         if (sr->smallSetElementSetNames)
1540         {
1541             rc_temp.which = Z_RecordComp_simple;
1542             rc_temp.u.simple = sr->smallSetElementSetNames;
1543             rc = &rc_temp;
1544         }
1545
1546         char *stylesheet_name = 0;
1547         if (cfg)
1548             err = cfg->check_syntax(odr_encode(),
1549                                     m_default_target,
1550                                     sr->preferredRecordSyntax, rc,
1551                                     &addinfo, &stylesheet_name, &m_schema);
1552         if (stylesheet_name)
1553         {
1554             m_parent->low_socket_close();
1555
1556             if (m_stylesheet_xsp)
1557                 xsltFreeStylesheet(m_stylesheet_xsp);
1558
1559             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1560                                                        stylesheet_name);
1561             m_stylesheet_offset = 0;
1562             xfree(stylesheet_name);
1563
1564             m_parent->low_socket_open();
1565         }
1566         if (err == -1)
1567         {
1568             sr->preferredRecordSyntax =
1569                 yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, VAL_USMARC);
1570             m_marcxml_flag = 1;
1571         }
1572         else if (err)
1573         {
1574             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1575             
1576             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1577             new_apdu->u.searchResponse->records =
1578                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1579             *new_apdu->u.searchResponse->searchStatus = 0;
1580             
1581             send_to_client(new_apdu);
1582             
1583             return 0;
1584         }
1585     }
1586     else if (apdu->which == Z_APDU_presentRequest)
1587     {
1588         Z_PresentRequest *pr = apdu->u.presentRequest;
1589         int err = 0;
1590         char *addinfo = 0;
1591         Yaz_ProxyConfig *cfg = check_reconfigure();
1592
1593         char *stylesheet_name = 0;
1594         if (cfg)
1595             err = cfg->check_syntax(odr_encode(), m_default_target,
1596                                     pr->preferredRecordSyntax,
1597                                     pr->recordComposition,
1598                                     &addinfo, &stylesheet_name, &m_schema);
1599         if (stylesheet_name)
1600         {
1601             m_parent->low_socket_close();
1602
1603             if (m_stylesheet_xsp)
1604                 xsltFreeStylesheet(m_stylesheet_xsp);
1605
1606             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1607                                                        stylesheet_name);
1608             m_stylesheet_offset = 0;
1609             xfree(stylesheet_name);
1610
1611             m_parent->low_socket_open();
1612         }
1613         if (err == -1)
1614         {
1615             pr->preferredRecordSyntax =
1616                 yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, VAL_USMARC);
1617             m_marcxml_flag = 1;
1618         }
1619         else if (err)
1620         {
1621             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1622             
1623             new_apdu->u.presentResponse->referenceId = pr->referenceId;
1624             new_apdu->u.presentResponse->records =
1625                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1626             *new_apdu->u.presentResponse->presentStatus =
1627                 Z_PresentStatus_failure;
1628             
1629             send_to_client(new_apdu);
1630             
1631             return 0;
1632         }
1633     }
1634     return apdu;
1635 }
1636
1637 Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
1638 {
1639     if (!schema)
1640         return 0;
1641     Z_ElementSetNames *esn = (Z_ElementSetNames *)
1642         odr_malloc(o, sizeof(Z_ElementSetNames));
1643     esn->which = Z_ElementSetNames_generic;
1644     esn->u.generic = odr_strdup(o, schema);
1645     return esn;
1646 }
1647
1648 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
1649 {
1650     if (m_s2z_odr_init)
1651     {
1652         odr_destroy(m_s2z_odr_init);
1653         m_s2z_odr_init = 0;
1654     }
1655     if (m_s2z_odr_search)
1656     {
1657         odr_destroy(m_s2z_odr_search);
1658         m_s2z_odr_search = 0;
1659     }
1660
1661     m_http_keepalive = 0;
1662     m_http_version = 0;
1663     if (!strcmp(hreq->version, "1.0")) 
1664     {
1665         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1666         if (v && !strcmp(v, "Keep-Alive"))
1667             m_http_keepalive = 1;
1668         else
1669             m_http_keepalive = 0;
1670         m_http_version = "1.0";
1671     }
1672     else
1673     {
1674         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1675         if (v && !strcmp(v, "close"))
1676             m_http_keepalive = 0;
1677         else
1678             m_http_keepalive = 1;
1679         m_http_version = "1.1";
1680     }
1681
1682     Z_SRW_PDU *srw_pdu = 0;
1683     Z_SOAP *soap_package = 0;
1684     char *charset = 0;
1685     Z_SRW_diagnostic *diagnostic = 0;
1686     int num_diagnostic = 0;
1687     if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
1688                        &charset) == 0
1689         || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
1690                           &charset, &diagnostic, &num_diagnostic) == 0)
1691     {
1692         m_s2z_odr_init = odr_createmem(ODR_ENCODE);
1693         m_s2z_odr_search = odr_createmem(ODR_ENCODE);
1694         m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
1695         m_s2z_init_apdu = 0;
1696         m_s2z_search_apdu = 0;
1697         m_s2z_present_apdu = 0;
1698
1699         m_s2z_stylesheet = 0;
1700         
1701         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
1702         {
1703             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
1704
1705             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
1706             // recordXPath unsupported.
1707             if (srw_req->recordXPath)
1708             {
1709                 yaz_add_srw_diagnostic(odr_decode(),
1710                                        &diagnostic, &num_diagnostic,
1711                                        72, 0);
1712             }
1713             // must have a query
1714             if (!srw_req->query.cql)
1715             {
1716                 yaz_add_srw_diagnostic(odr_decode(),
1717                                        &diagnostic, &num_diagnostic,
1718                                        7, "query");
1719             }
1720             // sort unsupported
1721             if (srw_req->sort_type != Z_SRW_sort_type_none)
1722             {
1723                 yaz_add_srw_diagnostic(odr_decode(),
1724                                        &diagnostic, &num_diagnostic,
1725                                        80, 0);
1726             }
1727             // save stylesheet
1728             if (srw_req->stylesheet)
1729                 m_s2z_stylesheet =
1730                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
1731                                               
1732             // set packing for response records ..
1733             if (srw_req->recordPacking &&
1734                 !strcmp(srw_req->recordPacking, "xml"))
1735                 m_s2z_packing = Z_SRW_recordPacking_XML;
1736             else
1737                 m_s2z_packing = Z_SRW_recordPacking_string;
1738
1739             if (num_diagnostic)
1740             {
1741                 Z_SRW_PDU *srw_pdu =
1742                     yaz_srw_get(odr_encode(),
1743                                 Z_SRW_searchRetrieve_response);
1744                 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1745                 
1746                 srw_res->diagnostics = diagnostic;
1747                 srw_res->num_diagnostics = num_diagnostic;
1748                 send_srw_response(srw_pdu);
1749                 return;
1750             }
1751
1752             // prepare search PDU
1753             m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
1754                                           Z_APDU_searchRequest);
1755             Z_SearchRequest *z_searchRequest =
1756                 m_s2z_search_apdu->u.searchRequest;
1757
1758             z_searchRequest->num_databaseNames = 1;
1759             z_searchRequest->databaseNames = (char**)
1760                 odr_malloc(m_s2z_odr_search, sizeof(char *));
1761             z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
1762                                                            srw_req->database);
1763             
1764             // query transformation
1765             Z_Query *query = (Z_Query *)
1766                 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
1767             z_searchRequest->query = query;
1768             
1769             if (srw_req->query_type == Z_SRW_query_type_cql)
1770             {
1771                 Z_External *ext = (Z_External *) 
1772                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
1773                 ext->direct_reference = 
1774                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
1775                 ext->indirect_reference = 0;
1776                 ext->descriptor = 0;
1777                 ext->which = Z_External_CQL;
1778                 ext->u.cql = srw_req->query.cql;
1779                 
1780                 query->which = Z_Query_type_104;
1781                 query->u.type_104 =  ext;
1782             }
1783             else if (srw_req->query_type == Z_SRW_query_type_pqf)
1784             {
1785                 Z_RPNQuery *RPNquery;
1786                 YAZ_PQF_Parser pqf_parser;
1787                 
1788                 pqf_parser = yaz_pqf_create ();
1789                 
1790                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
1791                                           srw_req->query.pqf);
1792                 if (!RPNquery)
1793                 {
1794                     const char *pqf_msg;
1795                     size_t off;
1796                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1797                     yaz_log(LOG_LOG, "%*s^\n", off+4, "");
1798                     yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
1799                     
1800                     send_to_srw_client_error(10, 0);
1801                     return;
1802                 }
1803                 query->which = Z_Query_type_1;
1804                 query->u.type_1 =  RPNquery;
1805                 
1806                 yaz_pqf_destroy (pqf_parser);
1807             }
1808             else
1809             {
1810                 send_to_srw_client_error(7, "query");
1811                 return;
1812             }
1813
1814             // present
1815             m_s2z_present_apdu = 0;
1816             int max = 0;
1817             if (srw_req->maximumRecords)
1818                 max = *srw_req->maximumRecords;
1819             int start = 1;
1820             if (srw_req->startRecord)
1821                 start = *srw_req->startRecord;
1822             if (max > 0)
1823             {
1824                 // Some backend, such as Voyager doesn't honor piggyback
1825                 // So we use present always (0 &&).
1826                 if (0 && start <= 1)  // Z39.50 piggyback
1827                 {
1828                     *z_searchRequest->smallSetUpperBound = max;
1829                     *z_searchRequest->mediumSetPresentNumber = max;
1830                     *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
1831
1832                     z_searchRequest->preferredRecordSyntax =
1833                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
1834                                                VAL_TEXT_XML);
1835                     if (srw_req->recordSchema)
1836                     {
1837                         z_searchRequest->smallSetElementSetNames =
1838                             z_searchRequest->mediumSetElementSetNames =
1839                             mk_esn_from_schema(m_s2z_odr_search,
1840                                                srw_req->recordSchema);
1841                     }
1842                 }
1843                 else   // Z39.50 present
1844                 {
1845                     m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
1846                                                    Z_APDU_presentRequest);
1847                     Z_PresentRequest *z_presentRequest = 
1848                         m_s2z_present_apdu->u.presentRequest;
1849                     *z_presentRequest->resultSetStartPoint = start;
1850                     *z_presentRequest->numberOfRecordsRequested = max;
1851                     z_presentRequest->preferredRecordSyntax =
1852                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
1853                                                VAL_TEXT_XML);
1854                     if (srw_req->recordSchema)
1855                     {
1856                         z_presentRequest->recordComposition =
1857                             (Z_RecordComposition *)
1858                             odr_malloc(m_s2z_odr_search,
1859                                        sizeof(Z_RecordComposition));
1860                         z_presentRequest->recordComposition->which = 
1861                             Z_RecordComp_simple;                    
1862                         z_presentRequest->recordComposition->u.simple =
1863                             mk_esn_from_schema(m_s2z_odr_search,
1864                                                srw_req->recordSchema);
1865                     }
1866                 }
1867             }
1868             if (!m_client)
1869             {
1870                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
1871                                             Z_APDU_initRequest);
1872                 
1873                 // prevent m_initRequest_apdu memory from being grabbed
1874                 // in Yaz_Proxy::handle_incoming_Z_PDU
1875                 m_initRequest_apdu = m_s2z_init_apdu;
1876                 handle_incoming_Z_PDU(m_s2z_init_apdu);
1877                 return;
1878             }
1879             else
1880             {
1881                 handle_incoming_Z_PDU(m_s2z_search_apdu);
1882                 return;
1883             }
1884         }
1885         else if (srw_pdu->which == Z_SRW_explain_request)
1886         {
1887             Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
1888
1889             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
1890
1891             // save stylesheet
1892             if (srw_req->stylesheet)
1893                 m_s2z_stylesheet =
1894                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
1895
1896             if (srw_req->recordPacking &&
1897                 !strcmp(srw_req->recordPacking, "xml"))
1898                 m_s2z_packing = Z_SRW_recordPacking_XML;
1899             else
1900                 m_s2z_packing = Z_SRW_recordPacking_string;
1901
1902             if (num_diagnostic)
1903             {
1904                 send_srw_explain_response(diagnostic, num_diagnostic);
1905                 return;
1906             }
1907
1908             if (!m_client)
1909             {
1910                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
1911                                             Z_APDU_initRequest);
1912                 
1913                 // prevent m_initRequest_apdu memory from being grabbed
1914                 // in Yaz_Proxy::handle_incoming_Z_PDU
1915                 m_initRequest_apdu = m_s2z_init_apdu;
1916                 handle_incoming_Z_PDU(m_s2z_init_apdu);
1917             }
1918             else
1919                 send_srw_explain_response(0, 0);
1920             return;
1921         }
1922         else if (srw_pdu->which == Z_SRW_scan_request)
1923         {
1924             m_s2z_database = odr_strdup(m_s2z_odr_init,
1925                                         srw_pdu->u.scan_request->database);
1926
1927             yaz_add_srw_diagnostic(odr_decode(),
1928                                    &diagnostic, &num_diagnostic,
1929                                    4, "scan");
1930             Z_SRW_PDU *srw_pdu =
1931                 yaz_srw_get(odr_encode(),
1932                             Z_SRW_scan_response);
1933             Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
1934             
1935             srw_res->diagnostics = diagnostic;
1936             srw_res->num_diagnostics = num_diagnostic;
1937             send_srw_response(srw_pdu);
1938             return;
1939         }
1940         else
1941         {
1942             m_s2z_database = 0;
1943
1944             send_to_srw_client_error(4, 0);
1945         }
1946     }
1947     int len = 0;
1948     Z_GDU *p = z_get_HTTP_Response(odr_encode(), 400);
1949     timeout(0);
1950     send_GDU(p, &len);
1951 }
1952
1953 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
1954 {
1955     Z_ReferenceId **refid = get_referenceIdP(apdu);
1956     nmem_reset(m_referenceId_mem);
1957     if (refid && *refid)
1958     {
1959         m_referenceId = (Z_ReferenceId *)
1960             nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
1961         m_referenceId->len = m_referenceId->size = (*refid)->len;
1962         m_referenceId->buf = (unsigned char *)
1963             nmem_malloc(m_referenceId_mem, (*refid)->len);
1964         memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
1965     }
1966     else
1967         m_referenceId = 0;
1968
1969     if (!m_client && m_invalid_session)
1970     {
1971         m_apdu_invalid_session = apdu;
1972         m_mem_invalid_session = odr_extract_mem(odr_decode());
1973         apdu = m_initRequest_apdu;
1974     }
1975     
1976     // Determine our client.
1977     Z_OtherInformation **oi;
1978     get_otherInfoAPDU(apdu, &oi);
1979     m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
1980     if (!m_client)
1981     {
1982         delete this;
1983         return;
1984     }
1985     m_client->m_server = this;
1986
1987     if (apdu->which == Z_APDU_initRequest)
1988     {
1989         if (apdu->u.initRequest->implementationId)
1990             yaz_log(LOG_LOG, "%simplementationId: %s",
1991                     m_session_str, apdu->u.initRequest->implementationId);
1992         if (apdu->u.initRequest->implementationName)
1993             yaz_log(LOG_LOG, "%simplementationName: %s",
1994                     m_session_str, apdu->u.initRequest->implementationName);
1995         if (apdu->u.initRequest->implementationVersion)
1996             yaz_log(LOG_LOG, "%simplementationVersion: %s",
1997                     m_session_str, apdu->u.initRequest->implementationVersion);
1998         if (m_initRequest_apdu == 0)
1999         {
2000             if (m_initRequest_mem)
2001                 nmem_destroy(m_initRequest_mem);
2002             m_initRequest_apdu = apdu;
2003             m_initRequest_mem = odr_extract_mem(odr_decode());
2004
2005             m_initRequest_preferredMessageSize = *apdu->u.initRequest->
2006                 preferredMessageSize;
2007             *apdu->u.initRequest->preferredMessageSize = 1024*1024;
2008             m_initRequest_maximumRecordSize = *apdu->u.initRequest->
2009                 maximumRecordSize;
2010             *apdu->u.initRequest->maximumRecordSize = 1024*1024;
2011
2012             // save init options for the response..
2013             m_initRequest_options = apdu->u.initRequest->options;
2014             
2015             apdu->u.initRequest->options = 
2016                 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2017                                            sizeof(Odr_bitmask));
2018             ODR_MASK_ZERO(apdu->u.initRequest->options);
2019             int i;
2020             for (i = 0; i<= 24; i++)
2021                 ODR_MASK_SET(apdu->u.initRequest->options, i);
2022             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2023                            Z_Options_negotiationModel);
2024             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2025                            Z_Options_concurrentOperations);
2026
2027             // make new version
2028             m_initRequest_version = apdu->u.initRequest->protocolVersion;
2029             apdu->u.initRequest->protocolVersion = 
2030                 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2031                                            sizeof(Odr_bitmask));
2032             ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
2033
2034             for (i = 0; i<= 8; i++)
2035                 ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
2036         }
2037         if (m_client->m_init_flag)
2038         {
2039             if (handle_init_response_for_invalid_session(apdu))
2040                 return;
2041             if (m_client->m_initResponse)
2042             {
2043                 Z_APDU *apdu2 = m_client->m_initResponse;
2044                 apdu2->u.initResponse->otherInfo = 0;
2045                 if (m_client->m_cookie && *m_client->m_cookie)
2046                     set_otherInformationString(apdu2, VAL_COOKIE, 1,
2047                                                m_client->m_cookie);
2048                 apdu2->u.initResponse->referenceId =
2049                     apdu->u.initRequest->referenceId;
2050                 apdu2->u.initResponse->options = m_client->m_initResponse_options;
2051                 apdu2->u.initResponse->protocolVersion = 
2052                     m_client->m_initResponse_version;
2053                 
2054                 send_to_client(apdu2);
2055                 return;
2056             }
2057         }
2058         m_client->m_init_flag = 1;
2059     }
2060     handle_max_record_retrieve(apdu);
2061
2062     if (apdu)
2063         apdu = handle_syntax_validation(apdu);
2064
2065     if (apdu)
2066         apdu = handle_query_transformation(apdu);
2067
2068     if (apdu)
2069         apdu = handle_query_validation(apdu);
2070
2071     if (apdu)
2072         apdu = result_set_optimize(apdu);
2073     if (!apdu)
2074     {
2075         m_client->timeout(m_target_idletime);  // mark it active even 
2076         // though we didn't use it
2077         return;
2078     }
2079
2080     // delete other info part from PDU before sending to target
2081     get_otherInfoAPDU(apdu, &oi);
2082     if (oi)
2083         *oi = 0;
2084
2085     if (apdu->which == Z_APDU_presentRequest &&
2086         m_client->m_resultSetStartPoint == 0)
2087     {
2088         Z_PresentRequest *pr = apdu->u.presentRequest;
2089         m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
2090         m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
2091     } else {
2092         m_client->m_resultSetStartPoint = 0;
2093     }
2094     if (m_client->send_to_target(apdu) < 0)
2095     {
2096         delete m_client;
2097         m_client = 0;
2098         delete this;
2099     }
2100     else
2101         m_client->m_waiting = 1;
2102 }
2103
2104 void Yaz_Proxy::connectNotify()
2105 {
2106 }
2107
2108 void Yaz_Proxy::shutdown()
2109 {
2110     m_invalid_session = 0;
2111     // only keep if keep_alive flag is set...
2112     if (m_client && 
2113         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
2114         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
2115         m_client->m_waiting == 0)
2116     {
2117         yaz_log(LOG_LOG, "%sShutdown (client to proxy) keepalive %s",
2118                  m_session_str,
2119                  m_client->get_hostname());
2120         yaz_log(LOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
2121                 m_session_str, m_client->m_pdu_recv,
2122                 m_client->m_bytes_sent + m_client->m_bytes_recv,
2123                 m_keepalive_limit_bw, m_keepalive_limit_pdu);
2124         assert (m_client->m_waiting != 2);
2125         // Tell client (if any) that no server connection is there..
2126         m_client->m_server = 0;
2127         m_invalid_session = 0;
2128     }
2129     else if (m_client)
2130     {
2131         yaz_log (LOG_LOG, "%sShutdown (client to proxy) close %s",
2132                  m_session_str,
2133                  m_client->get_hostname());
2134         assert (m_client->m_waiting != 2);
2135         delete m_client;
2136     }
2137     else if (!m_parent)
2138     {
2139         yaz_log (LOG_LOG, "%sshutdown (client to proxy) bad state",
2140                  m_session_str);
2141         assert (m_parent);
2142     }
2143     else 
2144     {
2145         yaz_log (LOG_LOG, "%sShutdown (client to proxy)",
2146                  m_session_str);
2147     }
2148     if (m_parent)
2149         m_parent->pre_init();
2150     delete this;
2151 }
2152
2153 const char *Yaz_ProxyClient::get_session_str() 
2154 {
2155     if (!m_server)
2156         return "0 ";
2157     return m_server->get_session_str();
2158 }
2159
2160 void Yaz_ProxyClient::shutdown()
2161 {
2162     yaz_log (LOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
2163              get_hostname());
2164     delete m_server;
2165     delete this;
2166 }
2167
2168 void Yaz_Proxy::failNotify()
2169 {
2170     inc_request_no();
2171     yaz_log (LOG_LOG, "%sConnection closed by client",
2172              get_session_str());
2173     shutdown();
2174 }
2175
2176 void Yaz_ProxyClient::failNotify()
2177 {
2178     if (m_server)
2179         m_server->inc_request_no();
2180     yaz_log (LOG_LOG, "%sConnection closed by target %s", 
2181              get_session_str(), get_hostname());
2182     shutdown();
2183 }
2184
2185 void Yaz_ProxyClient::connectNotify()
2186 {
2187     const char *s = get_session_str();
2188     const char *h = get_hostname();
2189     yaz_log (LOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
2190              m_target_idletime);
2191     timeout(m_target_idletime);
2192     if (!m_server)
2193         pre_init_client();
2194 }
2195
2196 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
2197                                                   *the_PDU_Observable, int fd)
2198 {
2199     return new Yaz_ProxyClient(the_PDU_Observable, 0);
2200 }
2201
2202 Yaz_ProxyClient::~Yaz_ProxyClient()
2203 {
2204     if (m_prev)
2205         *m_prev = m_next;
2206     if (m_next)
2207         m_next->m_prev = m_prev;
2208     m_waiting = 2;     // for debugging purposes only.
2209     odr_destroy(m_init_odr);
2210     delete m_last_query;
2211     xfree (m_last_resultSetId);
2212     xfree (m_cookie);
2213 }
2214
2215 void Yaz_ProxyClient::pre_init_client()
2216 {
2217     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
2218     Z_InitRequest *req = apdu->u.initRequest;
2219     
2220     int i;
2221     for (i = 0; i<= 24; i++)
2222         ODR_MASK_SET(req->options, i);
2223     ODR_MASK_CLEAR(apdu->u.initRequest->options,
2224                    Z_Options_negotiationModel);
2225     ODR_MASK_CLEAR(apdu->u.initRequest->options,
2226                    Z_Options_concurrentOperations);
2227     for (i = 0; i<= 10; i++)
2228         ODR_MASK_SET(req->protocolVersion, i);
2229
2230     if (send_to_target(apdu) < 0)
2231     {
2232         delete this;
2233     }
2234     else
2235     {
2236         m_waiting = 1;
2237         m_init_flag = 1;
2238     }
2239 }
2240
2241 void Yaz_Proxy::pre_init()
2242 {
2243     int i;
2244     const char *name = 0;
2245     const char *zurl_in_use[MAX_ZURL_PLEX];
2246     int limit_bw, limit_pdu, limit_req;
2247     int target_idletime, client_idletime;
2248     int max_clients;
2249     int keepalive_limit_bw, keepalive_limit_pdu;
2250     int pre_init;
2251     const char *cql2rpn = 0;
2252
2253     Yaz_ProxyConfig *cfg = check_reconfigure();
2254
2255     zurl_in_use[0] = 0;
2256
2257     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
2258         set_APDU_yazlog(1);
2259     else
2260         set_APDU_yazlog(0);
2261
2262     for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
2263                                           &limit_bw, &limit_pdu, &limit_req,
2264                                           &target_idletime, &client_idletime,
2265                                           &max_clients, 
2266                                           &keepalive_limit_bw,
2267                                           &keepalive_limit_pdu,
2268                                           &pre_init,
2269                                           &cql2rpn) ; i++)
2270     {
2271         if (pre_init)
2272         {
2273             int j;
2274             for (j = 0; zurl_in_use[j]; j++)
2275             {
2276                 Yaz_ProxyClient *c;
2277                 int spare = 0;
2278                 int spare_waiting = 0;
2279                 int in_use = 0;
2280                 int other = 0;
2281                 for (c = m_clientPool; c; c = c->m_next)
2282                 {
2283                     if (!strcmp(zurl_in_use[j], c->get_hostname()))
2284                     {
2285                         if (c->m_cookie == 0)
2286                         {
2287                             if (c->m_server == 0)
2288                                 if (c->m_waiting)
2289                                     spare_waiting++;
2290                                 else
2291                                     spare++;
2292                             else
2293                                 in_use++;
2294                         }
2295                         else
2296                             other++;
2297                     }
2298                 }
2299                 yaz_log(LOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
2300                         "sparew=%d preinit=%d",m_session_str,
2301                         name, zurl_in_use[j], in_use, other,
2302                         spare, spare_waiting, pre_init);
2303                 if (spare + spare_waiting < pre_init)
2304                 {
2305                     c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
2306                     c->m_next = m_clientPool;
2307                     if (c->m_next)
2308                         c->m_next->m_prev = &c->m_next;
2309                     m_clientPool = c;
2310                     c->m_prev = &m_clientPool;
2311                     
2312                     if (m_log_mask & PROXY_LOG_APDU_SERVER)
2313                         c->set_APDU_yazlog(1);
2314                     else
2315                         c->set_APDU_yazlog(0);
2316
2317                     if (c->client(zurl_in_use[j]))
2318                     {
2319                         timeout(60);
2320                         delete c;
2321                         return;
2322                     }
2323                     c->timeout(30);
2324                     c->m_waiting = 1;
2325                     c->m_target_idletime = target_idletime;
2326                     c->m_seqno = m_seqno++;
2327                 }
2328             }
2329         }
2330     }
2331 }
2332
2333 void Yaz_Proxy::timeoutNotify()
2334 {
2335     if (m_parent)
2336     {
2337         if (m_bw_hold_PDU)
2338         {
2339             timeout(m_client_idletime);
2340             Z_GDU *apdu = m_bw_hold_PDU;
2341             m_bw_hold_PDU = 0;
2342             
2343             if (apdu->which == Z_GDU_Z3950)
2344                 handle_incoming_Z_PDU(apdu->u.z3950);
2345             else if (apdu->which == Z_GDU_HTTP_Request)
2346                 handle_incoming_HTTP(apdu->u.HTTP_Request);
2347         }
2348         else if (m_stylesheet_nprl)
2349             convert_xsl_delay();
2350         else
2351         {
2352             inc_request_no();
2353
2354             yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
2355             shutdown();
2356         }
2357     }
2358     else
2359     {
2360         timeout(600);
2361         pre_init();
2362     }
2363 }
2364
2365 void Yaz_Proxy::markInvalid()
2366 {
2367     m_client = 0;
2368     m_invalid_session = 1;
2369 }
2370
2371 void Yaz_ProxyClient::timeoutNotify()
2372 {
2373     if (m_server)
2374         m_server->inc_request_no();
2375
2376     yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
2377              get_hostname());
2378     m_waiting = 1;
2379     m_root->pre_init();
2380     if (m_server && m_init_flag)
2381     {
2382         // target timed out in a session that was properly initialized
2383         // server object stay alive but we mark it as invalid so it
2384         // gets initialized again
2385         m_server->markInvalid();
2386         m_server = 0;
2387     }
2388     shutdown();
2389 }
2390
2391 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
2392                                  Yaz_Proxy *parent) :
2393     Yaz_Z_Assoc (the_PDU_Observable)
2394 {
2395     m_cookie = 0;
2396     m_next = 0;
2397     m_prev = 0;
2398     m_init_flag = 0;
2399     m_last_query = 0;
2400     m_last_resultSetId = 0;
2401     m_last_resultCount = 0;
2402     m_last_ok = 0;
2403     m_sr_transform = 0;
2404     m_waiting = 0;
2405     m_init_odr = odr_createmem (ODR_DECODE);
2406     m_initResponse = 0;
2407     m_initResponse_options = 0;
2408     m_initResponse_version = 0;
2409     m_initResponse_preferredMessageSize = 0;
2410     m_initResponse_maximumRecordSize = 0;
2411     m_resultSetStartPoint = 0;
2412     m_bytes_sent = m_bytes_recv = 0;
2413     m_pdu_recv = 0;
2414     m_server = 0;
2415     m_seqno = 0;
2416     m_target_idletime = 600;
2417     m_root = parent;
2418 }
2419
2420 const char *Yaz_Proxy::option(const char *name, const char *value)
2421 {
2422     if (!strcmp (name, "optimize")) {
2423         if (value) {
2424             xfree (m_optimize); 
2425             m_optimize = xstrdup (value);
2426         }
2427         return m_optimize;
2428     }
2429     return 0;
2430 }
2431
2432 void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
2433 {
2434
2435 }
2436
2437 void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
2438 {
2439     if (apdu->which == Z_GDU_Z3950)
2440         recv_Z_PDU(apdu->u.z3950, len);
2441     else if (apdu->which == Z_GDU_HTTP_Response)
2442         recv_HTTP_response(apdu->u.HTTP_Response, len);
2443     else
2444         shutdown();
2445 }
2446
2447 int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
2448 {
2449     if (!m_invalid_session)
2450         return 0;
2451     m_invalid_session = 0;
2452     handle_incoming_Z_PDU(m_apdu_invalid_session);
2453     assert (m_mem_invalid_session);
2454     nmem_destroy(m_mem_invalid_session);
2455     m_mem_invalid_session = 0;
2456     return 1;
2457 }
2458
2459 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
2460 {
2461     m_bytes_recv += len;
2462
2463     m_pdu_recv++;
2464     m_waiting = 0;
2465     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
2466         yaz_log (LOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
2467                  apdu_name(apdu), get_hostname(), len);
2468     if (apdu->which == Z_APDU_initResponse)
2469     {
2470         if (!m_server)  // if this is a pre init session , check for more
2471             m_root->pre_init();
2472         NMEM nmem = odr_extract_mem (odr_decode());
2473         odr_reset (m_init_odr);
2474         nmem_transfer (m_init_odr->mem, nmem);
2475         m_initResponse = apdu;
2476         m_initResponse_options = apdu->u.initResponse->options;
2477         m_initResponse_version = apdu->u.initResponse->protocolVersion;
2478         m_initResponse_preferredMessageSize = 
2479             *apdu->u.initResponse->preferredMessageSize;
2480         m_initResponse_maximumRecordSize = 
2481             *apdu->u.initResponse->maximumRecordSize;
2482
2483         Z_InitResponse *ir = apdu->u.initResponse;
2484         char *im0 = ir->implementationName;
2485         
2486         char *im1 = (char*) 
2487             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
2488         *im1 = '\0';
2489         if (im0)
2490         {
2491             strcat(im1, im0);
2492             strcat(im1, " ");
2493         }
2494         strcat(im1, "(YAZ Proxy)");
2495         ir->implementationName = im1;
2496
2497         nmem_destroy (nmem);
2498
2499         if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
2500             return;
2501     }
2502     if (apdu->which == Z_APDU_searchResponse)
2503     {
2504         Z_SearchResponse *sr = apdu->u.searchResponse;
2505         m_last_resultCount = *sr->resultCount;
2506         int status = *sr->searchStatus;
2507         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
2508         {
2509             m_last_ok = 1;
2510             
2511             if (sr->records && sr->records->which == Z_Records_DBOSD)
2512             {
2513                 m_cache.add(odr_decode(),
2514                             sr->records->u.databaseOrSurDiagnostics, 1,
2515                             *sr->resultCount);
2516             }
2517         }
2518     }
2519     if (apdu->which == Z_APDU_presentResponse)
2520     {
2521         Z_PresentResponse *pr = apdu->u.presentResponse;
2522         if (m_sr_transform)
2523         {
2524             m_sr_transform = 0;
2525             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2526             Z_SearchResponse *sr = new_apdu->u.searchResponse;
2527             sr->referenceId = pr->referenceId;
2528             *sr->resultCount = m_last_resultCount;
2529             sr->records = pr->records;
2530             sr->nextResultSetPosition = pr->nextResultSetPosition;
2531             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
2532             apdu = new_apdu;
2533         }
2534         if (pr->records && 
2535             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
2536         {
2537             m_cache.add(odr_decode(),
2538                         pr->records->u.databaseOrSurDiagnostics,
2539                         m_resultSetStartPoint, -1);
2540             m_resultSetStartPoint = 0;
2541         }
2542     }
2543     if (m_cookie)
2544         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
2545     if (m_server)
2546     {
2547         m_server->send_to_client(apdu);
2548     }
2549     if (apdu->which == Z_APDU_close)
2550     {
2551         shutdown();
2552     }
2553 }
2554
2555 void Yaz_Proxy::low_socket_close()
2556 {
2557     int i;
2558     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
2559         if  (m_lo_fd[i] >= 0)
2560             ::close(m_lo_fd[i]);
2561 }
2562
2563 void Yaz_Proxy::low_socket_open()
2564 {
2565     int i;
2566     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
2567         m_lo_fd[i] = open("/dev/null", O_RDONLY);
2568 }
2569
2570 int Yaz_Proxy::server(const char *addr)
2571 {
2572     int r = Yaz_Z_Assoc::server(addr);
2573     if (!r)
2574     {
2575         yaz_log(LOG_LOG, "%sStarted proxy " VERSION " on %s", m_session_str, addr);
2576         timeout(1);
2577     }
2578     return r;
2579 }
2580