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