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