Log XSLT errors to log
[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.110 2004-03-17 10:49:22 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 #if 0
643             fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
644 #endif
645             xmlDocPtr res, doc = xmlParseMemory(
646                 (char*) r->u.octet_aligned->buf,
647                 r->u.octet_aligned->len);
648
649             
650             yaz_log(LOG_LOG, "%sXSLT convert %d",
651                     m_session_str, m_stylesheet_offset);
652             res = xsltApplyStylesheet(m_stylesheet_xsp, doc, 0);
653
654             if (res)
655             {
656                 xmlChar *out_buf;
657                 int out_len;
658                 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
659                 
660                 m_stylesheet_nprl->records[m_stylesheet_offset]->
661                     u.databaseRecord = 
662                     z_ext_record(odr_encode(), VAL_TEXT_XML,
663                                  (char*) out_buf, out_len);
664                 xmlFree(out_buf);
665                 xmlFreeDoc(res);
666             }
667
668             xmlFreeDoc(doc);
669         }
670     }
671     m_stylesheet_offset++;
672     if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
673     {
674         m_stylesheet_nprl = 0;
675         if (m_stylesheet_xsp)
676             xsltFreeStylesheet(m_stylesheet_xsp);
677         m_stylesheet_xsp = 0;
678         timeout(m_client_idletime);
679         send_PDU_convert(m_stylesheet_apdu);
680     }
681     else
682         timeout(0);
683 }
684
685 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p)
686 {
687     int i;
688
689     yaz_iconv_t cd = yaz_iconv_open("UTF-8", "MARC-8");
690     yaz_marc_t mt = yaz_marc_create();
691     yaz_marc_xml(mt, YAZ_MARC_MARCXML);
692     yaz_marc_iconv(mt, cd);
693     for (i = 0; i < p->num_records; i++)
694     {
695         Z_NamePlusRecord *npr = p->records[i];
696         if (npr->which == Z_NamePlusRecord_databaseRecord)
697         {
698             Z_External *r = npr->u.databaseRecord;
699             if (r->which == Z_External_octet)
700             {
701                 int rlen;
702                 char *result;
703                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
704                                         r->u.octet_aligned->len,
705                                         &result, &rlen))
706                 {
707                     npr->u.databaseRecord = z_ext_record(odr_encode(),
708                                                          VAL_TEXT_XML,
709                                                          result, rlen);
710                 }
711             }
712         }
713     }
714     if (cd)
715         yaz_iconv_close(cd);
716     yaz_marc_destroy(mt);
717 }
718
719 void Yaz_Proxy::logtime()
720 {
721     if (m_time_tv.tv_sec)
722     {
723         struct timeval tv;
724         gettimeofday(&tv, 0);
725         long diff = (tv.tv_sec - m_time_tv.tv_sec)*1000000 +
726             (tv.tv_usec - m_time_tv.tv_usec);
727         if (diff >= 0)
728             yaz_log(LOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
729                     diff/1000000, (diff/1000)%1000);
730     }
731     m_time_tv.tv_sec = 0;
732     m_time_tv.tv_usec = 0;
733 }
734
735 int Yaz_Proxy::send_http_response(int code)
736 {
737     ODR o = odr_encode();
738     Z_GDU *gdu = z_get_HTTP_Response(o, code);
739     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
740     if (m_http_version)
741         hres->version = odr_strdup(o, m_http_version);
742     if (m_http_keepalive)
743         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
744     else
745         timeout(0);
746     
747     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
748     {
749         yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
750                  gdu_name(gdu));
751     }
752     int len;
753     int r = send_GDU(gdu, &len);
754     m_bytes_sent += len;
755     m_bw_stat.add_bytes(len);
756     logtime();
757     return r;
758 }
759
760 int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
761 {
762     ODR o = odr_encode();
763     const char *ctype = "text/xml";
764     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
765     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
766     if (m_http_version)
767         hres->version = odr_strdup(o, m_http_version);
768     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
769     if (m_http_keepalive)
770         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
771     else
772         timeout(0);
773
774     static Z_SOAP_Handler soap_handlers[2] = {
775 #if HAVE_XSLT
776         {"http://www.loc.gov/zing/srw/", 0,
777          (Z_SOAP_fun) yaz_srw_codec},
778 #endif
779         {0, 0, 0}
780     };
781     
782     Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
783     soap_package->which = Z_SOAP_generic;
784     soap_package->u.generic = 
785         (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
786     soap_package->u.generic->no = 0;
787     soap_package->u.generic->ns = soap_handlers[0].ns;
788     soap_package->u.generic->p = (void *) srw_pdu;
789     soap_package->ns = m_soap_ns;
790     z_soap_codec_enc_xsl(o, &soap_package,
791                          &hres->content_buf, &hres->content_len,
792                          soap_handlers, 0, m_s2z_stylesheet);
793     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
794     {
795         yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
796                  gdu_name(gdu));
797     }
798     int len;
799     int r = send_GDU(gdu, &len);
800     m_bytes_sent += len;
801     m_bw_stat.add_bytes(len);
802     logtime();
803     return r;
804 }
805
806 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
807 {
808     ODR o = odr_encode();
809     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
810     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
811
812     srw_res->num_diagnostics = 1;
813     srw_res->diagnostics = (Z_SRW_diagnostic *)
814         odr_malloc(o, sizeof(*srw_res->diagnostics));
815     yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
816     return send_srw_response(srw_pdu);
817 }
818
819 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
820                              Z_DefaultDiagFormat *ddf)
821 {
822     int bib1_code = *ddf->condition;
823     if (bib1_code == 109)
824         return 404;
825     srw_res->num_diagnostics = 1;
826     srw_res->diagnostics = (Z_SRW_diagnostic *)
827         odr_malloc(o, sizeof(*srw_res->diagnostics));
828     yaz_mk_std_diagnostic(o, srw_res->diagnostics,
829                           yaz_diag_bib1_to_srw(*ddf->condition), 
830                           ddf->u.v2Addinfo);
831     return 0;
832 }
833
834 int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
835 {
836     ODR o = odr_encode();
837     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
838     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
839
840     srw_res->numberOfRecords = odr_intdup (o, hits);
841     if (records && records->which == Z_Records_DBOSD)
842     {
843         srw_res->num_records =
844             records->u.databaseOrSurDiagnostics->num_records;
845         int i;
846         srw_res->records = (Z_SRW_record *)
847             odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
848         for (i = 0; i < srw_res->num_records; i++)
849         {
850             Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
851             if (npr->which != Z_NamePlusRecord_databaseRecord)
852             {
853                 srw_res->records[i].recordSchema = "diagnostic";
854                 srw_res->records[i].recordPacking = m_s2z_packing;
855                 srw_res->records[i].recordData_buf = "67";
856                 srw_res->records[i].recordData_len = 2;
857                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
858                 continue;
859             }
860             Z_External *r = npr->u.databaseRecord;
861             oident *ent = oid_getentbyoid(r->direct_reference);
862             if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
863             {
864                 srw_res->records[i].recordSchema = m_schema;
865                 srw_res->records[i].recordPacking = m_s2z_packing;
866                 srw_res->records[i].recordData_buf = (char*) 
867                     r->u.octet_aligned->buf;
868                 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
869                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
870             }
871             else
872             {
873                 srw_res->records[i].recordSchema = "diagnostic";
874                 srw_res->records[i].recordPacking = m_s2z_packing;
875                 srw_res->records[i].recordData_buf = "67";
876                 srw_res->records[i].recordData_len = 2;
877                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
878             }
879         }
880     }
881     if (records && records->which == Z_Records_NSD)
882     {
883         int http_code;
884         http_code = z_to_srw_diag(odr_encode(), srw_res,
885                                    records->u.nonSurrogateDiagnostic);
886         if (http_code)
887             return send_http_response(http_code);
888     }
889     return send_srw_response(srw_pdu);
890     
891 }
892
893 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
894                                         int num_diagnostics)
895 {
896     Yaz_ProxyConfig *cfg = check_reconfigure();
897     if (cfg)
898     {
899         int len;
900         char *b = cfg->get_explain(odr_encode(), 0 /* target */,
901                                    m_s2z_database, &len);
902         if (b)
903         {
904             Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
905             Z_SRW_explainResponse *er = res->u.explain_response;
906
907             er->record.recordData_buf = b;
908             er->record.recordData_len = len;
909             er->record.recordPacking = m_s2z_packing;
910             er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
911
912             er->diagnostics = diagnostics;
913             er->num_diagnostics = num_diagnostics;
914             return send_srw_response(res);
915         }
916     }
917     return send_http_response(404);
918 }
919
920 int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
921 {
922     if (m_http_version)
923     {
924         if (apdu->which == Z_APDU_initResponse)
925         {
926             Z_InitResponse *res = apdu->u.initResponse;
927             if (*res->result == 0)
928             {
929                 send_to_srw_client_error(3, 0);
930             }
931             else if (!m_s2z_search_apdu)
932             {
933                 send_srw_explain_response(0, 0);
934             }
935             else
936             {
937                 handle_incoming_Z_PDU(m_s2z_search_apdu);
938             }
939         }
940         else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
941         {
942             m_s2z_search_apdu = 0;
943             Z_SearchResponse *res = apdu->u.searchResponse;
944             m_s2z_hit_count = *res->resultCount;
945             if (res->records && res->records->which == Z_Records_NSD)
946             {
947                 send_to_srw_client_ok(0, res->records, 1);
948             }
949             else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
950             {
951                 // adjust 
952                 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
953                 
954                 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
955                 {
956                     if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
957                         > m_s2z_hit_count)
958                         *pr->numberOfRecordsRequested =
959                             1 + m_s2z_hit_count - *pr->resultSetStartPoint;
960                 }
961                 handle_incoming_Z_PDU(m_s2z_present_apdu);
962             }
963             else
964             {
965                 m_s2z_present_apdu = 0;
966                 send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
967             }
968         }
969         else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
970         {
971             int start = 
972                 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
973
974             m_s2z_present_apdu = 0;
975             Z_PresentResponse *res = apdu->u.presentResponse;
976             send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
977         }
978     }
979     else
980     {
981         int len = 0;
982         if (m_log_mask & PROXY_LOG_REQ_CLIENT)
983             yaz_log (LOG_LOG, "%sSending %s to client", m_session_str,
984                      apdu_name(apdu));
985         int r = send_Z_PDU(apdu, &len);
986         m_bytes_sent += len;
987         m_bw_stat.add_bytes(len);
988         logtime();
989         return r;
990     }
991     return 0;
992 }
993
994 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
995 {
996     int kill_session = 0;
997     Z_ReferenceId **new_id = get_referenceIdP(apdu);
998
999     if (new_id)
1000         *new_id = m_referenceId;
1001     
1002     if (apdu->which == Z_APDU_searchResponse)
1003     {
1004         Z_SearchResponse *sr = apdu->u.searchResponse;
1005         Z_Records *p = sr->records;
1006         if (p && p->which == Z_Records_NSD)
1007         {
1008             Z_DiagRec dr, *dr_p = &dr;
1009             dr.which = Z_DiagRec_defaultFormat;
1010             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1011
1012             *sr->searchStatus = 0;
1013             display_diagrecs(&dr_p, 1);
1014         }
1015         else
1016         {
1017             if (p && p->which == Z_Records_DBOSD)
1018             {
1019                 if (m_marcxml_flag)
1020                     convert_to_marcxml(p->u.databaseOrSurDiagnostics);
1021                 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1022                     return 0;
1023                     
1024             }
1025             if (sr->resultCount)
1026             {
1027                 yaz_log(LOG_LOG, "%s%d hits", m_session_str,
1028                         *sr->resultCount);
1029                 if (*sr->resultCount < 0)
1030                 {
1031                     m_invalid_session = 1;
1032                     kill_session = 1;
1033
1034                     *sr->searchStatus = 0;
1035                     sr->records =
1036                         create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
1037                     *sr->resultCount = 0;
1038                 }
1039             }
1040         }
1041     }
1042     else if (apdu->which == Z_APDU_presentResponse)
1043     {
1044         Z_PresentResponse *sr = apdu->u.presentResponse;
1045         Z_Records *p = sr->records;
1046         if (p && p->which == Z_Records_NSD)
1047         {
1048             Z_DiagRec dr, *dr_p = &dr;
1049             dr.which = Z_DiagRec_defaultFormat;
1050             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1051             if (*sr->presentStatus == Z_PresentStatus_success)
1052                 *sr->presentStatus = Z_PresentStatus_failure;
1053             display_diagrecs(&dr_p, 1);
1054         }
1055         if (p && p->which == Z_Records_DBOSD)
1056         {
1057             if (m_marcxml_flag)
1058                 convert_to_marcxml(p->u.databaseOrSurDiagnostics);
1059             if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1060                 return 0;
1061         }
1062     }
1063     else if (apdu->which == Z_APDU_initResponse)
1064     {
1065         if (m_initRequest_options)
1066         {
1067             Z_Options *nopt = 
1068                 (Odr_bitmask *)odr_malloc(odr_encode(),
1069                                           sizeof(Odr_bitmask));
1070             ODR_MASK_ZERO(nopt);
1071
1072             int i;
1073             for (i = 0; i<24; i++)
1074                 if (ODR_MASK_GET(m_initRequest_options, i) &&
1075                     ODR_MASK_GET(apdu->u.initResponse->options, i))
1076                     ODR_MASK_SET(nopt, i);
1077             apdu->u.initResponse->options = nopt;           
1078         }
1079         if (m_initRequest_version)
1080         {
1081             Z_ProtocolVersion *nopt = 
1082                 (Odr_bitmask *)odr_malloc(odr_encode(),
1083                                           sizeof(Odr_bitmask));
1084             ODR_MASK_ZERO(nopt);
1085
1086             int i;
1087             for (i = 0; i<8; i++)
1088                 if (ODR_MASK_GET(m_initRequest_version, i) &&
1089                     ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
1090                     ODR_MASK_SET(nopt, i);
1091             apdu->u.initResponse->protocolVersion = nopt;           
1092         }
1093         apdu->u.initResponse->preferredMessageSize =
1094             odr_intdup(odr_encode(),
1095                        m_client->m_initResponse_preferredMessageSize >
1096                        m_initRequest_preferredMessageSize ?
1097                        m_initRequest_preferredMessageSize :
1098                        m_client->m_initResponse_preferredMessageSize);
1099         apdu->u.initResponse->maximumRecordSize =
1100             odr_intdup(odr_encode(),
1101                        m_client->m_initResponse_maximumRecordSize >
1102                        m_initRequest_maximumRecordSize ?
1103                        m_initRequest_maximumRecordSize :
1104                        m_client->m_initResponse_maximumRecordSize);
1105     }
1106     int r = send_PDU_convert(apdu);
1107     if (r)
1108         return r;
1109     if (kill_session)
1110     {
1111         delete m_client;
1112         m_client = 0;
1113         m_parent->pre_init();
1114     }
1115     return r;
1116 }
1117
1118 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
1119 {
1120     int len = 0;
1121     const char *apdu_name_tmp = apdu_name(apdu);
1122     int r = send_Z_PDU(apdu, &len);
1123     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
1124         yaz_log (LOG_LOG, "%sSending %s to %s %d bytes",
1125                  get_session_str(),
1126                  apdu_name_tmp, get_hostname(), len);
1127     m_bytes_sent += len;
1128     return r;
1129 }
1130
1131 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
1132 {
1133     if (apdu->which == Z_APDU_presentRequest)
1134     {
1135         Z_PresentRequest *pr = apdu->u.presentRequest;
1136         int toget = *pr->numberOfRecordsRequested;
1137         int start = *pr->resultSetStartPoint;
1138
1139         yaz_log(LOG_LOG, "%sPresent %s %d+%d", m_session_str,
1140                 pr->resultSetId, start, toget);
1141
1142         if (*m_parent->m_optimize == '0')
1143             return apdu;
1144
1145         if (!m_client->m_last_resultSetId)
1146         {
1147             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1148             new_apdu->u.presentResponse->records =
1149                 create_nonSurrogateDiagnostics(odr_encode(), 30,
1150                                                pr->resultSetId);
1151             send_to_client(new_apdu);
1152             return 0;
1153         }
1154         if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
1155         {
1156             if (start+toget-1 > m_client->m_last_resultCount)
1157             {
1158                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1159                 new_apdu->u.presentResponse->records =
1160                     create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
1161                 send_to_client(new_apdu);
1162                 return 0;
1163             }
1164             Z_NamePlusRecordList *npr;
1165             if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
1166                                           pr->preferredRecordSyntax,
1167                                           pr->recordComposition))
1168             {
1169                 yaz_log (LOG_LOG, "%sReturned cached records for present request", 
1170                          m_session_str);
1171                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1172                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
1173                 
1174                 new_apdu->u.presentResponse->numberOfRecordsReturned
1175                     = odr_intdup(odr_encode(), toget);
1176                                                                  
1177                 new_apdu->u.presentResponse->records = (Z_Records*)
1178                     odr_malloc(odr_encode(), sizeof(Z_Records));
1179                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
1180                 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
1181                 new_apdu->u.presentResponse->nextResultSetPosition =
1182                     odr_intdup(odr_encode(), start+toget);
1183
1184                 send_to_client(new_apdu);
1185                 return 0;
1186             }
1187         }
1188     }
1189
1190     if (apdu->which != Z_APDU_searchRequest)
1191         return apdu;
1192     Z_SearchRequest *sr = apdu->u.searchRequest;
1193     Yaz_Z_Query *this_query = new Yaz_Z_Query;
1194     Yaz_Z_Databases this_databases;
1195
1196     this_databases.set(sr->num_databaseNames, (const char **)
1197                        sr->databaseNames);
1198     
1199     this_query->set_Z_Query(sr->query);
1200
1201     char query_str[120];
1202     this_query->print(query_str, sizeof(query_str)-1);
1203     yaz_log(LOG_LOG, "%sSearch %s", m_session_str, query_str);
1204
1205     if (*m_parent->m_optimize != '0' &&
1206         m_client->m_last_ok && m_client->m_last_query &&
1207         m_client->m_last_query->match(this_query) &&
1208         !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
1209         m_client->m_last_databases.match(this_databases))
1210     {
1211         delete this_query;
1212         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
1213             m_client->m_last_resultCount < *sr->largeSetLowerBound)
1214         {
1215             Z_NamePlusRecordList *npr;
1216             int toget = *sr->mediumSetPresentNumber;
1217             Z_RecordComposition *comp = 0;
1218
1219             if (toget > m_client->m_last_resultCount)
1220                 toget = m_client->m_last_resultCount;
1221             
1222             if (sr->mediumSetElementSetNames)
1223             {
1224                 comp = (Z_RecordComposition *)
1225                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1226                 comp->which = Z_RecordComp_simple;
1227                 comp->u.simple = sr->mediumSetElementSetNames;
1228             }
1229  
1230             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1231                                           sr->preferredRecordSyntax, comp))
1232             {
1233                 yaz_log (LOG_LOG, "%sReturned cached records for medium set",
1234                          m_session_str);
1235                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1236                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1237                 new_apdu->u.searchResponse->resultCount =
1238                     &m_client->m_last_resultCount;
1239                 
1240                 new_apdu->u.searchResponse->numberOfRecordsReturned
1241                     = odr_intdup(odr_encode(), toget);
1242                                                         
1243                 new_apdu->u.searchResponse->presentStatus =
1244                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1245                 new_apdu->u.searchResponse->records = (Z_Records*)
1246                     odr_malloc(odr_encode(), sizeof(Z_Records));
1247                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1248                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1249                 new_apdu->u.searchResponse->nextResultSetPosition =
1250                     odr_intdup(odr_encode(), toget+1);
1251                 send_to_client(new_apdu);
1252                 return 0;
1253             }
1254             else
1255             {
1256                 // medium Set
1257                 // send present request (medium size)
1258                 yaz_log (LOG_LOG, "%sOptimizing search for medium set",
1259                          m_session_str);
1260
1261                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1262                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1263                 pr->referenceId = sr->referenceId;
1264                 pr->resultSetId = sr->resultSetName;
1265                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1266                 *pr->numberOfRecordsRequested = toget;
1267                 pr->recordComposition = comp;
1268                 m_client->m_sr_transform = 1;
1269                 return new_apdu;
1270             }
1271         }
1272         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
1273             m_client->m_last_resultCount <= 0)
1274         {
1275             // large set. Return pseudo-search response immediately
1276             yaz_log (LOG_LOG, "%sOptimizing search for large set",
1277                      m_session_str);
1278             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1279             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1280             new_apdu->u.searchResponse->resultCount =
1281                 &m_client->m_last_resultCount;
1282             send_to_client(new_apdu);
1283             return 0;
1284         }
1285         else
1286         {
1287             Z_NamePlusRecordList *npr;
1288             int toget = m_client->m_last_resultCount;
1289             Z_RecordComposition *comp = 0;
1290             // small set
1291             // send a present request (small set)
1292             
1293             if (sr->smallSetElementSetNames)
1294             {
1295                 comp = (Z_RecordComposition *)
1296                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1297                 comp->which = Z_RecordComp_simple;
1298                 comp->u.simple = sr->smallSetElementSetNames;
1299             }
1300
1301             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1302                                           sr->preferredRecordSyntax, comp))
1303             {
1304                 yaz_log (LOG_LOG, "%sReturned cached records for small set",
1305                          m_session_str);
1306                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1307                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1308                 new_apdu->u.searchResponse->resultCount =
1309                     &m_client->m_last_resultCount;
1310                 
1311                 new_apdu->u.searchResponse->numberOfRecordsReturned
1312                     = odr_intdup(odr_encode(), toget);
1313                                                                  
1314                 new_apdu->u.searchResponse->presentStatus =
1315                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1316                 new_apdu->u.searchResponse->records = (Z_Records*)
1317                     odr_malloc(odr_encode(), sizeof(Z_Records));
1318                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1319                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1320                 new_apdu->u.searchResponse->nextResultSetPosition =
1321                     odr_intdup(odr_encode(), toget+1);
1322                 send_to_client(new_apdu);
1323                 return 0;
1324             }
1325             else
1326             {
1327                 yaz_log (LOG_LOG, "%sOptimizing search for small set",
1328                          m_session_str);
1329                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1330                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1331                 pr->referenceId = sr->referenceId;
1332                 pr->resultSetId = sr->resultSetName;
1333                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1334                 *pr->numberOfRecordsRequested = toget;
1335                 pr->recordComposition = comp;
1336                 m_client->m_sr_transform = 1;
1337                 return new_apdu;
1338             }
1339         }
1340     }
1341     else  // query doesn't match
1342     {
1343         delete m_client->m_last_query;
1344         m_client->m_last_query = this_query;
1345         m_client->m_last_ok = 0;
1346         m_client->m_cache.clear();
1347         m_client->m_resultSetStartPoint = 0;
1348
1349         xfree (m_client->m_last_resultSetId);
1350         m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
1351
1352         m_client->m_last_databases.set(sr->num_databaseNames,
1353                                        (const char **) sr->databaseNames);
1354     }
1355     return apdu;
1356 }
1357
1358
1359 void Yaz_Proxy::inc_request_no()
1360 {
1361     char *cp = strchr(m_session_str, ' ');
1362     m_request_no++;
1363     if (cp)
1364         sprintf(cp+1, "%d ", m_request_no);
1365 }
1366
1367 void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
1368 {
1369     inc_request_no();
1370
1371     m_bytes_recv += len;
1372     
1373     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1374         yaz_log (LOG_LOG, "%sReceiving %s from client %d bytes",
1375                  m_session_str, gdu_name(apdu), len);
1376
1377     if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
1378         shutdown();
1379
1380     m_bw_stat.add_bytes(len);
1381     m_pdu_stat.add_bytes(1);
1382
1383     gettimeofday(&m_time_tv, 0);
1384
1385     int bw_total = m_bw_stat.get_total();
1386     int pdu_total = m_pdu_stat.get_total();
1387
1388     int reduce = 0;
1389     if (m_bw_max)
1390     {
1391         if (bw_total > m_bw_max)
1392         {
1393             reduce = (bw_total/m_bw_max);
1394         }
1395     }
1396     if (m_pdu_max)
1397     {
1398         if (pdu_total > m_pdu_max)
1399         {
1400             int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
1401             reduce = (reduce > nreduce) ? reduce : nreduce;
1402         }
1403     }
1404     if (reduce)  
1405     {
1406         yaz_log(LOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1407                 m_session_str, reduce, bw_total, pdu_total,
1408                 m_bw_max, m_pdu_max);
1409         
1410         m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
1411         timeout(reduce);       // call us reduce seconds later
1412     }
1413     else if (apdu->which == Z_GDU_Z3950)
1414         handle_incoming_Z_PDU(apdu->u.z3950);
1415     else if (apdu->which == Z_GDU_HTTP_Request)
1416         handle_incoming_HTTP(apdu->u.HTTP_Request);
1417 }
1418
1419 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
1420 {
1421     if (m_max_record_retrieve)
1422     {
1423         if (apdu->which == Z_APDU_presentRequest)
1424         {
1425             Z_PresentRequest *pr = apdu->u.presentRequest;
1426             if (pr->numberOfRecordsRequested && 
1427                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
1428                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
1429         }
1430     }
1431 }
1432
1433 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
1434                                                      int error,
1435                                                      const char *addinfo)
1436 {
1437     Z_Records *rec = (Z_Records *)
1438         odr_malloc (odr, sizeof(*rec));
1439     int *err = (int *)
1440         odr_malloc (odr, sizeof(*err));
1441     Z_DiagRec *drec = (Z_DiagRec *)
1442         odr_malloc (odr, sizeof(*drec));
1443     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1444         odr_malloc (odr, sizeof(*dr));
1445     *err = error;
1446     rec->which = Z_Records_NSD;
1447     rec->u.nonSurrogateDiagnostic = dr;
1448     dr->diagnosticSetId =
1449         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
1450     dr->condition = err;
1451     dr->which = Z_DefaultDiagFormat_v2Addinfo;
1452     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
1453     return rec;
1454 }
1455
1456 Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
1457 {
1458     if (apdu->which == Z_APDU_searchRequest &&
1459         apdu->u.searchRequest->query &&
1460         apdu->u.searchRequest->query->which == Z_Query_type_104 &&
1461         apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
1462     {
1463         Z_RPNQuery *rpnquery = 0;
1464         Z_SearchRequest *sr = apdu->u.searchRequest;
1465         char *addinfo = 0;
1466         
1467         yaz_log(LOG_LOG, "%sCQL: %s", m_session_str,
1468                 sr->query->u.type_104->u.cql);
1469
1470         int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
1471                                           &rpnquery, odr_encode(),
1472                                           &addinfo);
1473         if (r == -3)
1474             yaz_log(LOG_LOG, "%sNo CQL to RPN table", m_session_str);
1475         else if (r)
1476         {
1477             yaz_log(LOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
1478             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1479
1480             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1481             new_apdu->u.searchResponse->records =
1482                 create_nonSurrogateDiagnostics(odr_encode(),
1483                                                yaz_diag_srw_to_bib1(r),
1484                                                addinfo);
1485             *new_apdu->u.searchResponse->searchStatus = 0;
1486
1487             send_to_client(new_apdu);
1488
1489             return 0;
1490         }
1491         else
1492         {
1493             sr->query->which = Z_Query_type_1;
1494             sr->query->u.type_1 = rpnquery;
1495         }
1496         return apdu;
1497     }
1498     return apdu;
1499 }
1500
1501 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
1502 {
1503     if (apdu->which == Z_APDU_searchRequest)
1504     {
1505         Z_SearchRequest *sr = apdu->u.searchRequest;
1506         int err = 0;
1507         char *addinfo = 0;
1508
1509         Yaz_ProxyConfig *cfg = check_reconfigure();
1510         if (cfg)
1511             err = cfg->check_query(odr_encode(), m_default_target,
1512                                    sr->query, &addinfo);
1513         if (err)
1514         {
1515             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1516
1517             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1518             new_apdu->u.searchResponse->records =
1519                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1520             *new_apdu->u.searchResponse->searchStatus = 0;
1521
1522             send_to_client(new_apdu);
1523
1524             return 0;
1525         }
1526     }
1527     return apdu;
1528 }
1529
1530 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
1531 {
1532     m_marcxml_flag = 0;
1533     if (apdu->which == Z_APDU_searchRequest)
1534     {
1535         Z_SearchRequest *sr = apdu->u.searchRequest;
1536         int err = 0;
1537         char *addinfo = 0;
1538         Yaz_ProxyConfig *cfg = check_reconfigure();
1539
1540         Z_RecordComposition rc_temp, *rc = 0;
1541         if (sr->smallSetElementSetNames)
1542         {
1543             rc_temp.which = Z_RecordComp_simple;
1544             rc_temp.u.simple = sr->smallSetElementSetNames;
1545             rc = &rc_temp;
1546         }
1547
1548         char *stylesheet_name = 0;
1549         if (cfg)
1550             err = cfg->check_syntax(odr_encode(),
1551                                     m_default_target,
1552                                     sr->preferredRecordSyntax, rc,
1553                                     &addinfo, &stylesheet_name, &m_schema);
1554         if (stylesheet_name)
1555         {
1556             m_parent->low_socket_close();
1557
1558             if (m_stylesheet_xsp)
1559                 xsltFreeStylesheet(m_stylesheet_xsp);
1560
1561             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1562                                                        stylesheet_name);
1563             m_stylesheet_offset = 0;
1564             xfree(stylesheet_name);
1565
1566             m_parent->low_socket_open();
1567         }
1568         if (err == -1)
1569         {
1570             sr->preferredRecordSyntax =
1571                 yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN, VAL_USMARC);
1572             m_marcxml_flag = 1;
1573         }
1574         else if (err)
1575         {
1576             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1577             
1578             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1579             new_apdu->u.searchResponse->records =
1580                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1581             *new_apdu->u.searchResponse->searchStatus = 0;
1582             
1583             send_to_client(new_apdu);
1584             
1585             return 0;
1586         }
1587     }
1588     else if (apdu->which == Z_APDU_presentRequest)
1589     {
1590         Z_PresentRequest *pr = apdu->u.presentRequest;
1591         int err = 0;
1592         char *addinfo = 0;
1593         Yaz_ProxyConfig *cfg = check_reconfigure();
1594
1595         char *stylesheet_name = 0;
1596         if (cfg)
1597             err = cfg->check_syntax(odr_encode(), m_default_target,
1598                                     pr->preferredRecordSyntax,
1599                                     pr->recordComposition,
1600                                     &addinfo, &stylesheet_name, &m_schema);
1601         if (stylesheet_name)
1602         {
1603             m_parent->low_socket_close();
1604
1605             if (m_stylesheet_xsp)
1606                 xsltFreeStylesheet(m_stylesheet_xsp);
1607
1608             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
1609                                                        stylesheet_name);
1610             m_stylesheet_offset = 0;
1611             xfree(stylesheet_name);
1612
1613             m_parent->low_socket_open();
1614         }
1615         if (err == -1)
1616         {
1617             pr->preferredRecordSyntax =
1618                 yaz_oidval_to_z3950oid(odr_decode(), CLASS_RECSYN, VAL_USMARC);
1619             m_marcxml_flag = 1;
1620         }
1621         else if (err)
1622         {
1623             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1624             
1625             new_apdu->u.presentResponse->referenceId = pr->referenceId;
1626             new_apdu->u.presentResponse->records =
1627                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
1628             *new_apdu->u.presentResponse->presentStatus =
1629                 Z_PresentStatus_failure;
1630             
1631             send_to_client(new_apdu);
1632             
1633             return 0;
1634         }
1635     }
1636     return apdu;
1637 }
1638
1639 Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
1640 {
1641     if (!schema)
1642         return 0;
1643     Z_ElementSetNames *esn = (Z_ElementSetNames *)
1644         odr_malloc(o, sizeof(Z_ElementSetNames));
1645     esn->which = Z_ElementSetNames_generic;
1646     esn->u.generic = odr_strdup(o, schema);
1647     return esn;
1648 }
1649
1650 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
1651 {
1652     if (m_s2z_odr_init)
1653     {
1654         odr_destroy(m_s2z_odr_init);
1655         m_s2z_odr_init = 0;
1656     }
1657     if (m_s2z_odr_search)
1658     {
1659         odr_destroy(m_s2z_odr_search);
1660         m_s2z_odr_search = 0;
1661     }
1662
1663     m_http_keepalive = 0;
1664     m_http_version = 0;
1665     if (!strcmp(hreq->version, "1.0")) 
1666     {
1667         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1668         if (v && !strcmp(v, "Keep-Alive"))
1669             m_http_keepalive = 1;
1670         else
1671             m_http_keepalive = 0;
1672         m_http_version = "1.0";
1673     }
1674     else
1675     {
1676         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
1677         if (v && !strcmp(v, "close"))
1678             m_http_keepalive = 0;
1679         else
1680             m_http_keepalive = 1;
1681         m_http_version = "1.1";
1682     }
1683
1684     Z_SRW_PDU *srw_pdu = 0;
1685     Z_SOAP *soap_package = 0;
1686     char *charset = 0;
1687     Z_SRW_diagnostic *diagnostic = 0;
1688     int num_diagnostic = 0;
1689     if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
1690                        &charset) == 0
1691         || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
1692                           &charset, &diagnostic, &num_diagnostic) == 0)
1693     {
1694         m_s2z_odr_init = odr_createmem(ODR_ENCODE);
1695         m_s2z_odr_search = odr_createmem(ODR_ENCODE);
1696         m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
1697         m_s2z_init_apdu = 0;
1698         m_s2z_search_apdu = 0;
1699         m_s2z_present_apdu = 0;
1700
1701         m_s2z_stylesheet = 0;
1702         
1703         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
1704         {
1705             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
1706
1707             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
1708             // recordXPath unsupported.
1709             if (srw_req->recordXPath)
1710             {
1711                 yaz_add_srw_diagnostic(odr_decode(),
1712                                        &diagnostic, &num_diagnostic,
1713                                        72, 0);
1714             }
1715             // must have a query
1716             if (!srw_req->query.cql)
1717             {
1718                 yaz_add_srw_diagnostic(odr_decode(),
1719                                        &diagnostic, &num_diagnostic,
1720                                        7, "query");
1721             }
1722             // sort unsupported
1723             if (srw_req->sort_type != Z_SRW_sort_type_none)
1724             {
1725                 yaz_add_srw_diagnostic(odr_decode(),
1726                                        &diagnostic, &num_diagnostic,
1727                                        80, 0);
1728             }
1729             // save stylesheet
1730             if (srw_req->stylesheet)
1731                 m_s2z_stylesheet =
1732                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
1733                                               
1734             // set packing for response records ..
1735             if (srw_req->recordPacking &&
1736                 !strcmp(srw_req->recordPacking, "xml"))
1737                 m_s2z_packing = Z_SRW_recordPacking_XML;
1738             else
1739                 m_s2z_packing = Z_SRW_recordPacking_string;
1740
1741             if (num_diagnostic)
1742             {
1743                 Z_SRW_PDU *srw_pdu =
1744                     yaz_srw_get(odr_encode(),
1745                                 Z_SRW_searchRetrieve_response);
1746                 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1747                 
1748                 srw_res->diagnostics = diagnostic;
1749                 srw_res->num_diagnostics = num_diagnostic;
1750                 send_srw_response(srw_pdu);
1751                 return;
1752             }
1753
1754             // prepare search PDU
1755             m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
1756                                           Z_APDU_searchRequest);
1757             Z_SearchRequest *z_searchRequest =
1758                 m_s2z_search_apdu->u.searchRequest;
1759
1760             z_searchRequest->num_databaseNames = 1;
1761             z_searchRequest->databaseNames = (char**)
1762                 odr_malloc(m_s2z_odr_search, sizeof(char *));
1763             z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
1764                                                            srw_req->database);
1765             
1766             // query transformation
1767             Z_Query *query = (Z_Query *)
1768                 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
1769             z_searchRequest->query = query;
1770             
1771             if (srw_req->query_type == Z_SRW_query_type_cql)
1772             {
1773                 Z_External *ext = (Z_External *) 
1774                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
1775                 ext->direct_reference = 
1776                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
1777                 ext->indirect_reference = 0;
1778                 ext->descriptor = 0;
1779                 ext->which = Z_External_CQL;
1780                 ext->u.cql = srw_req->query.cql;
1781                 
1782                 query->which = Z_Query_type_104;
1783                 query->u.type_104 =  ext;
1784             }
1785             else if (srw_req->query_type == Z_SRW_query_type_pqf)
1786             {
1787                 Z_RPNQuery *RPNquery;
1788                 YAZ_PQF_Parser pqf_parser;
1789                 
1790                 pqf_parser = yaz_pqf_create ();
1791                 
1792                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
1793                                           srw_req->query.pqf);
1794                 if (!RPNquery)
1795                 {
1796                     const char *pqf_msg;
1797                     size_t off;
1798                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1799                     yaz_log(LOG_LOG, "%*s^\n", off+4, "");
1800                     yaz_log(LOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
1801                     
1802                     send_to_srw_client_error(10, 0);
1803                     return;
1804                 }
1805                 query->which = Z_Query_type_1;
1806                 query->u.type_1 =  RPNquery;
1807                 
1808                 yaz_pqf_destroy (pqf_parser);
1809             }
1810             else
1811             {
1812                 send_to_srw_client_error(7, "query");
1813                 return;
1814             }
1815
1816             // present
1817             m_s2z_present_apdu = 0;
1818             int max = 0;
1819             if (srw_req->maximumRecords)
1820                 max = *srw_req->maximumRecords;
1821             int start = 1;
1822             if (srw_req->startRecord)
1823                 start = *srw_req->startRecord;
1824             if (max > 0)
1825             {
1826                 // Some backend, such as Voyager doesn't honor piggyback
1827                 // So we use present always (0 &&).
1828                 if (0 && start <= 1)  // Z39.50 piggyback
1829                 {
1830                     *z_searchRequest->smallSetUpperBound = max;
1831                     *z_searchRequest->mediumSetPresentNumber = max;
1832                     *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
1833
1834                     z_searchRequest->preferredRecordSyntax =
1835                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
1836                                                VAL_TEXT_XML);
1837                     if (srw_req->recordSchema)
1838                     {
1839                         z_searchRequest->smallSetElementSetNames =
1840                             z_searchRequest->mediumSetElementSetNames =
1841                             mk_esn_from_schema(m_s2z_odr_search,
1842                                                srw_req->recordSchema);
1843                     }
1844                 }
1845                 else   // Z39.50 present
1846                 {
1847                     m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
1848                                                    Z_APDU_presentRequest);
1849                     Z_PresentRequest *z_presentRequest = 
1850                         m_s2z_present_apdu->u.presentRequest;
1851                     *z_presentRequest->resultSetStartPoint = start;
1852                     *z_presentRequest->numberOfRecordsRequested = max;
1853                     z_presentRequest->preferredRecordSyntax =
1854                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
1855                                                VAL_TEXT_XML);
1856                     if (srw_req->recordSchema)
1857                     {
1858                         z_presentRequest->recordComposition =
1859                             (Z_RecordComposition *)
1860                             odr_malloc(m_s2z_odr_search,
1861                                        sizeof(Z_RecordComposition));
1862                         z_presentRequest->recordComposition->which = 
1863                             Z_RecordComp_simple;                    
1864                         z_presentRequest->recordComposition->u.simple =
1865                             mk_esn_from_schema(m_s2z_odr_search,
1866                                                srw_req->recordSchema);
1867                     }
1868                 }
1869             }
1870             if (!m_client)
1871             {
1872                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
1873                                             Z_APDU_initRequest);
1874                 
1875                 // prevent m_initRequest_apdu memory from being grabbed
1876                 // in Yaz_Proxy::handle_incoming_Z_PDU
1877                 m_initRequest_apdu = m_s2z_init_apdu;
1878                 handle_incoming_Z_PDU(m_s2z_init_apdu);
1879                 return;
1880             }
1881             else
1882             {
1883                 handle_incoming_Z_PDU(m_s2z_search_apdu);
1884                 return;
1885             }
1886         }
1887         else if (srw_pdu->which == Z_SRW_explain_request)
1888         {
1889             Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
1890
1891             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
1892
1893             // save stylesheet
1894             if (srw_req->stylesheet)
1895                 m_s2z_stylesheet =
1896                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
1897
1898             if (srw_req->recordPacking &&
1899                 !strcmp(srw_req->recordPacking, "xml"))
1900                 m_s2z_packing = Z_SRW_recordPacking_XML;
1901             else
1902                 m_s2z_packing = Z_SRW_recordPacking_string;
1903
1904             if (num_diagnostic)
1905             {
1906                 send_srw_explain_response(diagnostic, num_diagnostic);
1907                 return;
1908             }
1909
1910             if (!m_client)
1911             {
1912                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
1913                                             Z_APDU_initRequest);
1914                 
1915                 // prevent m_initRequest_apdu memory from being grabbed
1916                 // in Yaz_Proxy::handle_incoming_Z_PDU
1917                 m_initRequest_apdu = m_s2z_init_apdu;
1918                 handle_incoming_Z_PDU(m_s2z_init_apdu);
1919             }
1920             else
1921                 send_srw_explain_response(0, 0);
1922             return;
1923         }
1924         else if (srw_pdu->which == Z_SRW_scan_request)
1925         {
1926             m_s2z_database = odr_strdup(m_s2z_odr_init,
1927                                         srw_pdu->u.scan_request->database);
1928
1929             yaz_add_srw_diagnostic(odr_decode(),
1930                                    &diagnostic, &num_diagnostic,
1931                                    4, "scan");
1932             Z_SRW_PDU *srw_pdu =
1933                 yaz_srw_get(odr_encode(),
1934                             Z_SRW_scan_response);
1935             Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
1936             
1937             srw_res->diagnostics = diagnostic;
1938             srw_res->num_diagnostics = num_diagnostic;
1939             send_srw_response(srw_pdu);
1940             return;
1941         }
1942         else
1943         {
1944             m_s2z_database = 0;
1945
1946             send_to_srw_client_error(4, 0);
1947         }
1948     }
1949     int len = 0;
1950     Z_GDU *p = z_get_HTTP_Response(odr_encode(), 400);
1951     timeout(0);
1952     send_GDU(p, &len);
1953 }
1954
1955 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
1956 {
1957     Z_ReferenceId **refid = get_referenceIdP(apdu);
1958     nmem_reset(m_referenceId_mem);
1959     if (refid && *refid)
1960     {
1961         m_referenceId = (Z_ReferenceId *)
1962             nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
1963         m_referenceId->len = m_referenceId->size = (*refid)->len;
1964         m_referenceId->buf = (unsigned char *)
1965             nmem_malloc(m_referenceId_mem, (*refid)->len);
1966         memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
1967     }
1968     else
1969         m_referenceId = 0;
1970
1971     if (!m_client && m_invalid_session)
1972     {
1973         m_apdu_invalid_session = apdu;
1974         m_mem_invalid_session = odr_extract_mem(odr_decode());
1975         apdu = m_initRequest_apdu;
1976     }
1977     
1978     // Determine our client.
1979     Z_OtherInformation **oi;
1980     get_otherInfoAPDU(apdu, &oi);
1981     m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
1982     if (!m_client)
1983     {
1984         delete this;
1985         return;
1986     }
1987     m_client->m_server = this;
1988
1989     if (apdu->which == Z_APDU_initRequest)
1990     {
1991         if (apdu->u.initRequest->implementationId)
1992             yaz_log(LOG_LOG, "%simplementationId: %s",
1993                     m_session_str, apdu->u.initRequest->implementationId);
1994         if (apdu->u.initRequest->implementationName)
1995             yaz_log(LOG_LOG, "%simplementationName: %s",
1996                     m_session_str, apdu->u.initRequest->implementationName);
1997         if (apdu->u.initRequest->implementationVersion)
1998             yaz_log(LOG_LOG, "%simplementationVersion: %s",
1999                     m_session_str, apdu->u.initRequest->implementationVersion);
2000         if (m_initRequest_apdu == 0)
2001         {
2002             if (m_initRequest_mem)
2003                 nmem_destroy(m_initRequest_mem);
2004             m_initRequest_apdu = apdu;
2005             m_initRequest_mem = odr_extract_mem(odr_decode());
2006
2007             m_initRequest_preferredMessageSize = *apdu->u.initRequest->
2008                 preferredMessageSize;
2009             *apdu->u.initRequest->preferredMessageSize = 1024*1024;
2010             m_initRequest_maximumRecordSize = *apdu->u.initRequest->
2011                 maximumRecordSize;
2012             *apdu->u.initRequest->maximumRecordSize = 1024*1024;
2013
2014             // save init options for the response..
2015             m_initRequest_options = apdu->u.initRequest->options;
2016             
2017             apdu->u.initRequest->options = 
2018                 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2019                                            sizeof(Odr_bitmask));
2020             ODR_MASK_ZERO(apdu->u.initRequest->options);
2021             int i;
2022             for (i = 0; i<= 24; i++)
2023                 ODR_MASK_SET(apdu->u.initRequest->options, i);
2024             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2025                            Z_Options_negotiationModel);
2026             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2027                            Z_Options_concurrentOperations);
2028
2029             // make new version
2030             m_initRequest_version = apdu->u.initRequest->protocolVersion;
2031             apdu->u.initRequest->protocolVersion = 
2032                 (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2033                                            sizeof(Odr_bitmask));
2034             ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
2035
2036             for (i = 0; i<= 8; i++)
2037                 ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
2038         }
2039         if (m_client->m_init_flag)
2040         {
2041             if (handle_init_response_for_invalid_session(apdu))
2042                 return;
2043             if (m_client->m_initResponse)
2044             {
2045                 Z_APDU *apdu2 = m_client->m_initResponse;
2046                 apdu2->u.initResponse->otherInfo = 0;
2047                 if (m_client->m_cookie && *m_client->m_cookie)
2048                     set_otherInformationString(apdu2, VAL_COOKIE, 1,
2049                                                m_client->m_cookie);
2050                 apdu2->u.initResponse->referenceId =
2051                     apdu->u.initRequest->referenceId;
2052                 apdu2->u.initResponse->options = m_client->m_initResponse_options;
2053                 apdu2->u.initResponse->protocolVersion = 
2054                     m_client->m_initResponse_version;
2055                 
2056                 send_to_client(apdu2);
2057                 return;
2058             }
2059         }
2060         m_client->m_init_flag = 1;
2061     }
2062     handle_max_record_retrieve(apdu);
2063
2064     if (apdu)
2065         apdu = handle_syntax_validation(apdu);
2066
2067     if (apdu)
2068         apdu = handle_query_transformation(apdu);
2069
2070     if (apdu)
2071         apdu = handle_query_validation(apdu);
2072
2073     if (apdu)
2074         apdu = result_set_optimize(apdu);
2075     if (!apdu)
2076     {
2077         m_client->timeout(m_target_idletime);  // mark it active even 
2078         // though we didn't use it
2079         return;
2080     }
2081
2082     // delete other info part from PDU before sending to target
2083     get_otherInfoAPDU(apdu, &oi);
2084     if (oi)
2085         *oi = 0;
2086
2087     if (apdu->which == Z_APDU_presentRequest &&
2088         m_client->m_resultSetStartPoint == 0)
2089     {
2090         Z_PresentRequest *pr = apdu->u.presentRequest;
2091         m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
2092         m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
2093     } else {
2094         m_client->m_resultSetStartPoint = 0;
2095     }
2096     if (m_client->send_to_target(apdu) < 0)
2097     {
2098         delete m_client;
2099         m_client = 0;
2100         delete this;
2101     }
2102     else
2103         m_client->m_waiting = 1;
2104 }
2105
2106 void Yaz_Proxy::connectNotify()
2107 {
2108 }
2109
2110 void Yaz_Proxy::shutdown()
2111 {
2112     m_invalid_session = 0;
2113     // only keep if keep_alive flag is set...
2114     if (m_client && 
2115         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
2116         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
2117         m_client->m_waiting == 0)
2118     {
2119         yaz_log(LOG_LOG, "%sShutdown (client to proxy) keepalive %s",
2120                  m_session_str,
2121                  m_client->get_hostname());
2122         yaz_log(LOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
2123                 m_session_str, m_client->m_pdu_recv,
2124                 m_client->m_bytes_sent + m_client->m_bytes_recv,
2125                 m_keepalive_limit_bw, m_keepalive_limit_pdu);
2126         assert (m_client->m_waiting != 2);
2127         // Tell client (if any) that no server connection is there..
2128         m_client->m_server = 0;
2129         m_invalid_session = 0;
2130     }
2131     else if (m_client)
2132     {
2133         yaz_log (LOG_LOG, "%sShutdown (client to proxy) close %s",
2134                  m_session_str,
2135                  m_client->get_hostname());
2136         assert (m_client->m_waiting != 2);
2137         delete m_client;
2138     }
2139     else if (!m_parent)
2140     {
2141         yaz_log (LOG_LOG, "%sshutdown (client to proxy) bad state",
2142                  m_session_str);
2143         assert (m_parent);
2144     }
2145     else 
2146     {
2147         yaz_log (LOG_LOG, "%sShutdown (client to proxy)",
2148                  m_session_str);
2149     }
2150     if (m_parent)
2151         m_parent->pre_init();
2152     delete this;
2153 }
2154
2155 const char *Yaz_ProxyClient::get_session_str() 
2156 {
2157     if (!m_server)
2158         return "0 ";
2159     return m_server->get_session_str();
2160 }
2161
2162 void Yaz_ProxyClient::shutdown()
2163 {
2164     yaz_log (LOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
2165              get_hostname());
2166     delete m_server;
2167     delete this;
2168 }
2169
2170 void Yaz_Proxy::failNotify()
2171 {
2172     inc_request_no();
2173     yaz_log (LOG_LOG, "%sConnection closed by client",
2174              get_session_str());
2175     shutdown();
2176 }
2177
2178 void Yaz_ProxyClient::failNotify()
2179 {
2180     if (m_server)
2181         m_server->inc_request_no();
2182     yaz_log (LOG_LOG, "%sConnection closed by target %s", 
2183              get_session_str(), get_hostname());
2184     shutdown();
2185 }
2186
2187 void Yaz_ProxyClient::connectNotify()
2188 {
2189     const char *s = get_session_str();
2190     const char *h = get_hostname();
2191     yaz_log (LOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
2192              m_target_idletime);
2193     timeout(m_target_idletime);
2194     if (!m_server)
2195         pre_init_client();
2196 }
2197
2198 IYaz_PDU_Observer *Yaz_ProxyClient::sessionNotify(IYaz_PDU_Observable
2199                                                   *the_PDU_Observable, int fd)
2200 {
2201     return new Yaz_ProxyClient(the_PDU_Observable, 0);
2202 }
2203
2204 Yaz_ProxyClient::~Yaz_ProxyClient()
2205 {
2206     if (m_prev)
2207         *m_prev = m_next;
2208     if (m_next)
2209         m_next->m_prev = m_prev;
2210     m_waiting = 2;     // for debugging purposes only.
2211     odr_destroy(m_init_odr);
2212     delete m_last_query;
2213     xfree (m_last_resultSetId);
2214     xfree (m_cookie);
2215 }
2216
2217 void Yaz_ProxyClient::pre_init_client()
2218 {
2219     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
2220     Z_InitRequest *req = apdu->u.initRequest;
2221     
2222     int i;
2223     for (i = 0; i<= 24; i++)
2224         ODR_MASK_SET(req->options, i);
2225     ODR_MASK_CLEAR(apdu->u.initRequest->options,
2226                    Z_Options_negotiationModel);
2227     ODR_MASK_CLEAR(apdu->u.initRequest->options,
2228                    Z_Options_concurrentOperations);
2229     for (i = 0; i<= 10; i++)
2230         ODR_MASK_SET(req->protocolVersion, i);
2231
2232     if (send_to_target(apdu) < 0)
2233     {
2234         delete this;
2235     }
2236     else
2237     {
2238         m_waiting = 1;
2239         m_init_flag = 1;
2240     }
2241 }
2242
2243 void Yaz_Proxy::pre_init()
2244 {
2245     int i;
2246     const char *name = 0;
2247     const char *zurl_in_use[MAX_ZURL_PLEX];
2248     int limit_bw, limit_pdu, limit_req;
2249     int target_idletime, client_idletime;
2250     int max_clients;
2251     int keepalive_limit_bw, keepalive_limit_pdu;
2252     int pre_init;
2253     const char *cql2rpn = 0;
2254
2255     Yaz_ProxyConfig *cfg = check_reconfigure();
2256
2257     zurl_in_use[0] = 0;
2258
2259     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
2260         set_APDU_yazlog(1);
2261     else
2262         set_APDU_yazlog(0);
2263
2264     for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
2265                                           &limit_bw, &limit_pdu, &limit_req,
2266                                           &target_idletime, &client_idletime,
2267                                           &max_clients, 
2268                                           &keepalive_limit_bw,
2269                                           &keepalive_limit_pdu,
2270                                           &pre_init,
2271                                           &cql2rpn) ; i++)
2272     {
2273         if (pre_init)
2274         {
2275             int j;
2276             for (j = 0; zurl_in_use[j]; j++)
2277             {
2278                 Yaz_ProxyClient *c;
2279                 int spare = 0;
2280                 int spare_waiting = 0;
2281                 int in_use = 0;
2282                 int other = 0;
2283                 for (c = m_clientPool; c; c = c->m_next)
2284                 {
2285                     if (!strcmp(zurl_in_use[j], c->get_hostname()))
2286                     {
2287                         if (c->m_cookie == 0)
2288                         {
2289                             if (c->m_server == 0)
2290                                 if (c->m_waiting)
2291                                     spare_waiting++;
2292                                 else
2293                                     spare++;
2294                             else
2295                                 in_use++;
2296                         }
2297                         else
2298                             other++;
2299                     }
2300                 }
2301                 yaz_log(LOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
2302                         "sparew=%d preinit=%d",m_session_str,
2303                         name, zurl_in_use[j], in_use, other,
2304                         spare, spare_waiting, pre_init);
2305                 if (spare + spare_waiting < pre_init)
2306                 {
2307                     c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
2308                     c->m_next = m_clientPool;
2309                     if (c->m_next)
2310                         c->m_next->m_prev = &c->m_next;
2311                     m_clientPool = c;
2312                     c->m_prev = &m_clientPool;
2313                     
2314                     if (m_log_mask & PROXY_LOG_APDU_SERVER)
2315                         c->set_APDU_yazlog(1);
2316                     else
2317                         c->set_APDU_yazlog(0);
2318
2319                     if (c->client(zurl_in_use[j]))
2320                     {
2321                         timeout(60);
2322                         delete c;
2323                         return;
2324                     }
2325                     c->timeout(30);
2326                     c->m_waiting = 1;
2327                     c->m_target_idletime = target_idletime;
2328                     c->m_seqno = m_seqno++;
2329                 }
2330             }
2331         }
2332     }
2333 }
2334
2335 void Yaz_Proxy::timeoutNotify()
2336 {
2337     if (m_parent)
2338     {
2339         if (m_bw_hold_PDU)
2340         {
2341             timeout(m_client_idletime);
2342             Z_GDU *apdu = m_bw_hold_PDU;
2343             m_bw_hold_PDU = 0;
2344             
2345             if (apdu->which == Z_GDU_Z3950)
2346                 handle_incoming_Z_PDU(apdu->u.z3950);
2347             else if (apdu->which == Z_GDU_HTTP_Request)
2348                 handle_incoming_HTTP(apdu->u.HTTP_Request);
2349         }
2350         else if (m_stylesheet_nprl)
2351             convert_xsl_delay();
2352         else
2353         {
2354             inc_request_no();
2355
2356             yaz_log (LOG_LOG, "%sTimeout (client to proxy)", m_session_str);
2357             shutdown();
2358         }
2359     }
2360     else
2361     {
2362         timeout(600);
2363         pre_init();
2364     }
2365 }
2366
2367 void Yaz_Proxy::markInvalid()
2368 {
2369     m_client = 0;
2370     m_invalid_session = 1;
2371 }
2372
2373 void Yaz_ProxyClient::timeoutNotify()
2374 {
2375     if (m_server)
2376         m_server->inc_request_no();
2377
2378     yaz_log (LOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
2379              get_hostname());
2380     m_waiting = 1;
2381     m_root->pre_init();
2382     if (m_server && m_init_flag)
2383     {
2384         // target timed out in a session that was properly initialized
2385         // server object stay alive but we mark it as invalid so it
2386         // gets initialized again
2387         m_server->markInvalid();
2388         m_server = 0;
2389     }
2390     shutdown();
2391 }
2392
2393 Yaz_ProxyClient::Yaz_ProxyClient(IYaz_PDU_Observable *the_PDU_Observable,
2394                                  Yaz_Proxy *parent) :
2395     Yaz_Z_Assoc (the_PDU_Observable)
2396 {
2397     m_cookie = 0;
2398     m_next = 0;
2399     m_prev = 0;
2400     m_init_flag = 0;
2401     m_last_query = 0;
2402     m_last_resultSetId = 0;
2403     m_last_resultCount = 0;
2404     m_last_ok = 0;
2405     m_sr_transform = 0;
2406     m_waiting = 0;
2407     m_init_odr = odr_createmem (ODR_DECODE);
2408     m_initResponse = 0;
2409     m_initResponse_options = 0;
2410     m_initResponse_version = 0;
2411     m_initResponse_preferredMessageSize = 0;
2412     m_initResponse_maximumRecordSize = 0;
2413     m_resultSetStartPoint = 0;
2414     m_bytes_sent = m_bytes_recv = 0;
2415     m_pdu_recv = 0;
2416     m_server = 0;
2417     m_seqno = 0;
2418     m_target_idletime = 600;
2419     m_root = parent;
2420 }
2421
2422 const char *Yaz_Proxy::option(const char *name, const char *value)
2423 {
2424     if (!strcmp (name, "optimize")) {
2425         if (value) {
2426             xfree (m_optimize); 
2427             m_optimize = xstrdup (value);
2428         }
2429         return m_optimize;
2430     }
2431     return 0;
2432 }
2433
2434 void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
2435 {
2436
2437 }
2438
2439 void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
2440 {
2441     if (apdu->which == Z_GDU_Z3950)
2442         recv_Z_PDU(apdu->u.z3950, len);
2443     else if (apdu->which == Z_GDU_HTTP_Response)
2444         recv_HTTP_response(apdu->u.HTTP_Response, len);
2445     else
2446         shutdown();
2447 }
2448
2449 int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
2450 {
2451     if (!m_invalid_session)
2452         return 0;
2453     m_invalid_session = 0;
2454     handle_incoming_Z_PDU(m_apdu_invalid_session);
2455     assert (m_mem_invalid_session);
2456     nmem_destroy(m_mem_invalid_session);
2457     m_mem_invalid_session = 0;
2458     return 1;
2459 }
2460
2461 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
2462 {
2463     m_bytes_recv += len;
2464
2465     m_pdu_recv++;
2466     m_waiting = 0;
2467     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
2468         yaz_log (LOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
2469                  apdu_name(apdu), get_hostname(), len);
2470     if (apdu->which == Z_APDU_initResponse)
2471     {
2472         if (!m_server)  // if this is a pre init session , check for more
2473             m_root->pre_init();
2474         NMEM nmem = odr_extract_mem (odr_decode());
2475         odr_reset (m_init_odr);
2476         nmem_transfer (m_init_odr->mem, nmem);
2477         m_initResponse = apdu;
2478         m_initResponse_options = apdu->u.initResponse->options;
2479         m_initResponse_version = apdu->u.initResponse->protocolVersion;
2480         m_initResponse_preferredMessageSize = 
2481             *apdu->u.initResponse->preferredMessageSize;
2482         m_initResponse_maximumRecordSize = 
2483             *apdu->u.initResponse->maximumRecordSize;
2484
2485         Z_InitResponse *ir = apdu->u.initResponse;
2486         char *im0 = ir->implementationName;
2487         
2488         char *im1 = (char*) 
2489             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
2490         *im1 = '\0';
2491         if (im0)
2492         {
2493             strcat(im1, im0);
2494             strcat(im1, " ");
2495         }
2496         strcat(im1, "(YAZ Proxy)");
2497         ir->implementationName = im1;
2498
2499         nmem_destroy (nmem);
2500
2501         if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
2502             return;
2503     }
2504     if (apdu->which == Z_APDU_searchResponse)
2505     {
2506         Z_SearchResponse *sr = apdu->u.searchResponse;
2507         m_last_resultCount = *sr->resultCount;
2508         int status = *sr->searchStatus;
2509         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
2510         {
2511             m_last_ok = 1;
2512             
2513             if (sr->records && sr->records->which == Z_Records_DBOSD)
2514             {
2515                 m_cache.add(odr_decode(),
2516                             sr->records->u.databaseOrSurDiagnostics, 1,
2517                             *sr->resultCount);
2518             }
2519         }
2520     }
2521     if (apdu->which == Z_APDU_presentResponse)
2522     {
2523         Z_PresentResponse *pr = apdu->u.presentResponse;
2524         if (m_sr_transform)
2525         {
2526             m_sr_transform = 0;
2527             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2528             Z_SearchResponse *sr = new_apdu->u.searchResponse;
2529             sr->referenceId = pr->referenceId;
2530             *sr->resultCount = m_last_resultCount;
2531             sr->records = pr->records;
2532             sr->nextResultSetPosition = pr->nextResultSetPosition;
2533             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
2534             apdu = new_apdu;
2535         }
2536         if (pr->records && 
2537             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
2538         {
2539             m_cache.add(odr_decode(),
2540                         pr->records->u.databaseOrSurDiagnostics,
2541                         m_resultSetStartPoint, -1);
2542             m_resultSetStartPoint = 0;
2543         }
2544     }
2545     if (m_cookie)
2546         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
2547     if (m_server)
2548     {
2549         m_server->send_to_client(apdu);
2550     }
2551     if (apdu->which == Z_APDU_close)
2552     {
2553         shutdown();
2554     }
2555 }
2556
2557 void Yaz_Proxy::low_socket_close()
2558 {
2559     int i;
2560     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
2561         if  (m_lo_fd[i] >= 0)
2562             ::close(m_lo_fd[i]);
2563 }
2564
2565 void Yaz_Proxy::low_socket_open()
2566 {
2567     int i;
2568     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
2569         m_lo_fd[i] = open("/dev/null", O_RDONLY);
2570 }
2571
2572 int Yaz_Proxy::server(const char *addr)
2573 {
2574     int r = Yaz_Z_Assoc::server(addr);
2575     if (!r)
2576     {
2577         yaz_log(LOG_LOG, "%sStarted proxy " VERSION " on %s", m_session_str, addr);
2578         timeout(1);
2579     }
2580     return r;
2581 }
2582