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