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