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