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