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