• Main Page
  • Data Structures
  • Files

seshigh.c

Go to the documentation of this file.
00001 /* This file is part of the YAZ toolkit.
00002  * Copyright (C) 1995-2008 Index Data
00003  * See the file LICENSE for details.
00004  */
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <assert.h>
00032 #include <ctype.h>
00033 
00034 #if HAVE_SYS_TYPES_H
00035 #include <sys/types.h>
00036 #endif
00037 #if HAVE_SYS_STAT_H
00038 #include <sys/stat.h>
00039 #endif
00040 
00041 #ifdef WIN32
00042 #include <io.h>
00043 #define S_ISREG(x) (x & _S_IFREG)
00044 #include <process.h>
00045 #endif
00046 
00047 #if HAVE_UNISTD_H
00048 #include <unistd.h>
00049 #endif
00050 
00051 #if YAZ_HAVE_XML2
00052 #include <libxml/parser.h>
00053 #include <libxml/tree.h>
00054 #endif
00055 
00056 #include <yaz/yconfig.h>
00057 #include <yaz/xmalloc.h>
00058 #include <yaz/comstack.h>
00059 #include "eventl.h"
00060 #include "session.h"
00061 #include "mime.h"
00062 #include <yaz/proto.h>
00063 #include <yaz/oid_db.h>
00064 #include <yaz/log.h>
00065 #include <yaz/logrpn.h>
00066 #include <yaz/querytowrbuf.h>
00067 #include <yaz/statserv.h>
00068 #include <yaz/diagbib1.h>
00069 #include <yaz/charneg.h>
00070 #include <yaz/otherinfo.h>
00071 #include <yaz/yaz-util.h>
00072 #include <yaz/pquery.h>
00073 #include <yaz/oid_db.h>
00074 
00075 #include <yaz/srw.h>
00076 #include <yaz/backend.h>
00077 #include <yaz/yaz-ccl.h>
00078 
00079 static void process_gdu_request(association *assoc, request *req);
00080 static int process_z_request(association *assoc, request *req, char **msg);
00081 void backend_response(IOCHAN i, int event);
00082 static int process_gdu_response(association *assoc, request *req, Z_GDU *res);
00083 static int process_z_response(association *assoc, request *req, Z_APDU *res);
00084 static Z_APDU *process_initRequest(association *assoc, request *reqb);
00085 static Z_External *init_diagnostics(ODR odr, int errcode,
00086                                     const char *errstring);
00087 static Z_APDU *process_searchRequest(association *assoc, request *reqb,
00088     int *fd);
00089 static Z_APDU *response_searchRequest(association *assoc, request *reqb,
00090     bend_search_rr *bsrr, int *fd);
00091 static Z_APDU *process_presentRequest(association *assoc, request *reqb,
00092     int *fd);
00093 static Z_APDU *process_scanRequest(association *assoc, request *reqb, int *fd);
00094 static Z_APDU *process_sortRequest(association *assoc, request *reqb, int *fd);
00095 static void process_close(association *assoc, request *reqb);
00096 void save_referenceId(request *reqb, Z_ReferenceId *refid);
00097 static Z_APDU *process_deleteRequest(association *assoc, request *reqb,
00098     int *fd);
00099 static Z_APDU *process_segmentRequest(association *assoc, request *reqb);
00100 
00101 static Z_APDU *process_ESRequest(association *assoc, request *reqb, int *fd);
00102 
00103 /* dynamic logging levels */
00104 static int logbits_set = 0;
00105 static int log_session = 0; /* one-line logs for session */
00106 static int log_sessiondetail = 0; /* more detailed stuff */
00107 static int log_request = 0; /* one-line logs for requests */
00108 static int log_requestdetail = 0;  /* more detailed stuff */
00109 
00111 static void get_logbits(void)
00112 { /* needs to be called after parsing cmd-line args that can set loglevels!*/
00113     if (!logbits_set)
00114     {
00115         logbits_set = 1;
00116         log_session = yaz_log_module_level("session"); 
00117         log_sessiondetail = yaz_log_module_level("sessiondetail");
00118         log_request = yaz_log_module_level("request");
00119         log_requestdetail = yaz_log_module_level("requestdetail"); 
00120     }
00121 }
00122 
00123 
00124 
00125 static void wr_diag(WRBUF w, int error, const char *addinfo)
00126 {
00127     wrbuf_printf(w, "ERROR %d+", error);
00128     wrbuf_puts_replace_char(w, diagbib1_str(error), ' ', '_');
00129     if (addinfo){
00130         wrbuf_puts(w, "+");
00131         wrbuf_puts_replace_char(w, addinfo, ' ', '_');
00132     }
00133     
00134     wrbuf_puts(w, " ");    
00135 }
00136 
00137 
00138 /*
00139  * Create and initialize a new association-handle.
00140  *  channel  : iochannel for the current line.
00141  *  link     : communications channel.
00142  * Returns: 0 or a new association handle.
00143  */
00144 association *create_association(IOCHAN channel, COMSTACK link,
00145                                 const char *apdufile)
00146 {
00147     association *anew;
00148 
00149     if (!logbits_set)
00150         get_logbits();
00151     if (!(anew = (association *)xmalloc(sizeof(*anew))))
00152         return 0;
00153     anew->init = 0;
00154     anew->version = 0;
00155     anew->last_control = 0;
00156     anew->client_chan = channel;
00157     anew->client_link = link;
00158     anew->cs_get_mask = 0;
00159     anew->cs_put_mask = 0;
00160     anew->cs_accept_mask = 0;
00161     if (!(anew->decode = odr_createmem(ODR_DECODE)) ||
00162         !(anew->encode = odr_createmem(ODR_ENCODE)))
00163         return 0;
00164     if (apdufile && *apdufile)
00165     {
00166         FILE *f;
00167 
00168         if (!(anew->print = odr_createmem(ODR_PRINT)))
00169             return 0;
00170         if (*apdufile == '@')
00171         {
00172             odr_setprint(anew->print, yaz_log_file());
00173         }       
00174         else if (*apdufile != '-')
00175         {
00176             char filename[256];
00177             sprintf(filename, "%.200s.%ld", apdufile, (long)getpid());
00178             if (!(f = fopen(filename, "w")))
00179             {
00180                 yaz_log(YLOG_WARN|YLOG_ERRNO, "%s", filename);
00181                 return 0;
00182             }
00183             setvbuf(f, 0, _IONBF, 0);
00184             odr_setprint(anew->print, f);
00185         }
00186     }
00187     else
00188         anew->print = 0;
00189     anew->input_buffer = 0;
00190     anew->input_buffer_len = 0;
00191     anew->backend = 0;
00192     anew->state = ASSOC_NEW;
00193     request_initq(&anew->incoming);
00194     request_initq(&anew->outgoing);
00195     anew->proto = cs_getproto(link);
00196     anew->server = 0;
00197     return anew;
00198 }
00199 
00200 /*
00201  * Free association and release resources.
00202  */
00203 void destroy_association(association *h)
00204 {
00205     statserv_options_block *cb = statserv_getcontrol();
00206     request *req;
00207 
00208     xfree(h->init);
00209     odr_destroy(h->decode);
00210     odr_destroy(h->encode);
00211     if (h->print)
00212         odr_destroy(h->print);
00213     if (h->input_buffer)
00214     xfree(h->input_buffer);
00215     if (h->backend)
00216         (*cb->bend_close)(h->backend);
00217     while ((req = request_deq(&h->incoming)))
00218         request_release(req);
00219     while ((req = request_deq(&h->outgoing)))
00220         request_release(req);
00221     request_delq(&h->incoming);
00222     request_delq(&h->outgoing);
00223     xfree(h);
00224     xmalloc_trav("session closed");
00225     if (cb && cb->one_shot)
00226     {
00227         exit(0);
00228     }
00229 }
00230 
00231 static void do_close_req(association *a, int reason, char *message,
00232                          request *req)
00233 {
00234     Z_APDU apdu;
00235     Z_Close *cls = zget_Close(a->encode);
00236     
00237     /* Purge request queue */
00238     while (request_deq(&a->incoming));
00239     while (request_deq(&a->outgoing));
00240     if (a->version >= 3)
00241     {
00242         yaz_log(log_requestdetail, "Sending Close PDU, reason=%d, message=%s",
00243             reason, message ? message : "none");
00244         apdu.which = Z_APDU_close;
00245         apdu.u.close = cls;
00246         *cls->closeReason = reason;
00247         cls->diagnosticInformation = message;
00248         process_z_response(a, req, &apdu);
00249         iochan_settimeout(a->client_chan, 20);
00250     }
00251     else
00252     {
00253         request_release(req);
00254         yaz_log(log_requestdetail, "v2 client. No Close PDU");
00255         iochan_setevent(a->client_chan, EVENT_TIMEOUT); /* force imm close */
00256         a->cs_put_mask = 0;
00257     }
00258     a->state = ASSOC_DEAD;
00259 }
00260 
00261 static void do_close(association *a, int reason, char *message)
00262 {
00263     request *req = request_get(&a->outgoing);
00264     do_close_req (a, reason, message, req);
00265 }
00266 
00267 
00268 int ir_read(IOCHAN h, int event)
00269 {
00270     association *assoc = (association *)iochan_getdata(h);
00271     COMSTACK conn = assoc->client_link;
00272     request *req;
00273     
00274     if ((assoc->cs_put_mask & EVENT_INPUT) == 0 && (event & assoc->cs_get_mask))
00275     {
00276         yaz_log(YLOG_DEBUG, "ir_session (input)");
00277         /* We aren't speaking to this fellow */
00278         if (assoc->state == ASSOC_DEAD)
00279         {
00280             yaz_log(log_sessiondetail, "Connection closed - end of session");
00281             cs_close(conn);
00282             destroy_association(assoc);
00283             iochan_destroy(h);
00284             return 0;
00285         }
00286         assoc->cs_get_mask = EVENT_INPUT;
00287 
00288         do
00289         {
00290             int res = cs_get(conn, &assoc->input_buffer,
00291                              &assoc->input_buffer_len);
00292             if (res < 0 && cs_errno(conn) == CSBUFSIZE)
00293             {
00294                 yaz_log(log_session, "Connection error: %s res=%d",
00295                         cs_errmsg(cs_errno(conn)), res);
00296                 req = request_get(&assoc->incoming); /* get a new request */
00297                 do_close_req(assoc, Z_Close_protocolError, 
00298                              "Incoming package too large", req);
00299                 return 0;
00300             }
00301             else if (res <= 0)
00302             {
00303                 yaz_log(log_session, "Connection closed by client");
00304                 assoc->state = ASSOC_DEAD;
00305                 return 0;
00306             }
00307             else if (res == 1) /* incomplete read - wait for more  */
00308             {
00309                 if (conn->io_pending & CS_WANT_WRITE)
00310                     assoc->cs_get_mask |= EVENT_OUTPUT;
00311                 iochan_setflag(h, assoc->cs_get_mask);
00312                 return 0;
00313             }
00314             /* we got a complete PDU. Let's decode it */
00315             yaz_log(YLOG_DEBUG, "Got PDU, %d bytes: lead=%02X %02X %02X", res,
00316                     assoc->input_buffer[0] & 0xff,
00317                     assoc->input_buffer[1] & 0xff,
00318                     assoc->input_buffer[2] & 0xff);
00319             req = request_get(&assoc->incoming); /* get a new request */
00320             odr_reset(assoc->decode);
00321             odr_setbuf(assoc->decode, assoc->input_buffer, res, 0);
00322             if (!z_GDU(assoc->decode, &req->gdu_request, 0, 0))
00323             {
00324                 yaz_log(YLOG_WARN, "ODR error on incoming PDU: %s [element %s] "
00325                         "[near byte %ld] ",
00326                         odr_errmsg(odr_geterror(assoc->decode)),
00327                         odr_getelement(assoc->decode),
00328                         (long) odr_offset(assoc->decode));
00329                 if (assoc->decode->error != OHTTP)
00330                 {
00331                     yaz_log(YLOG_WARN, "PDU dump:");
00332                     odr_dumpBER(yaz_log_file(), assoc->input_buffer, res);
00333                     request_release(req);
00334                     do_close(assoc, Z_Close_protocolError, "Malformed package");
00335                 }
00336                 else
00337                 {
00338                     Z_GDU *p = z_get_HTTP_Response(assoc->encode, 400);
00339                     assoc->state = ASSOC_DEAD;
00340                     process_gdu_response(assoc, req, p);
00341                 }
00342                 return 0;
00343             }
00344             req->request_mem = odr_extract_mem(assoc->decode);
00345             if (assoc->print) 
00346             {
00347                 if (!z_GDU(assoc->print, &req->gdu_request, 0, 0))
00348                     yaz_log(YLOG_WARN, "ODR print error: %s", 
00349                             odr_errmsg(odr_geterror(assoc->print)));
00350                 odr_reset(assoc->print);
00351             }
00352             request_enq(&assoc->incoming, req);
00353         }
00354         while (cs_more(conn));
00355     }
00356     return 1;
00357 }
00358 
00359 /*
00360  * This is where PDUs from the client are read and the further
00361  * processing is initiated. Flow of control moves down through the
00362  * various process_* functions below, until the encoded result comes back up
00363  * to the output handler in here.
00364  * 
00365  *  h     : the I/O channel that has an outstanding event.
00366  *  event : the current outstanding event.
00367  */
00368 void ir_session(IOCHAN h, int event)
00369 {
00370     int res;
00371     association *assoc = (association *)iochan_getdata(h);
00372     COMSTACK conn = assoc->client_link;
00373     request *req;
00374 
00375     assert(h && conn && assoc);
00376     if (event == EVENT_TIMEOUT)
00377     {
00378         if (assoc->state != ASSOC_UP)
00379         {
00380             yaz_log(YLOG_DEBUG, "Final timeout - closing connection.");
00381             /* do we need to lod this at all */
00382             cs_close(conn);
00383             destroy_association(assoc);
00384             iochan_destroy(h);
00385         }
00386         else
00387         {
00388             yaz_log(log_sessiondetail, 
00389                     "Session idle too long. Sending close.");
00390             do_close(assoc, Z_Close_lackOfActivity, 0);
00391         }
00392         return;
00393     }
00394     if (event & assoc->cs_accept_mask)
00395     {
00396         if (!cs_accept(conn))
00397         {
00398             yaz_log(YLOG_WARN, "accept failed");
00399             destroy_association(assoc);
00400             iochan_destroy(h);
00401             return;
00402         }
00403         iochan_clearflag(h, EVENT_OUTPUT);
00404         if (conn->io_pending) 
00405         {   /* cs_accept didn't complete */
00406             assoc->cs_accept_mask = 
00407                 ((conn->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
00408                 ((conn->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
00409 
00410             iochan_setflag(h, assoc->cs_accept_mask);
00411         }
00412         else
00413         {   /* cs_accept completed. Prepare for reading (cs_get) */
00414             assoc->cs_accept_mask = 0;
00415             assoc->cs_get_mask = EVENT_INPUT;
00416             iochan_setflag(h, assoc->cs_get_mask);
00417         }
00418         return;
00419     }
00420     if (event & assoc->cs_get_mask) /* input */
00421     {
00422         if (!ir_read(h, event))
00423             return;
00424         req = request_head(&assoc->incoming);
00425         if (req->state == REQUEST_IDLE)
00426         {
00427             request_deq(&assoc->incoming);
00428             process_gdu_request(assoc, req);
00429         }
00430     }
00431     if (event & assoc->cs_put_mask)
00432     {
00433         request *req = request_head(&assoc->outgoing);
00434 
00435         assoc->cs_put_mask = 0;
00436         yaz_log(YLOG_DEBUG, "ir_session (output)");
00437         req->state = REQUEST_PENDING;
00438         switch (res = cs_put(conn, req->response, req->len_response))
00439         {
00440         case -1:
00441             yaz_log(log_sessiondetail, "Connection closed by client");
00442             cs_close(conn);
00443             destroy_association(assoc);
00444             iochan_destroy(h);
00445             break;
00446         case 0: /* all sent - release the request structure */
00447             yaz_log(YLOG_DEBUG, "Wrote PDU, %d bytes", req->len_response);
00448 #if 0
00449             yaz_log(YLOG_DEBUG, "HTTP out:\n%.*s", req->len_response,
00450                     req->response);
00451 #endif
00452             nmem_destroy(req->request_mem);
00453             request_deq(&assoc->outgoing);
00454             request_release(req);
00455             if (!request_head(&assoc->outgoing))
00456             {   /* restore mask for cs_get operation ... */
00457                 iochan_clearflag(h, EVENT_OUTPUT|EVENT_INPUT);
00458                 iochan_setflag(h, assoc->cs_get_mask);
00459                 if (assoc->state == ASSOC_DEAD)
00460                     iochan_setevent(assoc->client_chan, EVENT_TIMEOUT);
00461             }
00462             else
00463             {
00464                 assoc->cs_put_mask = EVENT_OUTPUT;
00465             }
00466             break;
00467         default:
00468             if (conn->io_pending & CS_WANT_WRITE)
00469                 assoc->cs_put_mask |= EVENT_OUTPUT;
00470             if (conn->io_pending & CS_WANT_READ)
00471                 assoc->cs_put_mask |= EVENT_INPUT;
00472             iochan_setflag(h, assoc->cs_put_mask);
00473         }
00474     }
00475     if (event & EVENT_EXCEPT)
00476     {
00477         yaz_log(YLOG_WARN, "ir_session (exception)");
00478         cs_close(conn);
00479         destroy_association(assoc);
00480         iochan_destroy(h);
00481     }
00482 }
00483 
00484 static int process_z_request(association *assoc, request *req, char **msg);
00485 
00486 
00487 static void assoc_init_reset(association *assoc)
00488 {
00489     xfree (assoc->init);
00490     assoc->init = (bend_initrequest *) xmalloc(sizeof(*assoc->init));
00491 
00492     assoc->init->stream = assoc->encode;
00493     assoc->init->print = assoc->print;
00494     assoc->init->auth = 0;
00495     assoc->init->referenceId = 0;
00496     assoc->init->implementation_version = 0;
00497     assoc->init->implementation_id = 0;
00498     assoc->init->implementation_name = 0;
00499     assoc->init->query_charset = 0;
00500     assoc->init->records_in_same_charset = 0;
00501     assoc->init->bend_sort = NULL;
00502     assoc->init->bend_search = NULL;
00503     assoc->init->bend_present = NULL;
00504     assoc->init->bend_esrequest = NULL;
00505     assoc->init->bend_delete = NULL;
00506     assoc->init->bend_scan = NULL;
00507     assoc->init->bend_segment = NULL;
00508     assoc->init->bend_fetch = NULL;
00509     assoc->init->bend_explain = NULL;
00510     assoc->init->bend_srw_scan = NULL;
00511     assoc->init->bend_srw_update = NULL;
00512 
00513     assoc->init->charneg_request = NULL;
00514     assoc->init->charneg_response = NULL;
00515 
00516     assoc->init->decode = assoc->decode;
00517     assoc->init->peer_name = 
00518         odr_strdup(assoc->encode, cs_addrstr(assoc->client_link));
00519 
00520     yaz_log(log_requestdetail, "peer %s", assoc->init->peer_name);
00521 }
00522 
00523 static int srw_bend_init(association *assoc, Z_SRW_diagnostic **d, int *num, Z_SRW_PDU *sr)
00524 {
00525     statserv_options_block *cb = statserv_getcontrol();
00526     if (!assoc->init)
00527     {
00528         const char *encoding = "UTF-8";
00529         Z_External *ce;
00530         bend_initresult *binitres;
00531 
00532         yaz_log(log_requestdetail, "srw_bend_init config=%s", cb->configname);
00533         assoc_init_reset(assoc);
00534         
00535         if (sr->username)
00536         {
00537             Z_IdAuthentication *auth = (Z_IdAuthentication *)
00538                 odr_malloc(assoc->decode, sizeof(*auth));
00539             int len;
00540 
00541             len = strlen(sr->username) + 1;
00542             if (sr->password) 
00543                 len += strlen(sr->password) + 2;
00544             auth->which = Z_IdAuthentication_open;
00545             auth->u.open = (char *) odr_malloc(assoc->decode, len);
00546             strcpy(auth->u.open, sr->username);
00547             if (sr->password && *sr->password)
00548             {
00549                 strcat(auth->u.open, "/");
00550                 strcat(auth->u.open, sr->password);
00551             }
00552             assoc->init->auth = auth;
00553         }
00554 
00555 #if 1
00556         ce = yaz_set_proposal_charneg(assoc->decode, &encoding, 1, 0, 0, 1);
00557         assoc->init->charneg_request = ce->u.charNeg3;
00558 #endif
00559         assoc->backend = 0;
00560         if (!(binitres = (*cb->bend_init)(assoc->init)))
00561         {
00562             assoc->state = ASSOC_DEAD;
00563             yaz_add_srw_diagnostic(assoc->encode, d, num,
00564                             YAZ_SRW_AUTHENTICATION_ERROR, 0);
00565             return 0;
00566         }
00567         assoc->backend = binitres->handle;
00568         assoc->init->auth = 0;
00569         if (binitres->errcode)
00570         {
00571             int srw_code = yaz_diag_bib1_to_srw(binitres->errcode);
00572             assoc->state = ASSOC_DEAD;
00573             yaz_add_srw_diagnostic(assoc->encode, d, num, srw_code,
00574                                    binitres->errstring);
00575             return 0;
00576         }
00577         return 1;
00578     }
00579     return 1;
00580 }
00581 
00582 static int retrieve_fetch(association *assoc, bend_fetch_rr *rr)
00583 {
00584 #if YAZ_HAVE_XML2
00585     yaz_record_conv_t rc = 0;
00586     const char *match_schema = 0;
00587     Odr_oid *match_syntax = 0;
00588 
00589     if (assoc->server)
00590     {
00591         int r;
00592         const char *input_schema = yaz_get_esn(rr->comp);
00593         Odr_oid *input_syntax_raw = rr->request_format;
00594         
00595         const char *backend_schema = 0;
00596         Odr_oid *backend_syntax = 0;
00597 
00598         r = yaz_retrieval_request(assoc->server->retrieval,
00599                                   input_schema,
00600                                   input_syntax_raw,
00601                                   &match_schema,
00602                                   &match_syntax,
00603                                   &rc,
00604                                   &backend_schema,
00605                                   &backend_syntax);
00606         if (r == -1) /* error ? */
00607         {
00608             const char *details = yaz_retrieval_get_error(
00609                 assoc->server->retrieval);
00610 
00611             rr->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
00612             if (details)
00613                 rr->errstring = odr_strdup(rr->stream, details);
00614             return -1;
00615         }
00616         else if (r == 1 || r == 3)
00617         {
00618             const char *details = input_schema;
00619             rr->errcode =
00620                 YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
00621             if (details)
00622                 rr->errstring = odr_strdup(rr->stream, details);
00623             return -1;
00624         }
00625         else if (r == 2)
00626         {
00627             rr->errcode = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
00628             if (input_syntax_raw)
00629             {
00630                 char oidbuf[OID_STR_MAX];
00631                 oid_oid_to_dotstring(input_syntax_raw, oidbuf);
00632                 rr->errstring = odr_strdup(rr->stream, oidbuf);
00633             }
00634             return -1;
00635         }
00636         if (backend_schema)
00637         {
00638             yaz_set_esn(&rr->comp, backend_schema, odr_getmem(rr->stream));
00639         }
00640         if (backend_syntax)
00641             rr->request_format = backend_syntax;
00642     }
00643     (*assoc->init->bend_fetch)(assoc->backend, rr);
00644     if (rc && rr->record && rr->errcode == 0 && rr->len > 0)
00645     {   /* post conversion must take place .. */
00646         WRBUF output_record = wrbuf_alloc();
00647         int r = yaz_record_conv_record(rc, rr->record, rr->len, output_record);
00648         if (r)
00649         {
00650             const char *details = yaz_record_conv_get_error(rc);
00651             rr->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
00652             if (details)
00653                 rr->errstring = odr_strdup(rr->stream, details);
00654         }
00655         else
00656         {
00657             rr->len = wrbuf_len(output_record);
00658             rr->record = (char *) odr_malloc(rr->stream, rr->len);
00659             memcpy(rr->record, wrbuf_buf(output_record), rr->len);
00660         }
00661         wrbuf_destroy(output_record);
00662     }
00663     if (match_syntax)
00664         rr->output_format = match_syntax;
00665     if (match_schema)
00666         rr->schema = odr_strdup(rr->stream, match_schema);
00667     return 0;
00668 #else
00669     (*assoc->init->bend_fetch)(assoc->backend, rr);
00670 #endif
00671     return 0;
00672 }
00673 
00674 static int srw_bend_fetch(association *assoc, int pos,
00675                           Z_SRW_searchRetrieveRequest *srw_req,
00676                           Z_SRW_record *record,
00677                           const char **addinfo)
00678 {
00679     bend_fetch_rr rr;
00680     ODR o = assoc->encode;
00681 
00682     rr.setname = "default";
00683     rr.number = pos;
00684     rr.referenceId = 0;
00685     rr.request_format = odr_oiddup(assoc->decode, yaz_oid_recsyn_xml);
00686 
00687     rr.comp = (Z_RecordComposition *)
00688             odr_malloc(assoc->decode, sizeof(*rr.comp));
00689     rr.comp->which = Z_RecordComp_complex;
00690     rr.comp->u.complex = (Z_CompSpec *)
00691             odr_malloc(assoc->decode, sizeof(Z_CompSpec));
00692     rr.comp->u.complex->selectAlternativeSyntax = (bool_t *)
00693         odr_malloc(assoc->encode, sizeof(bool_t));
00694     *rr.comp->u.complex->selectAlternativeSyntax = 0;    
00695     rr.comp->u.complex->num_dbSpecific = 0;
00696     rr.comp->u.complex->dbSpecific = 0;
00697     rr.comp->u.complex->num_recordSyntax = 0; 
00698     rr.comp->u.complex->recordSyntax = 0;
00699 
00700     rr.comp->u.complex->generic = (Z_Specification *) 
00701             odr_malloc(assoc->decode, sizeof(Z_Specification));
00702 
00703     /* schema uri = recordSchema (or NULL if recordSchema is not given) */
00704     rr.comp->u.complex->generic->which = Z_Schema_uri;
00705     rr.comp->u.complex->generic->schema.uri = srw_req->recordSchema;
00706 
00707     /* ESN = recordSchema if recordSchema is present */
00708     rr.comp->u.complex->generic->elementSpec = 0;
00709     if (srw_req->recordSchema)
00710     {
00711         rr.comp->u.complex->generic->elementSpec = 
00712             (Z_ElementSpec *) odr_malloc(assoc->encode, sizeof(Z_ElementSpec));
00713         rr.comp->u.complex->generic->elementSpec->which = 
00714             Z_ElementSpec_elementSetName;
00715         rr.comp->u.complex->generic->elementSpec->u.elementSetName =
00716             srw_req->recordSchema;
00717     }
00718     
00719     rr.stream = assoc->encode;
00720     rr.print = assoc->print;
00721 
00722     rr.basename = 0;
00723     rr.len = 0;
00724     rr.record = 0;
00725     rr.last_in_set = 0;
00726     rr.errcode = 0;
00727     rr.errstring = 0;
00728     rr.surrogate_flag = 0;
00729     rr.schema = srw_req->recordSchema;
00730 
00731     if (!assoc->init->bend_fetch)
00732         return 1;
00733 
00734     retrieve_fetch(assoc, &rr);
00735 
00736     if (rr.errcode && rr.surrogate_flag)
00737     {
00738         int code = yaz_diag_bib1_to_srw(rr.errcode);
00739         yaz_mk_sru_surrogate(o, record, pos, code, rr.errstring);
00740         return 0;
00741     }
00742     else if (rr.len >= 0)
00743     {
00744         record->recordData_buf = rr.record;
00745         record->recordData_len = rr.len;
00746         record->recordPosition = odr_intdup(o, pos);
00747         record->recordSchema = odr_strdup_null(
00748             o, rr.schema ? rr.schema : srw_req->recordSchema);
00749     }
00750     if (rr.errcode)
00751     {
00752         *addinfo = rr.errstring;
00753         return rr.errcode;
00754     }
00755     return 0;
00756 }
00757 
00758 static int cql2pqf(ODR odr, const char *cql, cql_transform_t ct,
00759                    Z_Query *query_result)
00760 {
00761     /* have a CQL query and  CQL to PQF transform .. */
00762     CQL_parser cp = cql_parser_create();
00763     int r;
00764     int srw_errcode = 0;
00765     const char *add = 0;
00766     char rpn_buf[5120];
00767             
00768     r = cql_parser_string(cp, cql);
00769     if (r)
00770     {
00771         srw_errcode = YAZ_SRW_QUERY_SYNTAX_ERROR;
00772     }
00773     if (!r)
00774     {
00775         /* Syntax OK */
00776         r = cql_transform_buf(ct,
00777                               cql_parser_result(cp),
00778                               rpn_buf, sizeof(rpn_buf)-1);
00779         if (r)
00780             srw_errcode = cql_transform_error(ct, &add);
00781     }
00782     if (!r)
00783     {
00784         /* Syntax & transform OK. */
00785         /* Convert PQF string to Z39.50 to RPN query struct */
00786         YAZ_PQF_Parser pp = yaz_pqf_create();
00787         Z_RPNQuery *rpnquery = yaz_pqf_parse(pp, odr, rpn_buf);
00788         if (!rpnquery)
00789         {
00790             size_t off;
00791             const char *pqf_msg;
00792             int code = yaz_pqf_error(pp, &pqf_msg, &off);
00793             yaz_log(YLOG_WARN, "PQF Parser Error %s (code %d)",
00794                     pqf_msg, code);
00795             srw_errcode = YAZ_SRW_QUERY_SYNTAX_ERROR;
00796         }
00797         else
00798         {
00799             query_result->which = Z_Query_type_1;
00800             query_result->u.type_1 = rpnquery;
00801         }
00802         yaz_pqf_destroy(pp);
00803     }
00804     cql_parser_destroy(cp);
00805     return srw_errcode;
00806 }
00807 
00808 static int cql2pqf_scan(ODR odr, const char *cql, cql_transform_t ct,
00809                         Z_AttributesPlusTerm *result)
00810 {
00811     Z_Query query;
00812     Z_RPNQuery *rpn;
00813     int srw_error = cql2pqf(odr, cql, ct, &query);
00814     if (srw_error)
00815         return srw_error;
00816     if (query.which != Z_Query_type_1 && query.which != Z_Query_type_101)
00817         return YAZ_SRW_QUERY_SYNTAX_ERROR; /* bad query type */
00818     rpn = query.u.type_1;
00819     if (!rpn->RPNStructure) 
00820         return YAZ_SRW_QUERY_SYNTAX_ERROR; /* must be structure */
00821     if (rpn->RPNStructure->which != Z_RPNStructure_simple)
00822         return YAZ_SRW_QUERY_SYNTAX_ERROR; /* must be simple */
00823     if (rpn->RPNStructure->u.simple->which != Z_Operand_APT)
00824         return YAZ_SRW_QUERY_SYNTAX_ERROR; /* must be be attributes + term */
00825     memcpy(result, rpn->RPNStructure->u.simple->u.attributesPlusTerm,
00826            sizeof(*result));
00827     return 0;
00828 }
00829                    
00830 
00831 static int ccl2pqf(ODR odr, const Odr_oct *ccl, CCL_bibset bibset,
00832                    bend_search_rr *bsrr) {
00833     char *ccl0;
00834     struct ccl_rpn_node *node;
00835     int errcode, pos;
00836 
00837     ccl0 = odr_strdupn(odr, (char*) ccl->buf, ccl->len);
00838     if ((node = ccl_fin