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