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