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