cfa249d79a776db9c051c7a73dcedfdc1bbf4a4f
[yazproxy-moved-to-github.git] / src / yaz-proxy.cpp
1 /* $Id: yaz-proxy.cpp,v 1.32 2005-06-10 22:54:22 adam Exp $
2    Copyright (c) 1998-2005, 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 #define HAVE_SYS_STAT_H 1
24 #define HAVE_SYS_TYPES_H 1
25 #endif
26
27 #if HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #if HAVE_SYS_TIME_H
31 #include <sys/time.h>
32 #endif
33 #if HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36 #if HAVE_SYS_STAT_H
37 #include <sys/stat.h>
38 #endif
39
40 #include <assert.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include <fcntl.h>
44
45 #include <yaz/srw.h>
46 #include <yaz/marcdisp.h>
47 #include <yaz/yaz-iconv.h>
48 #include <yaz/log.h>
49 #include <yaz/diagbib1.h>
50 #include "proxyp.h"
51 #include <yaz/pquery.h>
52 #include <yaz/otherinfo.h>
53 #include <yaz/charneg.h>
54 #include "msg-thread.h"
55
56 using namespace yazpp_1;
57
58 #define USE_AUTH_MSG 1
59
60 #if USE_AUTH_MSG
61 class Auth_Msg : public IMsg_Thread {
62 public:
63     int m_ret;
64     IMsg_Thread *handle();
65     void result();
66     Yaz_Proxy *m_proxy;
67     NMEM m_nmem;
68     char *m_apdu_buf;
69     int m_apdu_len;
70     Auth_Msg();
71     virtual ~Auth_Msg();
72 };
73
74 Auth_Msg::Auth_Msg()
75 {
76     m_nmem = nmem_create();
77 }
78
79 Auth_Msg::~Auth_Msg()
80 {
81     nmem_destroy(m_nmem);
82 }
83     
84 IMsg_Thread *Auth_Msg::handle()
85 {
86     yaz_log(YLOG_LOG, "Auth_Msg:handle begin");
87     ODR encode = odr_createmem(ODR_DECODE);
88     Z_APDU *apdu;
89
90     odr_setbuf(encode, m_apdu_buf, m_apdu_len, 0);
91     int r = z_APDU(encode, &apdu, 0, 0);
92     if (!r)
93     {
94         yaz_log(YLOG_WARN, "decode failed in Auth_Msg::handle");
95     }
96     else
97     {
98         m_ret = m_proxy->handle_authentication(apdu);
99     }
100     yaz_log(YLOG_LOG, "Auth_Msg:handle end");
101     odr_destroy(encode);
102     return this;
103 }
104
105 void Auth_Msg::result()
106 {
107     if (m_proxy->dec_ref())
108     {
109         yaz_log(YLOG_LOG, "Auth_Msg:result proxy gone");
110     }
111     else
112     {
113         yaz_log(YLOG_LOG, "Auth_Msg:result proxy ok buf=%p len=%d",
114                 m_apdu_buf, m_apdu_len);
115         odr_reset(m_proxy->odr_decode());       
116         odr_setbuf(m_proxy->odr_decode(), m_apdu_buf, m_apdu_len, 0);
117         Z_APDU *apdu = 0;
118         int r = z_APDU(m_proxy->odr_decode(), &apdu, 0, 0);
119         if (r)
120             yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU OK");
121         else
122             yaz_log(YLOG_LOG, "Auth_Msg::result z_APDU failed");
123         m_proxy->result_authentication(apdu, m_ret);
124     }
125     delete this;
126 }
127
128 #endif
129
130 void Yaz_Proxy::result_authentication(Z_APDU *apdu, int ret)
131 {
132     if (ret == 0)
133     {
134         Z_APDU *apdu_reject = zget_APDU(odr_encode(), Z_APDU_initResponse);
135         *apdu_reject->u.initResponse->result = 0;
136         send_to_client(apdu_reject);
137         dec_ref();
138     }
139     else
140         handle_incoming_Z_PDU_2(apdu);
141 }
142
143 static const char *apdu_name(Z_APDU *apdu)
144 {
145     switch (apdu->which)
146     {
147     case Z_APDU_initRequest:
148         return "initRequest";
149     case Z_APDU_initResponse:
150         return "initResponse";
151     case Z_APDU_searchRequest:
152         return "searchRequest";
153     case Z_APDU_searchResponse:
154         return "searchResponse";
155     case Z_APDU_presentRequest:
156         return "presentRequest";
157     case Z_APDU_presentResponse:
158         return "presentResponse";
159     case Z_APDU_deleteResultSetRequest:
160         return "deleteResultSetRequest";
161     case Z_APDU_deleteResultSetResponse:
162         return "deleteResultSetResponse";
163     case Z_APDU_scanRequest:
164         return "scanRequest";
165     case Z_APDU_scanResponse:
166         return "scanResponse";
167     case Z_APDU_sortRequest:
168         return "sortRequest";
169     case Z_APDU_sortResponse:
170         return "sortResponse";
171     case Z_APDU_extendedServicesRequest:
172         return "extendedServicesRequest";
173     case Z_APDU_extendedServicesResponse:
174         return "extendedServicesResponse";
175     case Z_APDU_close:
176         return "close";
177     }
178     return "other";
179 }
180
181 static const char *gdu_name(Z_GDU *gdu)
182 {
183     switch(gdu->which)
184     {
185     case Z_GDU_Z3950:
186         return apdu_name(gdu->u.z3950);
187     case Z_GDU_HTTP_Request:
188         return "HTTP Request";
189     case Z_GDU_HTTP_Response:
190         return "HTTP Response";
191     }
192     return "Unknown request/response";
193 }
194
195 Yaz_Proxy::Yaz_Proxy(IPDU_Observable *the_PDU_Observable,
196                      ISocketObservable *the_socket_observable,
197                      Yaz_Proxy *parent) 
198     :
199     Z_Assoc(the_PDU_Observable), m_bw_stat(60), m_pdu_stat(60)
200 {
201     m_PDU_Observable = the_PDU_Observable;
202     m_socket_observable = the_socket_observable;
203     m_client = 0;
204     m_parent = parent;
205     m_clientPool = 0;
206     m_seqno = 1;
207     m_keepalive_limit_bw = 500000;
208     m_keepalive_limit_pdu = 1000;
209     m_proxyTarget = 0;
210     m_default_target = 0;
211     m_proxy_negotiation_charset = 0;
212     m_proxy_negotiation_lang = 0;
213     m_charset_converter = new Yaz_CharsetConverter;
214     m_max_clients = 150;
215     m_log_mask = 0;
216     m_seed = time(0);
217     m_client_idletime = 600;
218     m_target_idletime = 600;
219     m_optimize = xstrdup ("1");
220     strcpy(m_session_str, "0 ");
221     m_session_no = 0;
222     m_bytes_sent = 0;
223     m_bytes_recv = 0;
224     m_bw_hold_PDU = 0;
225     m_bw_max = 0;
226     m_pdu_max = 0;
227     m_max_record_retrieve = 0;
228     m_reconfig_flag = 0;
229     m_config_fname = 0;
230     m_request_no = 0;
231     m_invalid_session = 0;
232     m_referenceId = 0;
233     m_referenceId_mem = nmem_create();
234     m_config = 0;
235     m_marcxml_mode = none;
236     m_stylesheet_xsp = 0;
237     m_stylesheet_nprl = 0;
238     m_s2z_stylesheet = 0;
239     m_s2z_database = 0;
240     m_schema = 0;
241     m_backend_type = 0;
242     m_backend_charset = 0;
243     m_frontend_type = 0;
244     m_initRequest_apdu = 0;
245     m_initRequest_mem = 0;
246     m_initRequest_preferredMessageSize = 0;
247     m_initRequest_maximumRecordSize = 0;
248     m_initRequest_options = 0;
249     m_initRequest_version = 0;
250     m_initRequest_oi_negotiation_charsets = 0;
251     m_initRequest_oi_negotiation_num_charsets = 0;
252     m_initRequest_oi_negotiation_langs = 0;
253     m_initRequest_oi_negotiation_num_langs = 0;
254     m_initRequest_oi_negotiation_selected = 0;
255     m_apdu_invalid_session = 0;
256     m_mem_invalid_session = 0;
257     m_s2z_odr_init = 0;
258     m_s2z_odr_search = 0;
259     m_s2z_init_apdu = 0;
260     m_s2z_search_apdu = 0;
261     m_s2z_present_apdu = 0;
262     m_http_keepalive = 0;
263     m_http_version = 0;
264     m_soap_ns = 0;
265     m_s2z_packing = Z_SRW_recordPacking_string;
266 #if HAVE_GETTIMEOFDAY
267     m_time_tv = xmalloc(sizeof(struct timeval));
268     struct timeval *tv = (struct timeval *) m_time_tv;
269     tv->tv_sec = 0;
270     tv->tv_usec = 0;
271 #else
272     m_time_tv = 0;
273 #endif
274     m_usemarcon_ini_stage1 = 0;
275     m_usemarcon_ini_stage2 = 0;
276     m_usemarcon = new Yaz_usemarcon();
277     if (!m_parent)
278         low_socket_open();
279     m_my_thread = 0;
280     m_ref_count = 1;
281 }
282
283 void Yaz_Proxy::inc_ref()
284 {
285     m_ref_count++;
286 }
287
288 Yaz_Proxy::~Yaz_Proxy()
289 {
290     assert(m_ref_count == 0);
291     yaz_log(YLOG_LOG, "%sClosed %d/%d sent/recv bytes total", m_session_str,
292             m_bytes_sent, m_bytes_recv);
293     nmem_destroy(m_initRequest_mem);
294     nmem_destroy(m_mem_invalid_session);
295     nmem_destroy(m_referenceId_mem);
296
297     xfree(m_proxyTarget);
298     xfree(m_default_target);
299     xfree(m_proxy_negotiation_charset);
300     xfree(m_proxy_negotiation_lang);
301     delete m_charset_converter;
302     xfree(m_optimize);
303
304 #if HAVE_XSLT
305     if (m_stylesheet_xsp)
306         xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
307 #endif
308     xfree (m_time_tv);
309
310     xfree (m_schema);
311     xfree (m_backend_type);
312     xfree (m_backend_charset);
313     xfree (m_usemarcon_ini_stage1);
314     xfree (m_usemarcon_ini_stage2);
315     delete m_usemarcon;
316     if (m_s2z_odr_init)
317         odr_destroy(m_s2z_odr_init);
318     if (m_s2z_odr_search)
319         odr_destroy(m_s2z_odr_search);
320     if (!m_parent)
321         low_socket_close();
322     if (!m_parent)
323         delete m_my_thread;
324     delete m_config;
325 }
326
327 void Yaz_Proxy::set_debug_mode(int mode)
328 {
329     m_debug_mode = mode;
330 }
331
332 int Yaz_Proxy::set_config(const char *config)
333 {
334     delete m_config;
335     m_config = new Yaz_ProxyConfig();
336     xfree(m_config_fname);
337     m_config_fname = xstrdup(config);
338     int r = m_config->read_xml(config);
339     if (!r)
340         m_config->get_generic_info(&m_log_mask, &m_max_clients);
341     return r;
342 }
343
344 void Yaz_Proxy::set_default_target(const char *target)
345 {
346     xfree (m_default_target);
347     m_default_target = 0;
348     if (target)
349         m_default_target = (char *) xstrdup (target);
350 }
351
352 void Yaz_Proxy::set_proxy_negotiation (const char *charset, const char *lang)
353 {
354     yaz_log(YLOG_LOG, "%sSet the proxy negotiation: charset to '%s', "
355         "language to '%s'", m_session_str, charset?charset:"none",
356         lang?lang:"none");
357     xfree (m_proxy_negotiation_charset);
358     xfree (m_proxy_negotiation_lang);
359     m_proxy_negotiation_charset = m_proxy_negotiation_lang = 0;
360     if (charset)
361         m_proxy_negotiation_charset = (char *) xstrdup (charset);
362     if (lang)
363         m_proxy_negotiation_lang = (char *) xstrdup (lang);
364 }
365
366 Yaz_ProxyConfig *Yaz_Proxy::check_reconfigure()
367 {
368     if (m_parent)
369         return m_parent->check_reconfigure();
370
371     Yaz_ProxyConfig *cfg = m_config;
372     if (m_reconfig_flag)
373     {
374         yaz_log(YLOG_LOG, "reconfigure");
375         yaz_log_reopen();
376         if (m_config_fname && cfg)
377         {
378             yaz_log(YLOG_LOG, "reconfigure config %s", m_config_fname);
379             int r = cfg->read_xml(m_config_fname);
380             if (r)
381                 yaz_log(YLOG_WARN, "reconfigure failed");
382             else
383             {
384                 m_log_mask = 0;
385                 cfg->get_generic_info(&m_log_mask, &m_max_clients);
386             }
387         }
388         else
389             yaz_log(YLOG_LOG, "reconfigure");
390         m_reconfig_flag = 0;
391     }
392     return cfg;
393 }
394
395 IPDU_Observer *Yaz_Proxy::sessionNotify(IPDU_Observable
396                                         *the_PDU_Observable, int fd)
397 {
398     check_reconfigure();
399     Yaz_Proxy *new_proxy = new Yaz_Proxy(the_PDU_Observable,
400                                          m_socket_observable, this);
401     new_proxy->m_config = 0;
402     new_proxy->m_config_fname = 0;
403     new_proxy->timeout(m_client_idletime);
404     new_proxy->m_target_idletime = m_target_idletime;
405     new_proxy->set_default_target(m_default_target);
406     new_proxy->m_max_clients = m_max_clients;
407     new_proxy->m_log_mask = m_log_mask;
408     new_proxy->set_APDU_log(get_APDU_log());
409     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
410         new_proxy->set_APDU_yazlog(1);
411     else
412         new_proxy->set_APDU_yazlog(0);
413     sprintf(new_proxy->m_session_str, "%ld:%d ", (long) time(0), m_session_no);
414     m_session_no++;
415     yaz_log (YLOG_LOG, "%sNew session %s", new_proxy->m_session_str,
416              the_PDU_Observable->getpeername());
417     new_proxy->set_proxy_negotiation(m_proxy_negotiation_charset,
418         m_proxy_negotiation_lang);
419     // create thread object the first time we get an incoming connection
420     if (!m_my_thread)
421         m_my_thread = new Msg_Thread(m_socket_observable);
422     new_proxy->m_my_thread = m_my_thread;
423     return new_proxy;
424 }
425
426 char *Yaz_Proxy::get_cookie(Z_OtherInformation **otherInfo)
427 {
428     int oid[OID_SIZE];
429     Z_OtherInformationUnit *oi;
430     struct oident ent;
431     ent.proto = PROTO_Z3950;
432     ent.oclass = CLASS_USERINFO;
433     ent.value = (oid_value) VAL_COOKIE;
434     assert (oid_ent_to_oid (&ent, oid));
435
436     if (oid_ent_to_oid (&ent, oid) && 
437         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
438         oi->which == Z_OtherInfo_characterInfo)
439         return oi->information.characterInfo;
440     return 0;
441 }
442 char *Yaz_Proxy::get_proxy(Z_OtherInformation **otherInfo)
443 {
444     int oid[OID_SIZE];
445     Z_OtherInformationUnit *oi;
446     struct oident ent;
447     ent.proto = PROTO_Z3950;
448     ent.oclass = CLASS_USERINFO;
449     ent.value = (oid_value) VAL_PROXY;
450     if (oid_ent_to_oid (&ent, oid) &&
451         (oi = update_otherInformation(otherInfo, 0, oid, 1, 1)) &&
452         oi->which == Z_OtherInfo_characterInfo)
453         return oi->information.characterInfo;
454     return 0;
455 }
456 const char *Yaz_Proxy::load_balance(const char **url)
457 {
458     int zurl_in_use[MAX_ZURL_PLEX];
459     int zurl_in_spare[MAX_ZURL_PLEX];
460     Yaz_ProxyClient *c;
461     int i;
462
463     for (i = 0; i<MAX_ZURL_PLEX; i++)
464     {
465         zurl_in_use[i] = 0;
466         zurl_in_spare[i] = 0;
467     }
468     for (c = m_parent->m_clientPool; c; c = c->m_next)
469     {
470         for (i = 0; url[i]; i++)
471             if (!strcmp(url[i], c->get_hostname()))
472             {
473                 zurl_in_use[i]++;
474                 if (c->m_cookie == 0 && c->m_server == 0 && c->m_waiting == 0)
475                     zurl_in_spare[i]++;
476             }
477     }
478     int min_use = 100000;
479     int spare_for_min = 0;
480     int max_spare = 0;
481     const char *ret_min = 0;
482     const char *ret_spare = 0;
483     for (i = 0; url[i]; i++)
484     {
485         yaz_log(YLOG_DEBUG, "%szurl=%s use=%d spare=%d",
486                 m_session_str, url[i], zurl_in_use[i], zurl_in_spare[i]);
487         if (min_use > zurl_in_use[i])
488         {
489             ret_min = url[i];
490             min_use = zurl_in_use[i];
491             spare_for_min = zurl_in_spare[i];
492         }
493         if (max_spare < zurl_in_spare[i]) 
494         {
495             ret_spare = url[i];
496             max_spare = zurl_in_spare[i];
497         }
498     }
499     return ret_min;
500 }
501
502 Yaz_ProxyClient *Yaz_Proxy::get_client(Z_APDU *apdu, const char *cookie,
503                                        const char *proxy_host)
504 {
505     assert (m_parent);
506     Yaz_Proxy *parent = m_parent;
507     Yaz_ProxyClient *c = m_client;
508     
509     if (!m_proxyTarget)
510     {
511         const char *url[MAX_ZURL_PLEX];
512         Yaz_ProxyConfig *cfg = check_reconfigure();
513         if (proxy_host)
514         {
515             if (parent && parent->m_debug_mode)
516             {
517                 // only to be enabled for debugging...
518                 if (!strcmp(proxy_host, "stop"))
519                     exit(0);
520             }
521             xfree(m_default_target);
522             m_default_target = xstrdup(proxy_host);
523         }
524         proxy_host = m_default_target;
525         int client_idletime = -1;
526         const char *cql2rpn_fname = 0;
527         const char *negotiation_charset = 0;
528         const char *negotiation_lang = 0;
529         const char *query_charset = 0;
530         url[0] = m_default_target;
531         url[1] = 0;
532         if (cfg)
533         {
534             int pre_init = 0;
535             cfg->get_target_info(proxy_host, url, &m_bw_max,
536                                  &m_pdu_max, &m_max_record_retrieve,
537                                  &m_target_idletime, &client_idletime,
538                                  &parent->m_max_clients,
539                                  &m_keepalive_limit_bw,
540                                  &m_keepalive_limit_pdu,
541                                  &pre_init,
542                                  &cql2rpn_fname,
543                                  &negotiation_charset,
544                                  &negotiation_lang,
545                                  &query_charset);
546         }
547         if (client_idletime != -1)
548         {
549             m_client_idletime = client_idletime;
550             timeout(m_client_idletime);
551         }
552         if (cql2rpn_fname)
553             m_cql2rpn.set_pqf_file(cql2rpn_fname);
554         if (negotiation_charset || negotiation_lang)
555         {
556             set_proxy_negotiation(negotiation_charset,
557                 negotiation_lang);
558         }
559         m_charset_converter->set_target_query_charset(query_charset);
560         if (!url[0])
561         {
562             yaz_log(YLOG_LOG, "%sNo default target", m_session_str);
563             return 0;
564         }
565         // we don't handle multiplexing for cookie session, so we just
566         // pick the first one in this case (anonymous users will be able
567         // to use any backend)
568         if (cookie && *cookie)
569             m_proxyTarget = (char*) xstrdup(url[0]);
570         else
571             m_proxyTarget = (char*) xstrdup(load_balance(url));
572     }
573     if (cookie && *cookie)
574     {   // search in sessions with a cookie
575         for (c = parent->m_clientPool; c; c = c->m_next)
576         {
577             assert (c->m_prev);
578             assert (*c->m_prev == c);
579             if (c->m_cookie && !strcmp(cookie,c->m_cookie) &&
580                 !strcmp(m_proxyTarget, c->get_hostname()))
581             {
582                 // Found it in cache
583                 // The following handles "cancel"
584                 // If connection is busy (waiting for PDU) and
585                 // we have an initRequest we can safely do re-open
586                 if (c->m_waiting && apdu->which == Z_APDU_initRequest)
587                 {
588                     yaz_log (YLOG_LOG, "%s REOPEN target=%s", m_session_str,
589                              c->get_hostname());
590                     c->close();
591                     c->m_init_flag = 0;
592                     
593                     c->m_last_ok = 0;
594                     c->m_cache.clear();
595                     c->m_last_resultCount = 0;
596                     c->m_sr_transform = 0;
597                     c->m_waiting = 0;
598                     c->m_resultSetStartPoint = 0;
599                     c->m_target_idletime = m_target_idletime;
600                     if (c->client(m_proxyTarget))
601                     {
602                         delete c;
603                         return 0;
604                     }
605                     c->timeout(30); 
606                 }
607                 c->m_seqno = parent->m_seqno;
608                 if (c->m_server && c->m_server != this)
609                     c->m_server->m_client = 0;
610                 c->m_server = this;
611                 (parent->m_seqno)++;
612                 yaz_log (YLOG_DEBUG, "get_client 1 %p %p", this, c);
613                 return c;
614             }
615         }
616     }
617     else if (!c &&
618             apdu->which == Z_APDU_initRequest &&
619             apdu->u.initRequest->idAuthentication == 0 &&
620             !ODR_MASK_GET(apdu->u.initRequest->options, Z_Options_negotiationModel))
621     {
622         // anonymous sessions without cookie.
623         // if authentication is set it is NOT anonymous se we can't share them.
624         // If charset and lang negotiation is use it is NOT anonymous session too.
625         for (c = parent->m_clientPool; c; c = c->m_next)
626         {
627             assert(c->m_prev);
628             assert(*c->m_prev == c);
629             if (c->m_server == 0 && c->m_cookie == 0 && 
630                 c->m_waiting == 0 && 
631                 !strcmp(m_proxyTarget, c->get_hostname()))
632             {
633                 // found it in cache
634                 yaz_log (YLOG_LOG, "%sREUSE %d %s",
635                          m_session_str, parent->m_seqno, c->get_hostname());
636                 
637                 c->m_seqno = parent->m_seqno;
638                 assert(c->m_server == 0);
639                 c->m_server = this;
640
641                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
642                     c->set_APDU_yazlog(1);
643                 else
644                     c->set_APDU_yazlog(0);
645
646                 (parent->m_seqno)++;
647                 
648                 parent->pre_init();
649                 
650                 return c;
651             }
652         }
653     }
654     if (!m_client)
655     {
656         if (apdu->which != Z_APDU_initRequest)
657         {
658             yaz_log (YLOG_LOG, "%sno init request as first PDU", m_session_str);
659             return 0;
660         }
661         Z_InitRequest *initRequest = apdu->u.initRequest;
662
663         if (initRequest->idAuthentication)
664         {
665             // the client uses authentication. We set the keepalive PDU
666             // to 0 so we don't cache it in releaseClient
667             m_keepalive_limit_pdu = 0;
668         }
669         // go through list of clients - and find the lowest/oldest one.
670         Yaz_ProxyClient *c_min = 0;
671         int min_seq = -1;
672         int no_of_clients = 0;
673         if (parent->m_clientPool)
674             yaz_log (YLOG_DEBUG, "Existing sessions");
675         for (c = parent->m_clientPool; c; c = c->m_next)
676         {
677             yaz_log (YLOG_DEBUG, " Session %-3d wait=%d %s cookie=%s", c->m_seqno,
678                                c->m_waiting, c->get_hostname(),
679                                c->m_cookie ? c->m_cookie : "");
680             no_of_clients++;
681             if (min_seq < 0 || c->m_seqno < min_seq)
682             {
683                 min_seq = c->m_seqno;
684                 c_min = c;
685             }
686         }
687         if (no_of_clients >= parent->m_max_clients)
688         {
689             c = c_min;
690             if (c->m_waiting || strcmp(m_proxyTarget, c->get_hostname()))
691             {
692                 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Destroy %d",
693                          m_session_str, parent->m_max_clients, c->m_seqno);
694                 if (c->m_server && c->m_server != this)
695                     delete c->m_server;
696                 c->m_server = 0;
697             }
698             else
699             {
700                 yaz_log (YLOG_LOG, "%sMAXCLIENTS %d Reuse %d %d %s",
701                          m_session_str, parent->m_max_clients,
702                          c->m_seqno, parent->m_seqno, c->get_hostname());
703                 xfree (c->m_cookie);
704                 c->m_cookie = 0;
705                 if (cookie)
706                     c->m_cookie = xstrdup(cookie);
707                 c->m_seqno = parent->m_seqno;
708                 if (c->m_server && c->m_server != this)
709                 {
710                     c->m_server->m_client = 0;
711                     delete c->m_server;
712                 }
713                 (parent->m_seqno)++;
714                 c->m_target_idletime = m_target_idletime;
715                 c->timeout(m_target_idletime);
716                 
717                 if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
718                     c->set_APDU_yazlog(1);
719                 else
720                     c->set_APDU_yazlog(0);
721
722                 return c;
723             }
724         }
725         else
726         {
727             yaz_log (YLOG_LOG, "%sNEW %d %s",
728                      m_session_str, parent->m_seqno, m_proxyTarget);
729             c = new Yaz_ProxyClient(m_PDU_Observable->clone(), parent);
730             c->m_next = parent->m_clientPool;
731             if (c->m_next)
732                 c->m_next->m_prev = &c->m_next;
733             parent->m_clientPool = c;
734             c->m_prev = &parent->m_clientPool;
735         }
736
737         xfree (c->m_cookie);
738         c->m_cookie = 0;
739         if (cookie)
740             c->m_cookie = xstrdup(cookie);
741
742         c->m_seqno = parent->m_seqno;
743         c->m_init_flag = 0;
744         c->m_last_resultCount = 0;
745         c->m_last_ok = 0;
746         c->m_cache.clear();
747         c->m_sr_transform = 0;
748         c->m_waiting = 0;
749         c->m_resultSetStartPoint = 0;
750         (parent->m_seqno)++;
751         if (c->client(m_proxyTarget))
752         {
753             delete c;
754             return 0;
755         }
756         c->m_target_idletime = m_target_idletime;
757         c->timeout(30);
758
759         if (parent->m_log_mask & PROXY_LOG_APDU_SERVER)
760             c->set_APDU_yazlog(1);
761         else
762             c->set_APDU_yazlog(0);
763     }
764     yaz_log (YLOG_DEBUG, "get_client 3 %p %p", this, c);
765     return c;
766 }
767
768 void Yaz_Proxy::display_diagrecs(Z_DiagRec **pp, int num)
769 {
770     int i;
771     for (i = 0; i<num; i++)
772     {
773         oident *ent;
774         Z_DefaultDiagFormat *r;
775         Z_DiagRec *p = pp[i];
776         if (p->which != Z_DiagRec_defaultFormat)
777         {
778             yaz_log(YLOG_LOG, "%sError no diagnostics", m_session_str);
779             return;
780         }
781         else
782             r = p->u.defaultFormat;
783         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
784             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
785             yaz_log(YLOG_LOG, "%sError unknown diagnostic set", m_session_str);
786         switch (r->which)
787         {
788         case Z_DefaultDiagFormat_v2Addinfo:
789             yaz_log(YLOG_LOG, "%sError %d %s:%s",
790                     m_session_str,
791                     *r->condition, diagbib1_str(*r->condition),
792                     r->u.v2Addinfo);
793             break;
794         case Z_DefaultDiagFormat_v3Addinfo:
795             yaz_log(YLOG_LOG, "%sError %d %s:%s",
796                     m_session_str,
797                     *r->condition, diagbib1_str(*r->condition),
798                     r->u.v3Addinfo);
799             break;
800         }
801     }
802 }
803
804 int Yaz_Proxy::convert_xsl(Z_NamePlusRecordList *p, Z_APDU *apdu)
805 {
806     if (!m_stylesheet_xsp || p->num_records <= 0)
807         return 0;  /* no XSLT to be done ... */
808
809     m_stylesheet_offset = 0;
810     m_stylesheet_nprl = p;
811     m_stylesheet_apdu = apdu;
812     timeout(0);
813     return 1;
814 }
815
816 void Yaz_Proxy::convert_xsl_delay()
817 {
818 #if HAVE_XSLT
819     Z_NamePlusRecord *npr = m_stylesheet_nprl->records[m_stylesheet_offset];
820     if (npr->which == Z_NamePlusRecord_databaseRecord)
821     {
822         Z_External *r = npr->u.databaseRecord;
823         if (r->which == Z_External_octet)
824         {
825 #if 0
826             fwrite((char*) r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, stdout);
827 #endif
828             xmlDocPtr res, doc = xmlParseMemory(
829                 (char*) r->u.octet_aligned->buf,
830                 r->u.octet_aligned->len);
831
832             
833             yaz_log(YLOG_LOG, "%sXSLT convert %d",
834                     m_session_str, m_stylesheet_offset);
835             res = xsltApplyStylesheet((xsltStylesheetPtr) m_stylesheet_xsp,
836                                       doc, 0);
837
838             if (res)
839             {
840                 xmlChar *out_buf;
841                 int out_len;
842                 xmlDocDumpFormatMemory (res, &out_buf, &out_len, 1);
843                 
844                 m_stylesheet_nprl->records[m_stylesheet_offset]->
845                     u.databaseRecord = 
846                     z_ext_record(odr_encode(), VAL_TEXT_XML,
847                                  (char*) out_buf, out_len);
848                 xmlFree(out_buf);
849                 xmlFreeDoc(res);
850             }
851
852             xmlFreeDoc(doc);
853         }
854     }
855 #endif
856     m_stylesheet_offset++;
857     if (m_stylesheet_offset == m_stylesheet_nprl->num_records)
858     {
859         m_stylesheet_nprl = 0;
860 #if HAVE_XSLT
861         if (m_stylesheet_xsp)
862             xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
863 #endif
864         m_stylesheet_xsp = 0;
865         timeout(m_client_idletime);
866         send_PDU_convert(m_stylesheet_apdu);
867     }
868     else
869         timeout(0);
870 }
871
872 void Yaz_Proxy::convert_to_frontend_type(Z_NamePlusRecordList *p)
873 {
874     if (m_frontend_type != VAL_NONE)
875     {
876         int i;
877         for (i = 0; i < p->num_records; i++)
878         {
879             Z_NamePlusRecord *npr = p->records[i];
880             if (npr->which == Z_NamePlusRecord_databaseRecord)
881             {
882                 Z_External *r = npr->u.databaseRecord;
883                 if (r->which == Z_External_octet)
884                 {
885 #if HAVE_USEMARCON
886                     if (m_usemarcon_ini_stage1 && *m_usemarcon_ini_stage1)
887                     {
888                         if (!m_usemarcon->m_stage1)
889                         {
890                             m_usemarcon->m_stage1 = new CDetails();
891                         }
892                         m_usemarcon->m_stage1->SetIniFileName(m_usemarcon_ini_stage1);
893                         m_usemarcon->m_stage1->SetMarcRecord((char*) r->u.octet_aligned->buf, r->u.octet_aligned->len);
894                         int res = m_usemarcon->m_stage1->Start();
895                         if (res == 0)
896                         {
897                             char *converted;
898                             int convlen;
899                             m_usemarcon->m_stage1->GetMarcRecord(converted, convlen);
900                             if (m_usemarcon_ini_stage2 && *m_usemarcon_ini_stage2)
901                             {
902                                 if (!m_usemarcon->m_stage2)
903                                 {
904                                     m_usemarcon->m_stage2 = new CDetails();
905                                 }
906                                 m_usemarcon->m_stage2->SetIniFileName(m_usemarcon_ini_stage2);
907                                 m_usemarcon->m_stage2->SetMarcRecord(converted, convlen);
908                                 res = m_usemarcon->m_stage2->Start();
909                                 if (res == 0)
910                                 {
911                                     free(converted);
912                                     m_usemarcon->m_stage2->GetMarcRecord(converted, convlen);
913                                 }
914                                 else
915                                 {
916                                     yaz_log(YLOG_LOG, "%sUSEMARCON stage 2 error %d", m_session_str, res);
917                                 }
918                             }
919                             npr->u.databaseRecord =
920                                 z_ext_record(odr_encode(),
921                                              m_frontend_type,
922                                              converted,
923                                              strlen(converted));
924                             free(converted);
925                         }
926                         else
927                         {
928                             yaz_log(YLOG_LOG, "%sUSEMARCON stage 1 error %d", m_session_str, res);
929                         }
930                         continue;
931                     }
932 #endif
933 /* HAVE_USEMARCON */
934                     npr->u.databaseRecord =
935                         z_ext_record(odr_encode(),
936                                      m_frontend_type,
937                                      (char*) r->u.octet_aligned->buf,
938                                      r->u.octet_aligned->len);
939                 }
940             }
941         }
942     }
943 }
944
945 void Yaz_Proxy::convert_records_charset(Z_NamePlusRecordList *p,
946                                         const char *backend_charset)
947 {
948     yaz_log(YLOG_LOG, "%sconvert_to_marc", m_session_str);
949     int sel =   m_charset_converter->get_client_charset_selected();
950     const char *client_record_charset =
951         m_charset_converter->get_client_query_charset();
952     if (sel && backend_charset && client_record_charset &&
953         strcmp(backend_charset, client_record_charset))
954     {
955         int i;
956         yaz_iconv_t cd = yaz_iconv_open(client_record_charset,
957                                         backend_charset);
958         yaz_marc_t mt = yaz_marc_create();
959         yaz_marc_xml(mt, YAZ_MARC_ISO2709);
960         yaz_marc_iconv(mt, cd);
961         for (i = 0; i < p->num_records; i++)
962         {
963             Z_NamePlusRecord *npr = p->records[i];
964             if (npr->which == Z_NamePlusRecord_databaseRecord)
965             {
966                 Z_External *r = npr->u.databaseRecord;
967                 oident *ent = oid_getentbyoid(r->direct_reference);
968                 if (!ent || ent->value == VAL_NONE)
969                     continue;
970
971                 if (ent->value == VAL_SUTRS)
972                 {
973                     WRBUF w = wrbuf_alloc();
974
975                     wrbuf_iconv_write(w, cd,  (char*) r->u.octet_aligned->buf,
976                                       r->u.octet_aligned->len);
977                     npr->u.databaseRecord =
978                         z_ext_record(odr_encode(), ent->value, wrbuf_buf(w),
979                                      wrbuf_len(w));
980                     wrbuf_free(w, 1);
981                 }
982                 else if (ent->value == VAL_TEXT_XML)
983                 {
984                     ;
985                 }
986                 else if (r->which == Z_External_octet)
987                 {
988                     int rlen;
989                     char *result;
990                     if (yaz_marc_decode_buf(mt,
991                                             (char*) r->u.octet_aligned->buf,
992                                             r->u.octet_aligned->len,
993                                             &result, &rlen))
994                     {
995                         npr->u.databaseRecord =
996                             z_ext_record(odr_encode(), ent->value, result, rlen);
997                         yaz_log(YLOG_LOG, "%sRecoding MARC record",
998                                 m_session_str);
999                     }
1000                 }
1001             }
1002         }
1003         if (cd)
1004             yaz_iconv_close(cd);
1005         yaz_marc_destroy(mt);
1006     }
1007     else
1008     {
1009         yaz_log(YLOG_LOG, "%sSkipping marc convert", m_session_str);
1010     }
1011 }
1012
1013 void Yaz_Proxy::convert_to_marcxml(Z_NamePlusRecordList *p,
1014                                    const char *backend_charset)
1015 {
1016     int i;
1017     if (!backend_charset)
1018         backend_charset = "MARC-8";
1019     yaz_iconv_t cd = yaz_iconv_open("UTF-8", backend_charset);
1020     yaz_marc_t mt = yaz_marc_create();
1021     yaz_marc_xml(mt, YAZ_MARC_MARCXML);
1022     yaz_marc_iconv(mt, cd);
1023     for (i = 0; i < p->num_records; i++)
1024     {
1025         Z_NamePlusRecord *npr = p->records[i];
1026         if (npr->which == Z_NamePlusRecord_databaseRecord)
1027         {
1028             Z_External *r = npr->u.databaseRecord;
1029             if (r->which == Z_External_OPAC)
1030             {
1031                 WRBUF w = wrbuf_alloc();
1032
1033                 yaz_display_OPAC(w, r->u.opac, 0);
1034                 npr->u.databaseRecord = z_ext_record(
1035                     odr_encode(), VAL_TEXT_XML,
1036                     wrbuf_buf(w), wrbuf_len(w)
1037                     );
1038                 wrbuf_free(w, 1);
1039             }
1040             else if (r->which == Z_External_octet)
1041             {
1042                 int rlen;
1043                 char *result;
1044                 if (yaz_marc_decode_buf(mt, (char*) r->u.octet_aligned->buf,
1045                                         r->u.octet_aligned->len,
1046                                         &result, &rlen))
1047                 {
1048                     npr->u.databaseRecord =
1049                         z_ext_record(odr_encode(), VAL_TEXT_XML, result, rlen);
1050                 }
1051             }
1052         }
1053     }
1054     if (cd)
1055         yaz_iconv_close(cd);
1056     yaz_marc_destroy(mt);
1057 }
1058
1059 void Yaz_Proxy::logtime()
1060 {
1061 #if HAVE_GETTIMEOFDAY
1062     struct timeval *tv = (struct timeval*) m_time_tv;
1063     if (tv->tv_sec)
1064     {
1065         struct timeval tv1;
1066         gettimeofday(&tv1, 0);
1067         long diff = (tv1.tv_sec - tv->tv_sec)*1000000 +
1068             (tv1.tv_usec - tv->tv_usec);
1069         if (diff >= 0)
1070             yaz_log(YLOG_LOG, "%sElapsed %ld.%03ld", m_session_str,
1071                     diff/1000000, (diff/1000)%1000);
1072     }
1073     tv->tv_sec = 0;
1074     tv->tv_usec = 0;
1075 #endif
1076 }
1077
1078 int Yaz_Proxy::send_http_response(int code)
1079 {
1080     ODR o = odr_encode();
1081     Z_GDU *gdu = z_get_HTTP_Response(o, code);
1082     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
1083     if (m_http_version)
1084         hres->version = odr_strdup(o, m_http_version);
1085     if (m_http_keepalive)
1086         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1087     else
1088         timeout(0);
1089     
1090     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1091     {
1092         yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1093                  gdu_name(gdu));
1094     }
1095     int len;
1096     int r = send_GDU(gdu, &len);
1097     m_bytes_sent += len;
1098     m_bw_stat.add_bytes(len);
1099     logtime();
1100     return r;
1101 }
1102
1103 int Yaz_Proxy::send_srw_response(Z_SRW_PDU *srw_pdu)
1104 {
1105     ODR o = odr_encode();
1106     const char *ctype = "text/xml";
1107     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
1108     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
1109     if (m_http_version)
1110         hres->version = odr_strdup(o, m_http_version);
1111     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
1112     if (m_http_keepalive)
1113         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
1114     else
1115         timeout(0);
1116
1117     static Z_SOAP_Handler soap_handlers[2] = {
1118 #if HAVE_XSLT
1119         {"http://www.loc.gov/zing/srw/", 0,
1120          (Z_SOAP_fun) yaz_srw_codec},
1121 #endif
1122         {0, 0, 0}
1123     };
1124     
1125     Z_SOAP *soap_package = (Z_SOAP*) odr_malloc(o, sizeof(Z_SOAP));
1126     soap_package->which = Z_SOAP_generic;
1127     soap_package->u.generic = 
1128         (Z_SOAP_Generic *) odr_malloc(o,  sizeof(*soap_package->u.generic));
1129     soap_package->u.generic->no = 0;
1130     soap_package->u.generic->ns = soap_handlers[0].ns;
1131     soap_package->u.generic->p = (void *) srw_pdu;
1132     soap_package->ns = m_soap_ns;
1133     z_soap_codec_enc_xsl(o, &soap_package,
1134                          &hres->content_buf, &hres->content_len,
1135                          soap_handlers, 0, m_s2z_stylesheet);
1136     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1137     {
1138         yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1139                  gdu_name(gdu));
1140     }
1141     int len;
1142     int r = send_GDU(gdu, &len);
1143     m_bytes_sent += len;
1144     m_bw_stat.add_bytes(len);
1145     logtime();
1146     return r;
1147 }
1148
1149 int Yaz_Proxy::send_to_srw_client_error(int srw_error, const char *add)
1150 {
1151     ODR o = odr_encode();
1152     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
1153     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1154
1155     srw_res->num_diagnostics = 1;
1156     srw_res->diagnostics = (Z_SRW_diagnostic *)
1157         odr_malloc(o, sizeof(*srw_res->diagnostics));
1158     yaz_mk_std_diagnostic(o, srw_res->diagnostics, srw_error, add);
1159     return send_srw_response(srw_pdu);
1160 }
1161
1162 int Yaz_Proxy::z_to_srw_diag(ODR o, Z_SRW_searchRetrieveResponse *srw_res,
1163                              Z_DefaultDiagFormat *ddf)
1164 {
1165     int bib1_code = *ddf->condition;
1166     if (bib1_code == 109)
1167         return 404;
1168     srw_res->num_diagnostics = 1;
1169     srw_res->diagnostics = (Z_SRW_diagnostic *)
1170         odr_malloc(o, sizeof(*srw_res->diagnostics));
1171     yaz_mk_std_diagnostic(o, srw_res->diagnostics,
1172                           yaz_diag_bib1_to_srw(*ddf->condition), 
1173                           ddf->u.v2Addinfo);
1174     return 0;
1175 }
1176
1177 int Yaz_Proxy::send_to_srw_client_ok(int hits, Z_Records *records, int start)
1178 {
1179     ODR o = odr_encode();
1180     Z_SRW_PDU *srw_pdu = yaz_srw_get(o, Z_SRW_searchRetrieve_response);
1181     Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
1182
1183     srw_res->numberOfRecords = odr_intdup (o, hits);
1184     if (records && records->which == Z_Records_DBOSD)
1185     {
1186         srw_res->num_records =
1187             records->u.databaseOrSurDiagnostics->num_records;
1188         int i;
1189         srw_res->records = (Z_SRW_record *)
1190             odr_malloc(o, srw_res->num_records * sizeof(Z_SRW_record));
1191         for (i = 0; i < srw_res->num_records; i++)
1192         {
1193             Z_NamePlusRecord *npr = records->u.databaseOrSurDiagnostics->records[i];
1194             if (npr->which != Z_NamePlusRecord_databaseRecord)
1195             {
1196                 srw_res->records[i].recordSchema = "diagnostic";
1197                 srw_res->records[i].recordPacking = m_s2z_packing;
1198                 srw_res->records[i].recordData_buf = "67";
1199                 srw_res->records[i].recordData_len = 2;
1200                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1201                 continue;
1202             }
1203             Z_External *r = npr->u.databaseRecord;
1204             oident *ent = oid_getentbyoid(r->direct_reference);
1205             if (r->which == Z_External_octet && ent->value == VAL_TEXT_XML)
1206             {
1207                 srw_res->records[i].recordSchema = m_schema;
1208                 srw_res->records[i].recordPacking = m_s2z_packing;
1209                 srw_res->records[i].recordData_buf = (char*) 
1210                     r->u.octet_aligned->buf;
1211                 srw_res->records[i].recordData_len = r->u.octet_aligned->len;
1212                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1213             }
1214             else
1215             {
1216                 srw_res->records[i].recordSchema = "diagnostic";
1217                 srw_res->records[i].recordPacking = m_s2z_packing;
1218                 srw_res->records[i].recordData_buf = "67";
1219                 srw_res->records[i].recordData_len = 2;
1220                 srw_res->records[i].recordPosition = odr_intdup(o, i+start);
1221             }
1222         }
1223     }
1224     if (records && records->which == Z_Records_NSD)
1225     {
1226         int http_code;
1227         http_code = z_to_srw_diag(odr_encode(), srw_res,
1228                                    records->u.nonSurrogateDiagnostic);
1229         if (http_code)
1230             return send_http_response(http_code);
1231     }
1232     return send_srw_response(srw_pdu);
1233     
1234 }
1235
1236 int Yaz_Proxy::send_srw_explain_response(Z_SRW_diagnostic *diagnostics,
1237                                         int num_diagnostics)
1238 {
1239     Yaz_ProxyConfig *cfg = check_reconfigure();
1240     if (cfg)
1241     {
1242         int len;
1243         char *b = cfg->get_explain_doc(odr_encode(), 0 /* target */,
1244                                        m_s2z_database, &len);
1245         if (b)
1246         {
1247             Z_SRW_PDU *res = yaz_srw_get(odr_encode(), Z_SRW_explain_response);
1248             Z_SRW_explainResponse *er = res->u.explain_response;
1249
1250             er->record.recordData_buf = b;
1251             er->record.recordData_len = len;
1252             er->record.recordPacking = m_s2z_packing;
1253             er->record.recordSchema = "http://explain.z3950.org/dtd/2.0/";
1254
1255             er->diagnostics = diagnostics;
1256             er->num_diagnostics = num_diagnostics;
1257             return send_srw_response(res);
1258         }
1259     }
1260     return send_http_response(404);
1261 }
1262
1263 int Yaz_Proxy::send_PDU_convert(Z_APDU *apdu)
1264 {
1265     if (m_http_version)
1266     {
1267         if (apdu->which == Z_APDU_initResponse)
1268         {
1269             Z_InitResponse *res = apdu->u.initResponse;
1270             if (*res->result == 0)
1271             {
1272                 send_to_srw_client_error(3, 0);
1273             }
1274             else if (!m_s2z_search_apdu)
1275             {
1276                 send_srw_explain_response(0, 0);
1277             }
1278             else
1279             {
1280                 handle_incoming_Z_PDU(m_s2z_search_apdu);
1281             }
1282         }
1283         else if (m_s2z_search_apdu && apdu->which == Z_APDU_searchResponse)
1284         {
1285             m_s2z_search_apdu = 0;
1286             Z_SearchResponse *res = apdu->u.searchResponse;
1287             m_s2z_hit_count = *res->resultCount;
1288             if (res->records && res->records->which == Z_Records_NSD)
1289             {
1290                 send_to_srw_client_ok(0, res->records, 1);
1291             }
1292             else if (m_s2z_present_apdu && m_s2z_hit_count > 0)
1293             {
1294                 // adjust 
1295                 Z_PresentRequest *pr = m_s2z_present_apdu->u.presentRequest;
1296                 
1297                 if (*pr->resultSetStartPoint <= m_s2z_hit_count)
1298                 {
1299                     if (*pr->numberOfRecordsRequested+ *pr->resultSetStartPoint
1300                         > m_s2z_hit_count)
1301                         *pr->numberOfRecordsRequested =
1302                             1 + m_s2z_hit_count - *pr->resultSetStartPoint;
1303                 }
1304                 handle_incoming_Z_PDU(m_s2z_present_apdu);
1305             }
1306             else
1307             {
1308                 m_s2z_present_apdu = 0;
1309                 send_to_srw_client_ok(m_s2z_hit_count, res->records, 1);
1310             }
1311         }
1312         else if (m_s2z_present_apdu && apdu->which == Z_APDU_presentResponse)
1313         {
1314             int start = 
1315                 *m_s2z_present_apdu->u.presentRequest->resultSetStartPoint;
1316
1317             m_s2z_present_apdu = 0;
1318             Z_PresentResponse *res = apdu->u.presentResponse;
1319             send_to_srw_client_ok(m_s2z_hit_count, res->records, start);
1320         }
1321     }
1322     else
1323     {
1324         int len = 0;
1325         if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1326             yaz_log (YLOG_LOG, "%sSending %s to client", m_session_str,
1327                      apdu_name(apdu));
1328         int r = send_Z_PDU(apdu, &len);
1329         m_bytes_sent += len;
1330         m_bw_stat.add_bytes(len);
1331         logtime();
1332         return r;
1333     }
1334     return 0;
1335 }
1336
1337 int Yaz_Proxy::send_to_client(Z_APDU *apdu)
1338 {
1339     int kill_session = 0;
1340     Z_ReferenceId **new_id = get_referenceIdP(apdu);
1341
1342     if (new_id)
1343         *new_id = m_referenceId;
1344     
1345     if (apdu->which == Z_APDU_searchResponse)
1346     {
1347         Z_SearchResponse *sr = apdu->u.searchResponse;
1348         Z_Records *p = sr->records;
1349         if (p && p->which == Z_Records_NSD)
1350         {
1351             Z_DiagRec dr, *dr_p = &dr;
1352             dr.which = Z_DiagRec_defaultFormat;
1353             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1354
1355             *sr->searchStatus = 0;
1356             display_diagrecs(&dr_p, 1);
1357         }
1358         else
1359         {
1360             if (p && p->which == Z_Records_DBOSD)
1361             {
1362                 if (m_backend_type
1363 #if HAVE_USEMARCON
1364                     || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
1365 #endif
1366                     )
1367                     convert_to_frontend_type(p->u.databaseOrSurDiagnostics);
1368                 if (m_marcxml_mode == marcxml)
1369                     convert_to_marcxml(p->u.databaseOrSurDiagnostics,
1370                                        m_backend_charset);
1371                 else
1372                     convert_records_charset(p->u.databaseOrSurDiagnostics,
1373                                             m_backend_charset);
1374                 if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1375                     return 0;
1376                     
1377             }
1378             if (sr->resultCount)
1379             {
1380                 yaz_log(YLOG_LOG, "%s%d hits", m_session_str,
1381                         *sr->resultCount);
1382                 if (*sr->resultCount < 0)
1383                 {
1384                     m_invalid_session = 1;
1385                     kill_session = 1;
1386
1387                     *sr->searchStatus = 0;
1388                     sr->records =
1389                         create_nonSurrogateDiagnostics(odr_encode(), 2, 0);
1390                     *sr->resultCount = 0;
1391                 }
1392             }
1393         }
1394     }
1395     else if (apdu->which == Z_APDU_presentResponse)
1396     {
1397         Z_PresentResponse *sr = apdu->u.presentResponse;
1398         Z_Records *p = sr->records;
1399         if (p && p->which == Z_Records_NSD)
1400         {
1401             Z_DiagRec dr, *dr_p = &dr;
1402             dr.which = Z_DiagRec_defaultFormat;
1403             dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1404             if (*sr->presentStatus == Z_PresentStatus_success)
1405                 *sr->presentStatus = Z_PresentStatus_failure;
1406             display_diagrecs(&dr_p, 1);
1407         }
1408         if (p && p->which == Z_Records_DBOSD)
1409         {
1410             if (m_backend_type 
1411 #if HAVE_USEMARCON
1412                 || m_usemarcon_ini_stage1 || m_usemarcon_ini_stage2
1413 #endif
1414                 )
1415                 convert_to_frontend_type(p->u.databaseOrSurDiagnostics);
1416             if (m_marcxml_mode == marcxml)
1417                 convert_to_marcxml(p->u.databaseOrSurDiagnostics,
1418                                    m_backend_charset);
1419             else
1420                 convert_records_charset(p->u.databaseOrSurDiagnostics,
1421                                         m_backend_charset);
1422             if (convert_xsl(p->u.databaseOrSurDiagnostics, apdu))
1423                 return 0;
1424         }
1425     }
1426     else if (apdu->which == Z_APDU_initResponse)
1427     {
1428         //Get and check negotiation record
1429         //from init response.
1430         handle_charset_lang_negotiation(apdu);
1431         
1432         if (m_initRequest_options)
1433         {
1434             Z_Options *nopt = 
1435                 (Odr_bitmask *)odr_malloc(odr_encode(),
1436                                           sizeof(Odr_bitmask));
1437             ODR_MASK_ZERO(nopt);
1438
1439             int i;
1440             for (i = 0; i<24; i++)
1441                 if (ODR_MASK_GET(m_initRequest_options, i) &&
1442                     ODR_MASK_GET(apdu->u.initResponse->options, i))
1443                     ODR_MASK_SET(nopt, i);
1444             apdu->u.initResponse->options = nopt;           
1445         }
1446         if (m_initRequest_version)
1447         {
1448             Z_ProtocolVersion *nopt = 
1449                 (Odr_bitmask *)odr_malloc(odr_encode(),
1450                                           sizeof(Odr_bitmask));
1451             ODR_MASK_ZERO(nopt);
1452
1453             int i;
1454             for (i = 0; i<8; i++)
1455                 if (ODR_MASK_GET(m_initRequest_version, i) &&
1456                     ODR_MASK_GET(apdu->u.initResponse->protocolVersion, i))
1457                     ODR_MASK_SET(nopt, i);
1458             apdu->u.initResponse->protocolVersion = nopt;           
1459         }
1460         apdu->u.initResponse->preferredMessageSize =
1461             odr_intdup(odr_encode(),
1462                        m_client->m_initResponse_preferredMessageSize >
1463                        m_initRequest_preferredMessageSize ?
1464                        m_initRequest_preferredMessageSize :
1465                        m_client->m_initResponse_preferredMessageSize);
1466         apdu->u.initResponse->maximumRecordSize =
1467             odr_intdup(odr_encode(),
1468                        m_client->m_initResponse_maximumRecordSize >
1469                        m_initRequest_maximumRecordSize ?
1470                        m_initRequest_maximumRecordSize :
1471                        m_client->m_initResponse_maximumRecordSize);
1472     }
1473     
1474     int r = send_PDU_convert(apdu);
1475     if (r)
1476         return r;
1477     if (kill_session)
1478     {
1479         delete m_client;
1480         m_client = 0;
1481         m_parent->pre_init();
1482     }
1483     return r;
1484 }
1485
1486 int Yaz_ProxyClient::send_to_target(Z_APDU *apdu)
1487 {
1488     int len = 0;
1489     const char *apdu_name_tmp = apdu_name(apdu);
1490     int r = send_Z_PDU(apdu, &len);
1491     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
1492         yaz_log (YLOG_LOG, "%sSending %s to %s %d bytes",
1493                  get_session_str(),
1494                  apdu_name_tmp, get_hostname(), len);
1495     m_bytes_sent += len;
1496     return r;
1497 }
1498
1499 Z_APDU *Yaz_Proxy::result_set_optimize(Z_APDU *apdu)
1500 {
1501     if (apdu->which == Z_APDU_presentRequest)
1502     {
1503         Z_PresentRequest *pr = apdu->u.presentRequest;
1504         int toget = *pr->numberOfRecordsRequested;
1505         int start = *pr->resultSetStartPoint;
1506
1507         yaz_log(YLOG_LOG, "%sPresent %s %d+%d", m_session_str,
1508                 pr->resultSetId, start, toget);
1509
1510         if (*m_parent->m_optimize == '0')
1511             return apdu;
1512
1513         if (!m_client->m_last_resultSetId)
1514         {
1515             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1516             new_apdu->u.presentResponse->records =
1517                 create_nonSurrogateDiagnostics(odr_encode(), 30,
1518                                                pr->resultSetId);
1519             send_to_client(new_apdu);
1520             return 0;
1521         }
1522         if (!strcmp(m_client->m_last_resultSetId, pr->resultSetId))
1523         {
1524             if (start+toget-1 > m_client->m_last_resultCount)
1525             {
1526                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1527                 new_apdu->u.presentResponse->records =
1528                     create_nonSurrogateDiagnostics(odr_encode(), 13, 0);
1529                 send_to_client(new_apdu);
1530                 return 0;
1531             }
1532             Z_NamePlusRecordList *npr;
1533 #if 0
1534             yaz_log(YLOG_LOG, "%sCache lookup %d+%d syntax=%s",
1535                     m_session_str, start, toget, yaz_z3950oid_to_str(
1536                         pr->preferredRecordSyntax, &oclass));
1537 #endif
1538             if (m_client->m_cache.lookup (odr_encode(), &npr, start, toget,
1539                                           pr->preferredRecordSyntax,
1540                                           pr->recordComposition))
1541             {
1542                 yaz_log (YLOG_LOG, "%sReturned cached records for present request", 
1543                          m_session_str);
1544                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
1545                 new_apdu->u.presentResponse->referenceId = pr->referenceId;
1546                 
1547                 new_apdu->u.presentResponse->numberOfRecordsReturned
1548                     = odr_intdup(odr_encode(), toget);
1549                                                                  
1550                 new_apdu->u.presentResponse->records = (Z_Records*)
1551                     odr_malloc(odr_encode(), sizeof(Z_Records));
1552                 new_apdu->u.presentResponse->records->which = Z_Records_DBOSD;
1553                 new_apdu->u.presentResponse->records->u.databaseOrSurDiagnostics = npr;
1554                 new_apdu->u.presentResponse->nextResultSetPosition =
1555                     odr_intdup(odr_encode(), start+toget);
1556
1557                 send_to_client(new_apdu);
1558                 return 0;
1559             }
1560         }
1561     }
1562
1563     if (apdu->which != Z_APDU_searchRequest)
1564         return apdu;
1565     Z_SearchRequest *sr = apdu->u.searchRequest;
1566     Yaz_Z_Query *this_query = new Yaz_Z_Query;
1567     Yaz_Z_Databases this_databases;
1568
1569     this_databases.set(sr->num_databaseNames, (const char **)
1570                        sr->databaseNames);
1571     
1572     this_query->set_Z_Query(sr->query);
1573
1574     char query_str[120];
1575     this_query->print(query_str, sizeof(query_str)-1);
1576     yaz_log(YLOG_LOG, "%sSearch %s", m_session_str, query_str);
1577
1578     if (*m_parent->m_optimize != '0' &&
1579         m_client->m_last_ok && m_client->m_last_query &&
1580         m_client->m_last_query->match(this_query) &&
1581         !strcmp(m_client->m_last_resultSetId, sr->resultSetName) &&
1582         m_client->m_last_databases.match(this_databases))
1583     {
1584         delete this_query;
1585         if (m_client->m_last_resultCount > *sr->smallSetUpperBound &&
1586             m_client->m_last_resultCount < *sr->largeSetLowerBound)
1587         {
1588             Z_NamePlusRecordList *npr;
1589             int toget = *sr->mediumSetPresentNumber;
1590             Z_RecordComposition *comp = 0;
1591
1592             if (toget > m_client->m_last_resultCount)
1593                 toget = m_client->m_last_resultCount;
1594             
1595             if (sr->mediumSetElementSetNames)
1596             {
1597                 comp = (Z_RecordComposition *)
1598                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1599                 comp->which = Z_RecordComp_simple;
1600                 comp->u.simple = sr->mediumSetElementSetNames;
1601             }
1602  
1603             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1604                                           sr->preferredRecordSyntax, comp))
1605             {
1606                 yaz_log (YLOG_LOG, "%sReturned cached records for medium set",
1607                          m_session_str);
1608                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1609                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1610                 new_apdu->u.searchResponse->resultCount =
1611                     &m_client->m_last_resultCount;
1612                 
1613                 new_apdu->u.searchResponse->numberOfRecordsReturned
1614                     = odr_intdup(odr_encode(), toget);
1615                                                         
1616                 new_apdu->u.searchResponse->presentStatus =
1617                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1618                 new_apdu->u.searchResponse->records = (Z_Records*)
1619                     odr_malloc(odr_encode(), sizeof(Z_Records));
1620                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1621                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1622                 new_apdu->u.searchResponse->nextResultSetPosition =
1623                     odr_intdup(odr_encode(), toget+1);
1624                 send_to_client(new_apdu);
1625                 return 0;
1626             }
1627             else
1628             {
1629                 // medium Set
1630                 // send present request (medium size)
1631                 yaz_log (YLOG_LOG, "%sOptimizing search for medium set",
1632                          m_session_str);
1633
1634                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1635                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1636                 pr->referenceId = sr->referenceId;
1637                 pr->resultSetId = sr->resultSetName;
1638                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1639                 *pr->numberOfRecordsRequested = toget;
1640                 pr->recordComposition = comp;
1641                 m_client->m_sr_transform = 1;
1642                 return new_apdu;
1643             }
1644         }
1645         else if (m_client->m_last_resultCount >= *sr->largeSetLowerBound ||
1646             m_client->m_last_resultCount <= 0)
1647         {
1648             // large set. Return pseudo-search response immediately
1649             yaz_log (YLOG_LOG, "%sOptimizing search for large set",
1650                      m_session_str);
1651             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1652             new_apdu->u.searchResponse->referenceId = sr->referenceId;
1653             new_apdu->u.searchResponse->resultCount =
1654                 &m_client->m_last_resultCount;
1655             send_to_client(new_apdu);
1656             return 0;
1657         }
1658         else
1659         {
1660             Z_NamePlusRecordList *npr;
1661             int toget = m_client->m_last_resultCount;
1662             Z_RecordComposition *comp = 0;
1663             // small set
1664             // send a present request (small set)
1665             
1666             if (sr->smallSetElementSetNames)
1667             {
1668                 comp = (Z_RecordComposition *)
1669                     odr_malloc(odr_encode(), sizeof(Z_RecordComposition));
1670                 comp->which = Z_RecordComp_simple;
1671                 comp->u.simple = sr->smallSetElementSetNames;
1672             }
1673
1674             if (m_client->m_cache.lookup (odr_encode(), &npr, 1, toget,
1675                                           sr->preferredRecordSyntax, comp))
1676             {
1677                 yaz_log (YLOG_LOG, "%sReturned cached records for small set",
1678                          m_session_str);
1679                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
1680                 new_apdu->u.searchResponse->referenceId = sr->referenceId;
1681                 new_apdu->u.searchResponse->resultCount =
1682                     &m_client->m_last_resultCount;
1683                 
1684                 new_apdu->u.searchResponse->numberOfRecordsReturned
1685                     = odr_intdup(odr_encode(), toget);
1686                                                                  
1687                 new_apdu->u.searchResponse->presentStatus =
1688                     odr_intdup(odr_encode(), Z_PresentStatus_success);
1689                 new_apdu->u.searchResponse->records = (Z_Records*)
1690                     odr_malloc(odr_encode(), sizeof(Z_Records));
1691                 new_apdu->u.searchResponse->records->which = Z_Records_DBOSD;
1692                 new_apdu->u.searchResponse->records->u.databaseOrSurDiagnostics = npr;
1693                 new_apdu->u.searchResponse->nextResultSetPosition =
1694                     odr_intdup(odr_encode(), toget+1);
1695                 send_to_client(new_apdu);
1696                 return 0;
1697             }
1698             else
1699             {
1700                 yaz_log (YLOG_LOG, "%sOptimizing search for small set",
1701                          m_session_str);
1702                 Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentRequest);
1703                 Z_PresentRequest *pr = new_apdu->u.presentRequest;
1704                 pr->referenceId = sr->referenceId;
1705                 pr->resultSetId = sr->resultSetName;
1706                 pr->preferredRecordSyntax = sr->preferredRecordSyntax;
1707                 *pr->numberOfRecordsRequested = toget;
1708                 pr->recordComposition = comp;
1709                 m_client->m_sr_transform = 1;
1710                 return new_apdu;
1711             }
1712         }
1713     }
1714     else  // query doesn't match
1715     {
1716         delete m_client->m_last_query;
1717         m_client->m_last_query = this_query;
1718         m_client->m_last_ok = 0;
1719         m_client->m_cache.clear();
1720         m_client->m_resultSetStartPoint = 0;
1721
1722         xfree (m_client->m_last_resultSetId);
1723         m_client->m_last_resultSetId = xstrdup (sr->resultSetName);
1724
1725         m_client->m_last_databases.set(sr->num_databaseNames,
1726                                        (const char **) sr->databaseNames);
1727     }
1728     return apdu;
1729 }
1730
1731
1732 void Yaz_Proxy::inc_request_no()
1733 {
1734     char *cp = strchr(m_session_str, ' ');
1735     m_request_no++;
1736     if (cp)
1737         sprintf(cp+1, "%d ", m_request_no);
1738 }
1739
1740 void Yaz_Proxy::recv_GDU(Z_GDU *apdu, int len)
1741 {
1742     inc_request_no();
1743
1744     m_bytes_recv += len;
1745     
1746     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
1747         yaz_log (YLOG_LOG, "%sReceiving %s from client %d bytes",
1748                  m_session_str, gdu_name(apdu), len);
1749
1750     if (m_bw_hold_PDU)     // double incoming PDU. shutdown now.
1751         dec_ref();
1752
1753     m_bw_stat.add_bytes(len);
1754     m_pdu_stat.add_bytes(1);
1755
1756 #if HAVE_GETTIMEOFDAY
1757     gettimeofday((struct timeval *) m_time_tv, 0);
1758 #endif
1759
1760     int bw_total = m_bw_stat.get_total();
1761     int pdu_total = m_pdu_stat.get_total();
1762
1763     int reduce = 0;
1764     if (m_bw_max)
1765     {
1766         if (bw_total > m_bw_max)
1767         {
1768             reduce = (bw_total/m_bw_max);
1769         }
1770     }
1771     if (m_pdu_max)
1772     {
1773         if (pdu_total > m_pdu_max)
1774         {
1775             int nreduce = (m_pdu_max >= 60) ? 1 : 60/m_pdu_max;
1776             reduce = (reduce > nreduce) ? reduce : nreduce;
1777         }
1778     }
1779     m_http_version = 0;
1780     if (reduce)  
1781     {
1782         yaz_log(YLOG_LOG, "%sdelay=%d bw=%d pdu=%d limit-bw=%d limit-pdu=%d",
1783                 m_session_str, reduce, bw_total, pdu_total,
1784                 m_bw_max, m_pdu_max);
1785         
1786         m_bw_hold_PDU = apdu;  // save PDU and signal "on hold"
1787         timeout(reduce);       // call us reduce seconds later
1788     }
1789     else if (apdu->which == Z_GDU_Z3950)
1790         handle_incoming_Z_PDU(apdu->u.z3950);
1791     else if (apdu->which == Z_GDU_HTTP_Request)
1792         handle_incoming_HTTP(apdu->u.HTTP_Request);
1793 }
1794
1795 void Yaz_Proxy::handle_max_record_retrieve(Z_APDU *apdu)
1796 {
1797     if (m_max_record_retrieve)
1798     {
1799         if (apdu->which == Z_APDU_presentRequest)
1800         {
1801             Z_PresentRequest *pr = apdu->u.presentRequest;
1802             if (pr->numberOfRecordsRequested && 
1803                 *pr->numberOfRecordsRequested > m_max_record_retrieve)
1804                 *pr->numberOfRecordsRequested = m_max_record_retrieve;
1805         }
1806     }
1807 }
1808 void Yaz_Proxy::handle_charset_lang_negotiation(Z_APDU *apdu)
1809 {
1810     if (apdu->which == Z_APDU_initRequest)
1811     {
1812         yaz_log(YLOG_LOG, "%shandle_charset_lang_negotiation", 
1813                 m_session_str);
1814         if (m_initRequest_options &&
1815             !ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel) &&
1816             (m_proxy_negotiation_charset || m_proxy_negotiation_lang))
1817         {
1818             // There is no negotiation proposal from
1819             // client's side. OK. The proxy negotiation
1820             // in use, only.
1821             Z_InitRequest *initRequest = apdu->u.initRequest;
1822             Z_OtherInformation **otherInfo;  
1823             Z_OtherInformationUnit *oi;
1824             get_otherInfoAPDU(apdu, &otherInfo);
1825             oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
1826             if (oi)
1827             {
1828                 ODR_MASK_SET(initRequest->options,
1829                     Z_Options_negotiationModel);
1830                 oi->which = Z_OtherInfo_externallyDefinedInfo;    
1831                 oi->information.externallyDefinedInfo =
1832                 yaz_set_proposal_charneg(odr_encode(),
1833                     (const char**)&m_proxy_negotiation_charset,
1834                     m_proxy_negotiation_charset ? 1:0,
1835                     (const char**)&m_proxy_negotiation_lang,
1836                     m_proxy_negotiation_lang ? 1:0,
1837                     1);
1838             }
1839         }
1840         else if (m_initRequest_options &&
1841                  ODR_MASK_GET(m_initRequest_options,
1842                               Z_Options_negotiationModel) &&
1843                  m_charset_converter->get_target_query_charset())
1844         {
1845             yaz_log(YLOG_LOG, "%sManaged charset negotiation: charset=%s",
1846                     m_session_str,
1847                     m_charset_converter->get_target_query_charset());
1848             Z_InitRequest *initRequest = apdu->u.initRequest;
1849             Z_CharSetandLanguageNegotiation *negotiation =
1850                 yaz_get_charneg_record (initRequest->otherInfo);        
1851             if (negotiation &&
1852                 negotiation->which == Z_CharSetandLanguageNegotiation_proposal)
1853             {
1854                 NMEM nmem = nmem_create();
1855                 char **charsets = 0;
1856                 int num_charsets = 0;
1857                 char **langs = 0;
1858                 int num_langs = 0;
1859                 int selected = 0;
1860                 yaz_get_proposal_charneg (nmem, negotiation,
1861                                           &charsets, &num_charsets,
1862                                           &langs, &num_langs, &selected);
1863                 int i;
1864                 for (i = 0; i<num_charsets; i++)
1865                     yaz_log(YLOG_LOG, "%scharset %s", m_session_str,
1866                             charsets[i]);
1867                 for (i = 0; i<num_langs; i++)
1868                     yaz_log(YLOG_LOG, "%slang %s", m_session_str,
1869                             langs[i]);
1870
1871                 const char *t_charset =
1872                     m_charset_converter->get_target_query_charset();
1873                 // sweep through charsets and pick the first supported
1874                 // conversion
1875                 for (i = 0; i<num_charsets; i++)
1876                 {
1877                     const char *c_charset = charsets[i];
1878                     if (!odr_set_charset(odr_decode(), t_charset, c_charset))
1879                         break;
1880                 }
1881                 if (i != num_charsets)
1882                 {
1883                     // got one .. set up ODR for reverse direction
1884                     const char *c_charset = charsets[i];
1885                     odr_set_charset(odr_encode(), c_charset, t_charset);
1886                     m_charset_converter->set_client_query_charset(c_charset);
1887                     m_charset_converter->set_client_charset_selected(selected);
1888                 }
1889                 nmem_destroy(nmem);
1890                 ODR_MASK_CLEAR(m_initRequest_options, 
1891                                Z_Options_negotiationModel);
1892                 yaz_del_charneg_record(&initRequest->otherInfo);
1893             }
1894             else
1895             {
1896                 yaz_log(YLOG_WARN, "%sUnable to decode charset package",
1897                         m_session_str);
1898             }
1899         }
1900     }
1901     else if (apdu->which == Z_APDU_initResponse)
1902     {
1903         Z_InitResponse *initResponse = apdu->u.initResponse;    
1904         Z_OtherInformation **otherInfo;  
1905         
1906         if (ODR_MASK_GET(initResponse->options, Z_Options_negotiationModel))
1907         {
1908             char *charset = 0;
1909             char *lang = 0;
1910             int selected = 0;
1911             
1912             get_otherInfoAPDU(apdu, &otherInfo);
1913             
1914             if (!otherInfo && !(*otherInfo))
1915                 return;
1916             
1917             Z_CharSetandLanguageNegotiation *charneg =
1918                 yaz_get_charneg_record(*otherInfo);
1919                 
1920             if (!charneg)
1921                 return;
1922                                 
1923             yaz_get_response_charneg(m_referenceId_mem, charneg,
1924                 &charset, &lang, &selected);
1925                                 
1926             yaz_log(YLOG_LOG, "%sAccepted charset - '%s' and lang - '%s'",
1927                 m_session_str, (charset)?charset:"none", (lang)?lang:"none");
1928                 
1929             if (m_initRequest_options &&
1930                 ODR_MASK_GET(m_initRequest_options, Z_Options_negotiationModel))
1931             {
1932                 yaz_log(YLOG_LOG, "%sClient's negotiation record in use",
1933                     m_session_str);          
1934             }
1935             else if (m_proxy_negotiation_charset || m_proxy_negotiation_lang)
1936             {
1937                 // negotiation-charset, negotiation-lang
1938                 // elements of config file in use.
1939
1940                 yaz_log(YLOG_LOG, "%sProxy's negotiation record in use",
1941                     m_session_str);
1942
1943                 // clear negotiation option.
1944                 ODR_MASK_CLEAR(initResponse->options, Z_Options_negotiationModel);
1945                 
1946                 // Delete negotiation (charneg-3) entry.
1947                 yaz_del_charneg_record(otherInfo);
1948             }
1949         }
1950         else
1951         {
1952             if (m_proxy_negotiation_charset || m_proxy_negotiation_lang)
1953             {
1954                 yaz_log(YLOG_LOG, "%sTarget did not honor negotiation",
1955                         m_session_str);
1956             }
1957             else if (m_charset_converter->get_client_query_charset())
1958             {
1959                 Z_OtherInformation **otherInfo;  
1960                 Z_OtherInformationUnit *oi;
1961                 get_otherInfoAPDU(apdu, &otherInfo);
1962                 oi = update_otherInformation(otherInfo, 1, NULL, 0, 0);
1963                 if (oi)
1964                 {
1965                     ODR_MASK_SET(initResponse->options,
1966                                  Z_Options_negotiationModel);
1967                     ODR_MASK_SET(m_initRequest_options,
1968                                  Z_Options_negotiationModel);
1969
1970                     oi->which = Z_OtherInfo_externallyDefinedInfo;    
1971                     oi->information.externallyDefinedInfo =
1972                         yaz_set_response_charneg(
1973                             odr_encode(),
1974                             m_charset_converter->get_client_query_charset(),
1975                             0 /* no lang */,
1976                             m_charset_converter->get_client_charset_selected());
1977                 }
1978             }
1979         }
1980     }
1981 }
1982
1983 Z_Records *Yaz_Proxy::create_nonSurrogateDiagnostics(ODR odr,
1984                                                      int error,
1985                                                      const char *addinfo)
1986 {
1987     Z_Records *rec = (Z_Records *)
1988         odr_malloc (odr, sizeof(*rec));
1989     int *err = (int *)
1990         odr_malloc (odr, sizeof(*err));
1991     Z_DiagRec *drec = (Z_DiagRec *)
1992         odr_malloc (odr, sizeof(*drec));
1993     Z_DefaultDiagFormat *dr = (Z_DefaultDiagFormat *)
1994         odr_malloc (odr, sizeof(*dr));
1995     *err = error;
1996     rec->which = Z_Records_NSD;
1997     rec->u.nonSurrogateDiagnostic = dr;
1998     dr->diagnosticSetId =
1999         yaz_oidval_to_z3950oid (odr, CLASS_DIAGSET, VAL_BIB1);
2000     dr->condition = err;
2001     dr->which = Z_DefaultDiagFormat_v2Addinfo;
2002     dr->u.v2Addinfo = odr_strdup (odr, addinfo ? addinfo : "");
2003     return rec;
2004 }
2005
2006 Z_APDU *Yaz_Proxy::handle_query_transformation(Z_APDU *apdu)
2007 {
2008     if (apdu->which == Z_APDU_searchRequest &&
2009         apdu->u.searchRequest->query &&
2010         apdu->u.searchRequest->query->which == Z_Query_type_104 &&
2011         apdu->u.searchRequest->query->u.type_104->which == Z_External_CQL)
2012     {
2013         Z_RPNQuery *rpnquery = 0;
2014         Z_SearchRequest *sr = apdu->u.searchRequest;
2015         char *addinfo = 0;
2016         
2017         yaz_log(YLOG_LOG, "%sCQL: %s", m_session_str,
2018                 sr->query->u.type_104->u.cql);
2019
2020         int r = m_cql2rpn.query_transform(sr->query->u.type_104->u.cql,
2021                                           &rpnquery, odr_encode(),
2022                                           &addinfo);
2023         if (r == -3)
2024             yaz_log(YLOG_LOG, "%sNo CQL to RPN table", m_session_str);
2025         else if (r)
2026         {
2027             yaz_log(YLOG_LOG, "%sCQL Conversion error %d", m_session_str, r);
2028             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2029
2030             new_apdu->u.searchResponse->referenceId = sr->referenceId;
2031             new_apdu->u.searchResponse->records =
2032                 create_nonSurrogateDiagnostics(odr_encode(),
2033                                                yaz_diag_srw_to_bib1(r),
2034                                                addinfo);
2035             *new_apdu->u.searchResponse->searchStatus = 0;
2036
2037             send_to_client(new_apdu);
2038
2039             return 0;
2040         }
2041         else
2042         {
2043             sr->query->which = Z_Query_type_1;
2044             sr->query->u.type_1 = rpnquery;
2045         }
2046         return apdu;
2047     }
2048     return apdu;
2049 }
2050
2051 Z_APDU *Yaz_Proxy::handle_target_charset_conversion(Z_APDU *apdu)
2052 {
2053     if (apdu->which == Z_APDU_searchRequest &&
2054         apdu->u.searchRequest->query)
2055     {
2056         if (apdu->u.searchRequest->query->which == Z_Query_type_1
2057             || apdu->u.searchRequest->query->which == Z_Query_type_101)
2058         {
2059             if (m_http_version)
2060                 m_charset_converter->set_client_query_charset("UTF-8");
2061             Z_RPNQuery *rpnquery = apdu->u.searchRequest->query->u.type_1;
2062             m_charset_converter->convert_type_1(rpnquery, odr_encode());
2063         }
2064     }
2065     return apdu;
2066 }
2067
2068
2069 Z_APDU *Yaz_Proxy::handle_query_validation(Z_APDU *apdu)
2070 {
2071     if (apdu->which == Z_APDU_searchRequest)
2072     {
2073         Z_SearchRequest *sr = apdu->u.searchRequest;
2074         int err = 0;
2075         char *addinfo = 0;
2076
2077         Yaz_ProxyConfig *cfg = check_reconfigure();
2078         if (cfg)
2079             err = cfg->check_query(odr_encode(), m_default_target,
2080                                    sr->query, &addinfo);
2081         if (err)
2082         {
2083             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2084
2085             new_apdu->u.searchResponse->referenceId = sr->referenceId;
2086             new_apdu->u.searchResponse->records =
2087                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2088             *new_apdu->u.searchResponse->searchStatus = 0;
2089
2090             send_to_client(new_apdu);
2091
2092             return 0;
2093         }
2094     }
2095     return apdu;
2096 }
2097
2098 int Yaz_Proxy::handle_authentication(Z_APDU *apdu)
2099 {
2100     if (apdu->which != Z_APDU_initRequest)
2101         return 1;  // pass if no init request
2102     Z_InitRequest *req = apdu->u.initRequest;
2103
2104     Yaz_ProxyConfig *cfg = check_reconfigure();
2105     if (!cfg)
2106         return 1;  // pass if no config
2107
2108     int ret;
2109     if (req->idAuthentication == 0)
2110     {
2111         ret = cfg->client_authentication(m_default_target, 0, 0, 0);
2112     }
2113     else if (req->idAuthentication->which == Z_IdAuthentication_idPass)
2114     {
2115         ret = cfg->client_authentication(m_default_target,
2116             req->idAuthentication->u.idPass->userId,
2117             req->idAuthentication->u.idPass->groupId,
2118             req->idAuthentication->u.idPass->password);
2119     }
2120     else if (req->idAuthentication->which == Z_IdAuthentication_open)
2121     {
2122         char user[64], pass[64];
2123         *user = '\0';
2124         *pass = '\0';
2125         sscanf(req->idAuthentication->u.open, "%63[^/]/%63s", user, pass);
2126         ret = cfg->client_authentication(m_default_target, user, 0, pass);
2127     }
2128     else
2129         ret = cfg->client_authentication(m_default_target, 0, 0, 0);
2130
2131     cfg->target_authentication(m_default_target, odr_encode(), req);
2132
2133     return ret;
2134 }
2135
2136 Z_APDU *Yaz_Proxy::handle_syntax_validation(Z_APDU *apdu)
2137 {
2138     m_marcxml_mode = none;
2139     if (apdu->which == Z_APDU_searchRequest)
2140     {
2141         Z_SearchRequest *sr = apdu->u.searchRequest;
2142         int err = 0;
2143         char *addinfo = 0;
2144         Yaz_ProxyConfig *cfg = check_reconfigure();
2145
2146         Z_RecordComposition rc_temp, *rc = 0;
2147         if (sr->smallSetElementSetNames)
2148         {
2149             rc_temp.which = Z_RecordComp_simple;
2150             rc_temp.u.simple = sr->smallSetElementSetNames;
2151             rc = &rc_temp;
2152         }
2153
2154         if (sr->preferredRecordSyntax)
2155         {
2156             struct oident *ent;
2157             ent = oid_getentbyoid(sr->preferredRecordSyntax);
2158             m_frontend_type = ent->value;
2159         }
2160         else
2161             m_frontend_type = VAL_NONE;
2162
2163         char *stylesheet_name = 0;
2164         if (cfg)
2165             err = cfg->check_syntax(odr_encode(),
2166                                     m_default_target,
2167                                     sr->preferredRecordSyntax, rc,
2168                                     &addinfo, &stylesheet_name, &m_schema,
2169                                     &m_backend_type, &m_backend_charset,
2170                                     &m_usemarcon_ini_stage1,
2171                                     &m_usemarcon_ini_stage2);
2172         if (stylesheet_name)
2173         {
2174             m_parent->low_socket_close();
2175
2176 #if HAVE_XSLT
2177             if (m_stylesheet_xsp)
2178                 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
2179             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
2180                                                        stylesheet_name);
2181 #endif
2182             m_stylesheet_offset = 0;
2183             xfree(stylesheet_name);
2184
2185             m_parent->low_socket_open();
2186         }
2187         if (err == -1)
2188         {
2189             sr->smallSetElementSetNames = 0;
2190             sr->mediumSetElementSetNames = 0;
2191             m_marcxml_mode = marcxml;
2192             if (m_backend_type)
2193             {
2194                 
2195                 sr->preferredRecordSyntax =
2196                     yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
2197                                         m_backend_type);
2198             }
2199             else
2200                 sr->preferredRecordSyntax =
2201                     yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
2202                                            VAL_USMARC);
2203         }
2204         else if (err)
2205         {
2206             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
2207             
2208             new_apdu->u.searchResponse->referenceId = sr->referenceId;
2209             new_apdu->u.searchResponse->records =
2210                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2211             *new_apdu->u.searchResponse->searchStatus = 0;
2212             
2213             send_to_client(new_apdu);
2214             
2215             return 0;
2216         }
2217         else if (m_backend_type)
2218         {
2219             sr->preferredRecordSyntax =
2220                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
2221         }
2222     }
2223     else if (apdu->which == Z_APDU_presentRequest)
2224     {
2225         Z_PresentRequest *pr = apdu->u.presentRequest;
2226         int err = 0;
2227         char *addinfo = 0;
2228         Yaz_ProxyConfig *cfg = check_reconfigure();
2229
2230         if (pr->preferredRecordSyntax)
2231         {
2232             struct oident *ent;
2233             ent = oid_getentbyoid(pr->preferredRecordSyntax);
2234             m_frontend_type = ent->value;
2235         }
2236         else
2237             m_frontend_type = VAL_NONE;
2238
2239         char *stylesheet_name = 0;
2240         if (cfg)
2241             err = cfg->check_syntax(odr_encode(), m_default_target,
2242                                     pr->preferredRecordSyntax,
2243                                     pr->recordComposition,
2244                                     &addinfo, &stylesheet_name, &m_schema,
2245                                     &m_backend_type, &m_backend_charset,
2246                                     &m_usemarcon_ini_stage1,
2247                                     &m_usemarcon_ini_stage2
2248                                     );
2249         if (stylesheet_name)
2250         {
2251             m_parent->low_socket_close();
2252
2253 #if HAVE_XSLT
2254             if (m_stylesheet_xsp)
2255                 xsltFreeStylesheet((xsltStylesheetPtr) m_stylesheet_xsp);
2256             m_stylesheet_xsp = xsltParseStylesheetFile((const xmlChar*)
2257                                                        stylesheet_name);
2258 #endif
2259             m_stylesheet_offset = 0;
2260             xfree(stylesheet_name);
2261
2262             m_parent->low_socket_open();
2263         }
2264         if (err == -1)
2265         {
2266             pr->recordComposition = 0;
2267             m_marcxml_mode = marcxml;
2268             if (m_backend_type)
2269             {
2270                 
2271                 pr->preferredRecordSyntax =
2272                     yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN,
2273                                         m_backend_type);
2274             }
2275             else
2276                 pr->preferredRecordSyntax =
2277                     yaz_oidval_to_z3950oid(odr_encode(), CLASS_RECSYN,
2278                                            VAL_USMARC);
2279         }
2280         else if (err)
2281         {
2282             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_presentResponse);
2283             
2284             new_apdu->u.presentResponse->referenceId = pr->referenceId;
2285             new_apdu->u.presentResponse->records =
2286                 create_nonSurrogateDiagnostics(odr_encode(), err, addinfo);
2287             *new_apdu->u.presentResponse->presentStatus =
2288                 Z_PresentStatus_failure;
2289             
2290             send_to_client(new_apdu);
2291             
2292             return 0;
2293         }
2294         else if (m_backend_type)
2295         {
2296             pr->preferredRecordSyntax =
2297                 yaz_str_to_z3950oid(odr_encode(), CLASS_RECSYN, m_backend_type);
2298         }
2299     }
2300     return apdu;
2301 }
2302
2303 Z_ElementSetNames *Yaz_Proxy::mk_esn_from_schema(ODR o, const char *schema)
2304 {
2305     if (!schema)
2306         return 0;
2307     Z_ElementSetNames *esn = (Z_ElementSetNames *)
2308         odr_malloc(o, sizeof(Z_ElementSetNames));
2309     esn->which = Z_ElementSetNames_generic;
2310     esn->u.generic = odr_strdup(o, schema);
2311     return esn;
2312 }
2313
2314 void Yaz_Proxy::srw_get_client(const char *db, const char **backend_db)
2315 {
2316     const char *t = 0;
2317     Yaz_ProxyConfig *cfg = check_reconfigure();
2318     if (cfg)
2319         t = cfg->get_explain_name(db, backend_db);
2320
2321     if (m_client && m_default_target && t && strcmp(m_default_target, t))
2322     {
2323         releaseClient();
2324     }
2325     
2326     if (t)
2327     {
2328         xfree(m_default_target);
2329         m_default_target = xstrdup(t);
2330     }
2331 }
2332
2333 int Yaz_Proxy::file_access(Z_HTTP_Request *hreq)
2334 {
2335     struct stat sbuf;
2336     if (strcmp(hreq->method, "GET"))
2337         return 0;
2338     if (hreq->path[0] != '/')
2339     {
2340         yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
2341         return 0;
2342     }
2343     const char *cp = hreq->path;
2344     while (*cp)
2345     {
2346         if (*cp == '/' && strchr("/.", cp[1]))
2347         {
2348             yaz_log(YLOG_WARN, "Bad path: %s", hreq->path);
2349             return 0;
2350         }
2351         cp++;
2352     }
2353     const char *fname = hreq->path+1;
2354     if (stat(fname, &sbuf))
2355     {
2356         yaz_log(YLOG_WARN|YLOG_ERRNO, "%s: stat failed", fname);
2357         return 0;
2358     }
2359     if ((sbuf.st_mode & S_IFMT) != S_IFREG)
2360     {
2361         yaz_log(YLOG_WARN, "%s: not a regular file", fname);
2362         return 0;
2363     }
2364     if (sbuf.st_size > (off_t) 1000000)
2365     {
2366         yaz_log(YLOG_WARN, "%s: too large for transfer", fname);
2367         return 0;
2368     }
2369     
2370     ODR o = odr_encode();
2371     Yaz_ProxyConfig *cfg = check_reconfigure();
2372     const char *ctype = cfg->check_mime_type(fname);
2373     Z_GDU *gdu = z_get_HTTP_Response(o, 200);
2374     Z_HTTP_Response *hres = gdu->u.HTTP_Response;
2375     if (m_http_version)
2376         hres->version = odr_strdup(o, m_http_version);
2377     z_HTTP_header_add(o, &hres->headers, "Content-Type", ctype);
2378     if (m_http_keepalive)
2379         z_HTTP_header_add(o, &hres->headers, "Connection", "Keep-Alive");
2380     else
2381         timeout(0);
2382
2383     hres->content_len = sbuf.st_size;
2384     hres->content_buf = (char*) odr_malloc(o, hres->content_len);
2385     FILE *f = fopen(fname, "rb");
2386     if (f)
2387     {
2388         fread(hres->content_buf, 1, hres->content_len, f);
2389         fclose(f);
2390     }
2391     else
2392     {
2393         return 0;
2394     }
2395     if (m_log_mask & PROXY_LOG_REQ_CLIENT)
2396     {
2397         yaz_log (YLOG_LOG, "%sSending file %s to client", m_session_str,
2398                  fname);
2399     }
2400     int len;
2401     send_GDU(gdu, &len);
2402     return 1;
2403 }
2404         
2405 void Yaz_Proxy::handle_incoming_HTTP(Z_HTTP_Request *hreq)
2406 {
2407     if (m_s2z_odr_init)
2408     {
2409         odr_destroy(m_s2z_odr_init);
2410         m_s2z_odr_init = 0;
2411     }
2412     if (m_s2z_odr_search)
2413     {
2414         odr_destroy(m_s2z_odr_search);
2415         m_s2z_odr_search = 0;
2416     }
2417
2418     m_http_keepalive = 0;
2419     m_http_version = 0;
2420     if (!strcmp(hreq->version, "1.0")) 
2421     {
2422         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
2423         if (v && !strcmp(v, "Keep-Alive"))
2424             m_http_keepalive = 1;
2425         else
2426             m_http_keepalive = 0;
2427         m_http_version = "1.0";
2428     }
2429     else
2430     {
2431         const char *v = z_HTTP_header_lookup(hreq->headers, "Connection");
2432         if (v && !strcmp(v, "close"))
2433             m_http_keepalive = 0;
2434         else
2435             m_http_keepalive = 1;
2436         m_http_version = "1.1";
2437     }
2438
2439     Z_SRW_PDU *srw_pdu = 0;
2440     Z_SOAP *soap_package = 0;
2441     char *charset = 0;
2442     Z_SRW_diagnostic *diagnostic = 0;
2443     int num_diagnostic = 0;
2444
2445     if (file_access(hreq))
2446     {
2447         return;
2448     }
2449     else if (yaz_srw_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
2450                             &charset) == 0
2451              || yaz_sru_decode(hreq, &srw_pdu, &soap_package, odr_decode(),
2452                                &charset, &diagnostic, &num_diagnostic) == 0)
2453     {
2454         m_s2z_odr_init = odr_createmem(ODR_ENCODE);
2455         m_s2z_odr_search = odr_createmem(ODR_ENCODE);
2456         m_soap_ns = odr_strdup(m_s2z_odr_search, soap_package->ns);
2457         m_s2z_init_apdu = 0;
2458         m_s2z_search_apdu = 0;
2459         m_s2z_present_apdu = 0;
2460
2461         m_s2z_stylesheet = 0;
2462         
2463         if (srw_pdu->which == Z_SRW_searchRetrieve_request)
2464         {
2465             Z_SRW_searchRetrieveRequest *srw_req = srw_pdu->u.request;
2466
2467             const char *backend_db = srw_req->database;
2468             srw_get_client(srw_req->database, &backend_db);
2469
2470             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
2471             // recordXPath unsupported.
2472             if (srw_req->recordXPath)
2473             {
2474                 yaz_add_srw_diagnostic(odr_decode(),
2475                                        &diagnostic, &num_diagnostic,
2476                                        72, 0);
2477             }
2478             // sort unsupported
2479             if (srw_req->sort_type != Z_SRW_sort_type_none)
2480             {
2481                 yaz_add_srw_diagnostic(odr_decode(),
2482                                        &diagnostic, &num_diagnostic,
2483                                        80, 0);
2484             }
2485             // save stylesheet
2486             if (srw_req->stylesheet)
2487                 m_s2z_stylesheet =
2488                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
2489                                               
2490             // set packing for response records ..
2491             if (srw_req->recordPacking &&
2492                 !strcmp(srw_req->recordPacking, "xml"))
2493                 m_s2z_packing = Z_SRW_recordPacking_XML;
2494             else
2495                 m_s2z_packing = Z_SRW_recordPacking_string;
2496
2497             if (num_diagnostic)
2498             {
2499                 Z_SRW_PDU *srw_pdu =
2500                     yaz_srw_get(odr_encode(),
2501                                 Z_SRW_searchRetrieve_response);
2502                 Z_SRW_searchRetrieveResponse *srw_res = srw_pdu->u.response;
2503                 
2504                 srw_res->diagnostics = diagnostic;
2505                 srw_res->num_diagnostics = num_diagnostic;
2506                 send_srw_response(srw_pdu);
2507                 return;
2508             }
2509
2510             // prepare search PDU
2511             m_s2z_search_apdu = zget_APDU(m_s2z_odr_search,
2512                                           Z_APDU_searchRequest);
2513             Z_SearchRequest *z_searchRequest =
2514                 m_s2z_search_apdu->u.searchRequest;
2515
2516             z_searchRequest->num_databaseNames = 1;
2517             z_searchRequest->databaseNames = (char**)
2518                 odr_malloc(m_s2z_odr_search, sizeof(char *));
2519             z_searchRequest->databaseNames[0] = odr_strdup(m_s2z_odr_search,
2520                                                            backend_db);
2521             
2522             // query transformation
2523             Z_Query *query = (Z_Query *)
2524                 odr_malloc(m_s2z_odr_search, sizeof(Z_Query));
2525             z_searchRequest->query = query;
2526             
2527             if (srw_req->query_type == Z_SRW_query_type_cql)
2528             {
2529                 Z_External *ext = (Z_External *) 
2530                     odr_malloc(m_s2z_odr_search, sizeof(*ext));
2531                 ext->direct_reference = 
2532                     odr_getoidbystr(m_s2z_odr_search, "1.2.840.10003.16.2");
2533                 ext->indirect_reference = 0;
2534                 ext->descriptor = 0;
2535                 ext->which = Z_External_CQL;
2536                 ext->u.cql = srw_req->query.cql;
2537                 
2538                 query->which = Z_Query_type_104;
2539                 query->u.type_104 =  ext;
2540             }
2541             else if (srw_req->query_type == Z_SRW_query_type_pqf)
2542             {
2543                 Z_RPNQuery *RPNquery;
2544                 YAZ_PQF_Parser pqf_parser;
2545                 
2546                 pqf_parser = yaz_pqf_create ();
2547                 
2548                 RPNquery = yaz_pqf_parse (pqf_parser, m_s2z_odr_search,
2549                                           srw_req->query.pqf);
2550                 if (!RPNquery)
2551                 {
2552                     const char *pqf_msg;
2553                     size_t off;
2554                     int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
2555                     yaz_log(YLOG_LOG, "%*s^\n", off+4, "");
2556                     yaz_log(YLOG_LOG, "Bad PQF: %s (code %d)\n", pqf_msg, code);
2557                     
2558                     send_to_srw_client_error(10, 0);
2559                     return;
2560                 }
2561                 query->which = Z_Query_type_1;
2562                 query->u.type_1 =  RPNquery;
2563                 
2564                 yaz_pqf_destroy (pqf_parser);
2565             }
2566             else
2567             {
2568                 send_to_srw_client_error(7, "query");
2569                 return;
2570             }
2571
2572             // present
2573             m_s2z_present_apdu = 0;
2574             int max = 0;
2575             if (srw_req->maximumRecords)
2576                 max = *srw_req->maximumRecords;
2577             int start = 1;
2578             if (srw_req->startRecord)
2579                 start = *srw_req->startRecord;
2580             if (max > 0)
2581             {
2582                 // Some backend, such as Voyager doesn't honor piggyback
2583                 // So we use present always (0 &&).
2584                 if (0 && start <= 1)  // Z39.50 piggyback
2585                 {
2586                     *z_searchRequest->smallSetUpperBound = max;
2587                     *z_searchRequest->mediumSetPresentNumber = max;
2588                     *z_searchRequest->largeSetLowerBound = 2000000000; // 2e9
2589
2590                     z_searchRequest->preferredRecordSyntax =
2591                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
2592                                                VAL_TEXT_XML);
2593                     if (srw_req->recordSchema)
2594                     {
2595                         z_searchRequest->smallSetElementSetNames =
2596                             z_searchRequest->mediumSetElementSetNames =
2597                             mk_esn_from_schema(m_s2z_odr_search,
2598                                                srw_req->recordSchema);
2599                     }
2600                 }
2601                 else   // Z39.50 present
2602                 {
2603                     m_s2z_present_apdu = zget_APDU(m_s2z_odr_search, 
2604                                                    Z_APDU_presentRequest);
2605                     Z_PresentRequest *z_presentRequest = 
2606                         m_s2z_present_apdu->u.presentRequest;
2607                     *z_presentRequest->resultSetStartPoint = start;
2608                     *z_presentRequest->numberOfRecordsRequested = max;
2609                     z_presentRequest->preferredRecordSyntax =
2610                         yaz_oidval_to_z3950oid(m_s2z_odr_search, CLASS_RECSYN,
2611                                                VAL_TEXT_XML);
2612                     if (srw_req->recordSchema)
2613                     {
2614                         z_presentRequest->recordComposition =
2615                             (Z_RecordComposition *)
2616                             odr_malloc(m_s2z_odr_search,
2617                                        sizeof(Z_RecordComposition));
2618                         z_presentRequest->recordComposition->which = 
2619                             Z_RecordComp_simple;                    
2620                         z_presentRequest->recordComposition->u.simple =
2621                             mk_esn_from_schema(m_s2z_odr_search,
2622                                                srw_req->recordSchema);
2623                     }
2624                 }
2625             }
2626             if (!m_client)
2627             {
2628                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
2629                                             Z_APDU_initRequest);
2630                 
2631                 // prevent m_initRequest_apdu memory from being grabbed
2632                 // in Yaz_Proxy::handle_incoming_Z_PDU
2633                 m_initRequest_apdu = m_s2z_init_apdu;
2634                 handle_incoming_Z_PDU(m_s2z_init_apdu);
2635                 return;
2636             }
2637             else
2638             {
2639                 handle_incoming_Z_PDU(m_s2z_search_apdu);
2640                 return;
2641             }
2642         }
2643         else if (srw_pdu->which == Z_SRW_explain_request)
2644         {
2645             Z_SRW_explainRequest *srw_req = srw_pdu->u.explain_request;
2646
2647             const char *backend_db = srw_req->database;
2648             srw_get_client(srw_req->database, &backend_db);
2649
2650             m_s2z_database = odr_strdup(m_s2z_odr_init, srw_req->database);
2651
2652             // save stylesheet
2653             if (srw_req->stylesheet)
2654                 m_s2z_stylesheet =
2655                     odr_strdup(m_s2z_odr_init, srw_req->stylesheet);
2656
2657             if (srw_req->recordPacking &&
2658                 !strcmp(srw_req->recordPacking, "xml"))
2659                 m_s2z_packing = Z_SRW_recordPacking_XML;
2660             else
2661                 m_s2z_packing = Z_SRW_recordPacking_string;
2662
2663             if (num_diagnostic)
2664             {
2665                 send_srw_explain_response(diagnostic, num_diagnostic);
2666                 return;
2667             }
2668
2669             if (!m_client)
2670             {
2671                 m_s2z_init_apdu = zget_APDU(m_s2z_odr_init,
2672                                             Z_APDU_initRequest);
2673                 
2674                 // prevent m_initRequest_apdu memory from being grabbed
2675                 // in Yaz_Proxy::handle_incoming_Z_PDU
2676                 m_initRequest_apdu = m_s2z_init_apdu;
2677                 handle_incoming_Z_PDU(m_s2z_init_apdu);
2678             }
2679             else
2680                 send_srw_explain_response(0, 0);
2681             return;
2682         }
2683         else if (srw_pdu->which == Z_SRW_scan_request)
2684         {
2685             m_s2z_database = odr_strdup(m_s2z_odr_init,
2686                                         srw_pdu->u.scan_request->database);
2687
2688             yaz_add_srw_diagnostic(odr_decode(),
2689                                    &diagnostic, &num_diagnostic,
2690                                    4, "scan");
2691             Z_SRW_PDU *srw_pdu =
2692                 yaz_srw_get(odr_encode(),
2693                             Z_SRW_scan_response);
2694             Z_SRW_scanResponse *srw_res = srw_pdu->u.scan_response;
2695             
2696             srw_res->diagnostics = diagnostic;
2697             srw_res->num_diagnostics = num_diagnostic;
2698             send_srw_response(srw_pdu);
2699             return;
2700         }
2701         else
2702         {
2703             m_s2z_database = 0;
2704
2705             send_to_srw_client_error(4, 0);
2706         }
2707     }
2708     send_http_response(400);
2709 }
2710
2711 void Yaz_Proxy::handle_init(Z_APDU *apdu)
2712 {
2713     Z_OtherInformation **oi;
2714     get_otherInfoAPDU(apdu, &oi);
2715
2716     if (apdu->u.initRequest->implementationId)
2717         yaz_log(YLOG_LOG, "%simplementationId: %s",
2718                 m_session_str, apdu->u.initRequest->implementationId);
2719     if (apdu->u.initRequest->implementationName)
2720         yaz_log(YLOG_LOG, "%simplementationName: %s",
2721                 m_session_str, apdu->u.initRequest->implementationName);
2722     if (apdu->u.initRequest->implementationVersion)
2723         yaz_log(YLOG_LOG, "%simplementationVersion: %s",
2724                 m_session_str, apdu->u.initRequest->implementationVersion);
2725     if (m_initRequest_apdu == 0)
2726     {
2727         if (m_initRequest_mem)
2728             nmem_destroy(m_initRequest_mem);
2729         
2730         m_initRequest_apdu = apdu;
2731         m_initRequest_mem = odr_extract_mem(odr_decode());
2732         
2733         m_initRequest_preferredMessageSize = *apdu->u.initRequest->
2734             preferredMessageSize;
2735         *apdu->u.initRequest->preferredMessageSize = 1024*1024;
2736         m_initRequest_maximumRecordSize = *apdu->u.initRequest->
2737             maximumRecordSize;
2738         *apdu->u.initRequest->maximumRecordSize = 1024*1024;
2739
2740         Z_CharSetandLanguageNegotiation *charSetandLangRecord =
2741             yaz_get_charneg_record(*oi);
2742         
2743         // Save proposal charsets and langs.
2744         if (ODR_MASK_GET(apdu->u.initRequest->options,
2745                          Z_Options_negotiationModel) 
2746             && charSetandLangRecord)
2747         {
2748             
2749             yaz_get_proposal_charneg(m_referenceId_mem,
2750                                      charSetandLangRecord,
2751                                      &m_initRequest_oi_negotiation_charsets,
2752                                      &m_initRequest_oi_negotiation_num_charsets,
2753                                      &m_initRequest_oi_negotiation_langs,
2754                                      &m_initRequest_oi_negotiation_num_langs,
2755                                      &m_initRequest_oi_negotiation_selected);
2756             
2757             for (int i = 0; i<m_initRequest_oi_negotiation_num_charsets; i++)
2758             {
2759                 yaz_log(YLOG_LOG, "%scharacters set proposal: %s",
2760                         m_session_str,(m_initRequest_oi_negotiation_charsets[i])?
2761                         m_initRequest_oi_negotiation_charsets[i]:"none");
2762             }
2763             for (int i=0; i<m_initRequest_oi_negotiation_num_langs; i++)
2764             {
2765                 yaz_log(YLOG_LOG, "%slanguages proposal: %s",
2766                         m_session_str, (m_initRequest_oi_negotiation_langs[i])?
2767                         m_initRequest_oi_negotiation_langs[i]:"none");
2768             }
2769             yaz_log(YLOG_LOG, "%sselected proposal: %d (boolean)",
2770                     m_session_str, m_initRequest_oi_negotiation_selected);
2771         }       
2772         // save init options for the response..
2773         m_initRequest_options = apdu->u.initRequest->options;
2774         
2775         apdu->u.initRequest->options = 
2776             (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2777                                        sizeof(Odr_bitmask));
2778         ODR_MASK_ZERO(apdu->u.initRequest->options);
2779         int i;
2780         for (i = 0; i<= 24; i++)
2781             ODR_MASK_SET(apdu->u.initRequest->options, i);
2782         // check negotiation option
2783         if (!ODR_MASK_GET(m_initRequest_options,
2784                           Z_Options_negotiationModel))
2785         {
2786             ODR_MASK_CLEAR(apdu->u.initRequest->options,
2787                            Z_Options_negotiationModel);
2788         }
2789         ODR_MASK_CLEAR(apdu->u.initRequest->options,
2790                        Z_Options_concurrentOperations);
2791         // make new version
2792         m_initRequest_version = apdu->u.initRequest->protocolVersion;
2793         apdu->u.initRequest->protocolVersion = 
2794             (Odr_bitmask *)nmem_malloc(m_initRequest_mem,
2795                                        sizeof(Odr_bitmask));
2796         ODR_MASK_ZERO(apdu->u.initRequest->protocolVersion);
2797         
2798         for (i = 0; i<= 8; i++)
2799             ODR_MASK_SET(apdu->u.initRequest->protocolVersion, i);
2800     }
2801     if (m_client->m_init_flag)
2802     {
2803         if (handle_init_response_for_invalid_session(apdu))
2804             return;
2805         if (m_client->m_initResponse)
2806         {
2807             Z_APDU *apdu2 = m_client->m_initResponse;
2808             apdu2->u.initResponse->otherInfo = 0;
2809             if (m_client->m_cookie && *m_client->m_cookie)
2810                 set_otherInformationString(apdu2, VAL_COOKIE, 1,
2811                                            m_client->m_cookie);
2812             apdu2->u.initResponse->referenceId =
2813                 apdu->u.initRequest->referenceId;
2814             apdu2->u.initResponse->options = m_client->m_initResponse_options;
2815             apdu2->u.initResponse->protocolVersion = 
2816                 m_client->m_initResponse_version;
2817             
2818             send_to_client(apdu2);
2819             return;
2820         }
2821     }
2822     m_client->m_init_flag = 1;
2823     
2824 #if USE_AUTH_MSG
2825     Auth_Msg *m = new Auth_Msg;
2826     m->m_proxy = this;
2827     z_APDU(odr_encode(), &apdu, 0, "encode");
2828     char *apdu_buf = odr_getbuf(odr_encode(), &m->m_apdu_len, 0);
2829     m->m_apdu_buf = (char*) nmem_malloc(m->m_nmem, m->m_apdu_len);
2830     memcpy(m->m_apdu_buf, apdu_buf, m->m_apdu_len);
2831     odr_reset(odr_encode());
2832
2833     inc_ref();
2834     m_my_thread->put(m);
2835 #else
2836     int ret = handle_authentication(apdu);
2837     result_authentication(apdu, ret);
2838 #endif
2839 }
2840
2841 void Yaz_Proxy::handle_incoming_Z_PDU(Z_APDU *apdu)
2842 {
2843     Z_ReferenceId **refid = get_referenceIdP(apdu);
2844     nmem_reset(m_referenceId_mem);
2845     if (refid && *refid)
2846     {
2847         m_referenceId = (Z_ReferenceId *)
2848             nmem_malloc(m_referenceId_mem, sizeof(*m_referenceId));
2849         m_referenceId->len = m_referenceId->size = (*refid)->len;
2850         m_referenceId->buf = (unsigned char *)
2851             nmem_malloc(m_referenceId_mem, (*refid)->len);
2852         memcpy(m_referenceId->buf, (*refid)->buf, (*refid)->len);
2853     }
2854     else
2855         m_referenceId = 0;
2856
2857     if (!m_client && m_invalid_session)
2858     {
2859         m_apdu_invalid_session = apdu;
2860         m_mem_invalid_session = odr_extract_mem(odr_decode());
2861         apdu = m_initRequest_apdu;
2862     }
2863     
2864     // Determine our client.
2865     Z_OtherInformation **oi;
2866     get_otherInfoAPDU(apdu, &oi);
2867     m_client = get_client(apdu, get_cookie(oi), get_proxy(oi));
2868     if (!m_client)
2869     {
2870         if (m_http_version)
2871         {
2872             send_http_response(404);
2873             return;
2874         }
2875         else
2876         {
2877             dec_ref();
2878             return;
2879         }
2880     }
2881     
2882     m_client->m_server = this;
2883
2884     if (apdu->which == Z_APDU_initRequest)
2885         handle_init(apdu);
2886     else
2887         handle_incoming_Z_PDU_2(apdu);
2888 }
2889
2890 void Yaz_Proxy::handle_incoming_Z_PDU_2(Z_APDU *apdu)
2891 {
2892     handle_max_record_retrieve(apdu);
2893     
2894     if (apdu)
2895         apdu = handle_syntax_validation(apdu);
2896
2897     if (apdu)
2898         apdu = handle_query_transformation(apdu);
2899
2900     if (apdu)
2901         apdu = handle_target_charset_conversion(apdu);
2902
2903     if (apdu)
2904         apdu = handle_query_validation(apdu);
2905
2906     if (apdu)
2907         apdu = result_set_optimize(apdu);
2908
2909     if (!apdu)
2910     {
2911         m_client->timeout(m_target_idletime);  // mark it active even 
2912         // though we didn't use it
2913         return;
2914     }
2915     // Add otherInformation entry in APDU if
2916     // negotiation is in use.
2917     if (apdu)
2918         handle_charset_lang_negotiation(apdu);
2919
2920     // delete other info construct completely if 0 elements
2921     Z_OtherInformation **oi;
2922     get_otherInfoAPDU(apdu, &oi);
2923     if (oi && *oi && (*oi)->num_elements == 0)
2924         *oi = 0;
2925
2926     if (apdu->which == Z_APDU_presentRequest &&
2927         m_client->m_resultSetStartPoint == 0)
2928     {
2929         Z_PresentRequest *pr = apdu->u.presentRequest;
2930         m_client->m_resultSetStartPoint = *pr->resultSetStartPoint;
2931         m_client->m_cache.copy_presentRequest(apdu->u.presentRequest);
2932     } else {
2933         m_client->m_resultSetStartPoint = 0;
2934     }
2935     if (m_client->send_to_target(apdu) < 0)
2936     {
2937         delete m_client;
2938         m_client = 0;
2939         dec_ref();
2940     }
2941     else
2942         m_client->m_waiting = 1;
2943 }
2944
2945 void Yaz_Proxy::connectNotify()
2946 {
2947 }
2948
2949 void Yaz_Proxy::releaseClient()
2950 {
2951     xfree(m_proxyTarget);
2952     m_proxyTarget = 0;
2953     m_invalid_session = 0;
2954     // only keep if keep_alive flag is set...
2955     if (m_client && 
2956         m_client->m_pdu_recv < m_keepalive_limit_pdu &&
2957         m_client->m_bytes_recv+m_client->m_bytes_sent < m_keepalive_limit_bw &&
2958         m_client->m_waiting == 0)
2959     {
2960         yaz_log(YLOG_LOG, "%sShutdown (client to proxy) keepalive %s",
2961                  m_session_str,
2962                  m_client->get_hostname());
2963         yaz_log(YLOG_LOG, "%sbw=%d pdu=%d limit-bw=%d limit-pdu=%d",
2964                 m_session_str, m_client->m_pdu_recv,
2965                 m_client->m_bytes_sent + m_client->m_bytes_recv,
2966                 m_keepalive_limit_bw, m_keepalive_limit_pdu);
2967         assert (m_client->m_waiting != 2);
2968         // Tell client (if any) that no server connection is there..
2969         m_client->m_server = 0;
2970         m_client = 0;
2971     }
2972     else if (m_client)
2973     {
2974         yaz_log (YLOG_LOG, "%sShutdown (client to proxy) close %s",
2975                  m_session_str,
2976                  m_client->get_hostname());
2977         assert (m_client->m_waiting != 2);
2978         delete m_client;
2979         m_client = 0;
2980     }
2981     else if (!m_parent)
2982     {
2983         yaz_log (YLOG_LOG, "%sshutdown (client to proxy) bad state",
2984                  m_session_str);
2985         assert (m_parent);
2986     }
2987     else 
2988     {
2989         yaz_log (YLOG_LOG, "%sShutdown (client to proxy)",
2990                  m_session_str);
2991     }
2992     if (m_parent)
2993         m_parent->pre_init();
2994 }
2995
2996 bool Yaz_Proxy::dec_ref()
2997 {
2998     --m_ref_count;
2999     assert(m_ref_count >= 0);
3000     bool last = (m_ref_count == 0);
3001     if (m_ref_count == 0)
3002     {
3003         releaseClient();
3004         delete this;
3005     }
3006     return last;
3007 }
3008
3009 const char *Yaz_ProxyClient::get_session_str() 
3010 {
3011     if (!m_server)
3012         return "0 ";
3013     return m_server->get_session_str();
3014 }
3015
3016 void Yaz_ProxyClient::shutdown()
3017 {
3018     yaz_log (YLOG_LOG, "%sShutdown (proxy to target) %s", get_session_str(),
3019              get_hostname());
3020     delete m_server;
3021     delete this;
3022 }
3023
3024 void Yaz_Proxy::failNotify()
3025 {
3026     inc_request_no();
3027     yaz_log (YLOG_LOG, "%sConnection closed by client",
3028              get_session_str());
3029     dec_ref();
3030 }
3031
3032 void Yaz_ProxyClient::failNotify()
3033 {
3034     if (m_server)
3035         m_server->inc_request_no();
3036     yaz_log (YLOG_LOG, "%sConnection closed by target %s", 
3037              get_session_str(), get_hostname());
3038     shutdown();
3039 }
3040
3041 void Yaz_ProxyClient::connectNotify()
3042 {
3043     const char *s = get_session_str();
3044     const char *h = get_hostname();
3045     yaz_log (YLOG_LOG, "%sConnection accepted by %s timeout=%d", s, h,
3046              m_target_idletime);
3047     timeout(m_target_idletime);
3048     if (!m_server)
3049         pre_init_client();
3050 }
3051
3052 IPDU_Observer *Yaz_ProxyClient::sessionNotify(IPDU_Observable
3053                                               *the_PDU_Observable, int fd)
3054 {
3055     return new Yaz_ProxyClient(the_PDU_Observable, 0);
3056 }
3057
3058 Yaz_ProxyClient::~Yaz_ProxyClient()
3059 {
3060     if (m_prev)
3061         *m_prev = m_next;
3062     if (m_next)
3063         m_next->m_prev = m_prev;
3064     m_waiting = 2;     // for debugging purposes only.
3065     odr_destroy(m_init_odr);
3066     delete m_last_query;
3067     xfree (m_last_resultSetId);
3068     xfree (m_cookie);
3069 }
3070
3071 void Yaz_ProxyClient::pre_init_client()
3072 {
3073     Z_APDU *apdu = create_Z_PDU(Z_APDU_initRequest);
3074     Z_InitRequest *req = apdu->u.initRequest;
3075     
3076     int i;
3077     for (i = 0; i<= 24; i++)
3078         ODR_MASK_SET(req->options, i);
3079     ODR_MASK_CLEAR(apdu->u.initRequest->options,
3080                    Z_Options_negotiationModel);
3081     ODR_MASK_CLEAR(apdu->u.initRequest->options,
3082                    Z_Options_concurrentOperations);
3083     for (i = 0; i<= 10; i++)
3084         ODR_MASK_SET(req->protocolVersion, i);
3085
3086     if (send_to_target(apdu) < 0)
3087     {
3088         delete this;
3089     }
3090     else
3091     {
3092         m_waiting = 1;
3093         m_init_flag = 1;
3094     }
3095 }
3096
3097 void Yaz_Proxy::pre_init()
3098 {
3099     int i;
3100     const char *name = 0;
3101     const char *zurl_in_use[MAX_ZURL_PLEX];
3102     int limit_bw, limit_pdu, limit_req;
3103     int target_idletime, client_idletime;
3104     int max_clients;
3105     int keepalive_limit_bw, keepalive_limit_pdu;
3106     int pre_init;
3107     const char *cql2rpn = 0;
3108     const char *authentication = 0;
3109     const char *negotiation_charset = 0;
3110     const char *negotiation_lang = 0;
3111
3112     Yaz_ProxyConfig *cfg = check_reconfigure();
3113
3114     zurl_in_use[0] = 0;
3115
3116     if (m_log_mask & PROXY_LOG_APDU_CLIENT)
3117         set_APDU_yazlog(1);
3118     else
3119         set_APDU_yazlog(0);
3120
3121     for (i = 0; cfg && cfg->get_target_no(i, &name, zurl_in_use,
3122                                           &limit_bw, &limit_pdu, &limit_req,
3123                                           &target_idletime, &client_idletime,
3124                                           &max_clients, 
3125                                           &keepalive_limit_bw,
3126                                           &keepalive_limit_pdu,
3127                                           &pre_init,
3128                                           &cql2rpn,
3129                                           &authentication,
3130                                           &negotiation_charset,
3131                                           &negotiation_lang,
3132                                           0) ; i++)
3133     {
3134         if (pre_init)
3135         {
3136             int j;
3137             for (j = 0; zurl_in_use[j]; j++)
3138             {
3139                 Yaz_ProxyClient *c;
3140                 int spare = 0;
3141                 int spare_waiting = 0;
3142                 int in_use = 0;
3143                 int other = 0;
3144                 for (c = m_clientPool; c; c = c->m_next)
3145                 {
3146                     if (!strcmp(zurl_in_use[j], c->get_hostname()))
3147                     {
3148                         if (c->m_cookie == 0)
3149                         {
3150                             if (c->m_server == 0)
3151                                 if (c->m_waiting)
3152                                     spare_waiting++;
3153                                 else
3154                                     spare++;
3155                             else
3156                                 in_use++;
3157                         }
3158                         else
3159                             other++;
3160                     }
3161                 }
3162                 yaz_log(YLOG_LOG, "%spre-init %s %s use=%d other=%d spare=%d "
3163                         "sparew=%d preinit=%d",m_session_str,
3164                         name, zurl_in_use[j], in_use, other,
3165                         spare, spare_waiting, pre_init);
3166                 if (spare + spare_waiting < pre_init)
3167                 {
3168                     c = new Yaz_ProxyClient(m_PDU_Observable->clone(), this);
3169                     c->m_next = m_clientPool;
3170                     if (c->m_next)
3171                         c->m_next->m_prev = &c->m_next;
3172                     m_clientPool = c;
3173                     c->m_prev = &m_clientPool;
3174                     
3175                     if (m_log_mask & PROXY_LOG_APDU_SERVER)
3176                         c->set_APDU_yazlog(1);
3177                     else
3178                         c->set_APDU_yazlog(0);
3179
3180                     if (c->client(zurl_in_use[j]))
3181                     {
3182                         timeout(60);
3183                         delete c;
3184                         return;
3185                     }
3186                     c->timeout(30);
3187                     c->m_waiting = 1;
3188                     c->m_target_idletime = target_idletime;
3189                     c->m_seqno = m_seqno++;
3190                 }
3191             }
3192         }
3193     }
3194 }
3195
3196 void Yaz_Proxy::timeoutNotify()
3197 {
3198     if (m_parent)
3199     {
3200         if (m_bw_hold_PDU)
3201         {
3202             timeout(m_client_idletime);
3203             Z_GDU *apdu = m_bw_hold_PDU;
3204             m_bw_hold_PDU = 0;
3205             
3206             if (apdu->which == Z_GDU_Z3950)
3207                 handle_incoming_Z_PDU(apdu->u.z3950);
3208             else if (apdu->which == Z_GDU_HTTP_Request)
3209                 handle_incoming_HTTP(apdu->u.HTTP_Request);
3210         }
3211         else if (m_stylesheet_nprl)
3212             convert_xsl_delay();
3213         else
3214         {
3215             inc_request_no();
3216
3217             yaz_log (YLOG_LOG, "%sTimeout (client to proxy)", m_session_str);
3218             dec_ref();
3219         }
3220     }
3221     else
3222     {
3223         timeout(600);
3224         pre_init();
3225     }
3226 }
3227
3228 void Yaz_Proxy::markInvalid()
3229 {
3230     m_client = 0;
3231     m_invalid_session = 1;
3232 }
3233
3234 void Yaz_ProxyClient::timeoutNotify()
3235 {
3236     if (m_server)
3237         m_server->inc_request_no();
3238
3239     yaz_log (YLOG_LOG, "%sTimeout (proxy to target) %s", get_session_str(),
3240              get_hostname());
3241     m_waiting = 1;
3242     m_root->pre_init();
3243     if (m_server && m_init_flag)
3244     {
3245         // target timed out in a session that was properly initialized
3246         // server object stay alive but we mark it as invalid so it
3247         // gets initialized again
3248         m_server->markInvalid();
3249         m_server = 0;
3250     }
3251     shutdown();
3252 }
3253
3254 Yaz_ProxyClient::Yaz_ProxyClient(IPDU_Observable *the_PDU_Observable,
3255                                  Yaz_Proxy *parent) :
3256     Z_Assoc (the_PDU_Observable)
3257 {
3258     m_cookie = 0;
3259     m_next = 0;
3260     m_prev = 0;
3261     m_init_flag = 0;
3262     m_last_query = 0;
3263     m_last_resultSetId = 0;
3264     m_last_resultCount = 0;
3265     m_last_ok = 0;
3266     m_sr_transform = 0;
3267     m_waiting = 0;
3268     m_init_odr = odr_createmem (ODR_DECODE);
3269     m_initResponse = 0;
3270     m_initResponse_options = 0;
3271     m_initResponse_version = 0;
3272     m_initResponse_preferredMessageSize = 0;
3273     m_initResponse_maximumRecordSize = 0;
3274     m_resultSetStartPoint = 0;
3275     m_bytes_sent = m_bytes_recv = 0;
3276     m_pdu_recv = 0;
3277     m_server = 0;
3278     m_seqno = 0;
3279     m_target_idletime = 600;
3280     m_root = parent;
3281 }
3282
3283 const char *Yaz_Proxy::option(const char *name, const char *value)
3284 {
3285     if (!strcmp (name, "optimize")) {
3286         if (value) {
3287             xfree (m_optimize); 
3288             m_optimize = xstrdup (value);
3289         }
3290         return m_optimize;
3291     }
3292     return 0;
3293 }
3294
3295 void Yaz_ProxyClient::recv_HTTP_response(Z_HTTP_Response *apdu, int len)
3296 {
3297
3298 }
3299
3300 void Yaz_ProxyClient::recv_GDU(Z_GDU *apdu, int len)
3301 {
3302     if (apdu->which == Z_GDU_Z3950)
3303         recv_Z_PDU(apdu->u.z3950, len);
3304     else if (apdu->which == Z_GDU_HTTP_Response)
3305         recv_HTTP_response(apdu->u.HTTP_Response, len);
3306     else
3307         shutdown();
3308 }
3309
3310 int Yaz_Proxy::handle_init_response_for_invalid_session(Z_APDU *apdu)
3311 {
3312     if (!m_invalid_session)
3313         return 0;
3314     m_invalid_session = 0;
3315     handle_incoming_Z_PDU(m_apdu_invalid_session);
3316     assert (m_mem_invalid_session);
3317     nmem_destroy(m_mem_invalid_session);
3318     m_mem_invalid_session = 0;
3319     return 1;
3320 }
3321
3322 void Yaz_ProxyClient::recv_Z_PDU(Z_APDU *apdu, int len)
3323 {
3324     m_bytes_recv += len;
3325
3326     m_pdu_recv++;
3327     m_waiting = 0;
3328     if (m_root->get_log_mask() & PROXY_LOG_REQ_SERVER)
3329         yaz_log (YLOG_LOG, "%sReceiving %s from %s %d bytes", get_session_str(),
3330                  apdu_name(apdu), get_hostname(), len);
3331     if (apdu->which == Z_APDU_initResponse)
3332     {
3333         if (!m_server)  // if this is a pre init session , check for more
3334             m_root->pre_init();
3335         NMEM nmem = odr_extract_mem (odr_decode());
3336         odr_reset (m_init_odr);
3337         nmem_transfer (m_init_odr->mem, nmem);
3338         m_initResponse = apdu;
3339         m_initResponse_options = apdu->u.initResponse->options;
3340         m_initResponse_version = apdu->u.initResponse->protocolVersion;
3341         m_initResponse_preferredMessageSize = 
3342             *apdu->u.initResponse->preferredMessageSize;
3343         m_initResponse_maximumRecordSize = 
3344             *apdu->u.initResponse->maximumRecordSize;
3345
3346         Z_InitResponse *ir = apdu->u.initResponse;
3347         char *im0 = ir->implementationName;
3348         
3349         char *im1 = (char*) 
3350             odr_malloc(m_init_odr, 20 + (im0 ? strlen(im0) : 0));
3351         *im1 = '\0';
3352         if (im0)
3353         {
3354             strcat(im1, im0);
3355             strcat(im1, " ");
3356         }
3357         strcat(im1, "(YAZ Proxy)");
3358         ir->implementationName = im1;
3359
3360         nmem_destroy (nmem);
3361
3362         if (m_server && m_server->handle_init_response_for_invalid_session(apdu))
3363             return;
3364     }
3365     if (apdu->which == Z_APDU_searchResponse)
3366     {
3367         Z_SearchResponse *sr = apdu->u.searchResponse;
3368         m_last_resultCount = *sr->resultCount;
3369         int status = *sr->searchStatus;
3370         if (status && (!sr->records || sr->records->which == Z_Records_DBOSD))
3371         {
3372             m_last_ok = 1;
3373             
3374             if (sr->records && sr->records->which == Z_Records_DBOSD)
3375             {
3376                 m_cache.add(odr_decode(),
3377                             sr->records->u.databaseOrSurDiagnostics, 1,
3378                             *sr->resultCount);
3379             }
3380         }
3381     }
3382     if (apdu->which == Z_APDU_presentResponse)
3383     {
3384         Z_PresentResponse *pr = apdu->u.presentResponse;
3385         if (m_sr_transform)
3386         {
3387             m_sr_transform = 0;
3388             Z_APDU *new_apdu = create_Z_PDU(Z_APDU_searchResponse);
3389             Z_SearchResponse *sr = new_apdu->u.searchResponse;
3390             sr->referenceId = pr->referenceId;
3391             *sr->resultCount = m_last_resultCount;
3392             sr->records = pr->records;
3393             sr->nextResultSetPosition = pr->nextResultSetPosition;
3394             sr->numberOfRecordsReturned = pr->numberOfRecordsReturned;
3395             apdu = new_apdu;
3396         }
3397         if (pr->records && 
3398             pr->records->which == Z_Records_DBOSD && m_resultSetStartPoint)
3399         {
3400             m_cache.add(odr_decode(),
3401                         pr->records->u.databaseOrSurDiagnostics,
3402                         m_resultSetStartPoint, -1);
3403             m_resultSetStartPoint = 0;
3404         }
3405     }
3406     if (m_cookie)
3407         set_otherInformationString (apdu, VAL_COOKIE, 1, m_cookie);
3408     if (m_server)
3409     {
3410         m_server->send_to_client(apdu);
3411     }
3412     if (apdu->which == Z_APDU_close)
3413     {
3414         shutdown();
3415     }
3416 }
3417
3418 void Yaz_Proxy::low_socket_close()
3419 {
3420 #if WIN32
3421 #else
3422     int i;
3423     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
3424         if  (m_lo_fd[i] >= 0)
3425             ::close(m_lo_fd[i]);
3426 #endif
3427 }
3428
3429 void Yaz_Proxy::low_socket_open()
3430 {
3431 #if WIN32
3432 #else
3433     int i;
3434     for (i = 0; i<NO_SPARE_SOLARIS_FD; i++)
3435         m_lo_fd[i] = open("/dev/null", O_RDONLY);
3436 #endif
3437 }
3438
3439 int Yaz_Proxy::server(const char *addr)
3440 {
3441     int r = Z_Assoc::server(addr);
3442     if (!r)
3443     {
3444         yaz_log(YLOG_LOG, "%sStarted proxy " 
3445 #ifdef VERSION
3446             VERSION 
3447 #endif
3448             " on %s", m_session_str, addr);
3449         timeout(1);
3450     }
3451     return r;
3452 }
3453