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