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