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