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