• Main Page
  • Data Structures
  • Files

zoom-c.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  */
00010 #include <assert.h>
00011 #include <string.h>
00012 #include <errno.h>
00013 #include "zoom-p.h"
00014 
00015 #include <yaz/yaz-util.h>
00016 #include <yaz/xmalloc.h>
00017 #include <yaz/otherinfo.h>
00018 #include <yaz/log.h>
00019 #include <yaz/pquery.h>
00020 #include <yaz/marcdisp.h>
00021 #include <yaz/diagbib1.h>
00022 #include <yaz/charneg.h>
00023 #include <yaz/ill.h>
00024 #include <yaz/srw.h>
00025 #include <yaz/cql.h>
00026 #include <yaz/ccl.h>
00027 #include <yaz/query-charset.h>
00028 #include <yaz/copy_types.h>
00029 #include <yaz/snprintf.h>
00030 
00031 static int log_api = 0;
00032 static int log_details = 0;
00033 
00034 typedef enum {
00035     zoom_pending,
00036     zoom_complete
00037 } zoom_ret;
00038 
00039 static void resultset_destroy(ZOOM_resultset r);
00040 static zoom_ret ZOOM_connection_send_init(ZOOM_connection c);
00041 static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out);
00042 static char *cql2pqf(ZOOM_connection c, const char *cql);
00043 
00044 ZOOM_API(const char *) ZOOM_get_event_str(int event)
00045 {
00046     static const char *ar[] = {
00047         "NONE",
00048         "CONNECT",
00049         "SEND_DATA",
00050         "RECV_DATA",
00051         "TIMEOUT",
00052         "UNKNOWN",
00053         "SEND_APDU",
00054         "RECV_APDU",
00055         "RECV_RECORD",
00056         "RECV_SEARCH",
00057         "END"
00058     };
00059     return ar[event];
00060 }
00061 
00062 /*
00063  * This wrapper is just for logging failed lookups.  It would be nicer
00064  * if it could cause failure when a lookup fails, but that's hard.
00065  */
00066 static Odr_oid *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
00067                                      oid_class oid_class, const char *str) {
00068     Odr_oid *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
00069                                      c->odr_out);
00070     if (res == 0)
00071         yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
00072                 c, (int) oid_class, str);
00073     return res;
00074 }
00075 
00076 
00077 static void initlog(void)
00078 {
00079     static int log_level_initialized = 0;
00080     if (!log_level_initialized)
00081     {
00082         log_api = yaz_log_module_level("zoom");
00083         log_details = yaz_log_module_level("zoomdetails");
00084         log_level_initialized = 1;
00085     }
00086 }
00087 
00088 static ZOOM_Event ZOOM_Event_create(int kind)
00089 {
00090     ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
00091     event->kind = kind;
00092     event->next = 0;
00093     event->prev = 0;
00094     yaz_log(log_details, "ZOOM_Event_create(kind=%d)", kind);
00095     return event;
00096 }
00097 
00098 static void ZOOM_Event_destroy(ZOOM_Event event)
00099 {
00100     xfree(event);
00101 }
00102 
00103 static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
00104 {
00105     if (c->m_queue_back)
00106     {
00107         c->m_queue_back->prev = event;
00108         assert(c->m_queue_front);
00109     }
00110     else
00111     {
00112         assert(!c->m_queue_front);
00113         c->m_queue_front = event;
00114     }
00115     event->next = c->m_queue_back;
00116     event->prev = 0;
00117     c->m_queue_back = event;
00118 }
00119 
00120 static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
00121 {
00122     ZOOM_Event event = c->m_queue_front;
00123     if (!event)
00124     {
00125         c->last_event = ZOOM_EVENT_NONE;
00126         return 0;
00127     }
00128     assert(c->m_queue_back);
00129     c->m_queue_front = event->prev;
00130     if (c->m_queue_front)
00131     {
00132         assert(c->m_queue_back);
00133         c->m_queue_front->next = 0;
00134     }
00135     else
00136         c->m_queue_back = 0;
00137     c->last_event = event->kind;
00138     return event;
00139 }
00140 
00141 static void ZOOM_connection_remove_events(ZOOM_connection c)
00142 {
00143     ZOOM_Event event;
00144     while ((event = ZOOM_connection_get_event(c)))
00145         ZOOM_Event_destroy(event);
00146 }
00147 
00148 ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
00149 {
00150     ZOOM_Event event = c->m_queue_front;
00151 
00152     return event ? event->kind : ZOOM_EVENT_NONE;
00153 }
00154 
00155 void ZOOM_connection_remove_tasks(ZOOM_connection c);
00156 
00157 static void set_dset_error(ZOOM_connection c, int error,
00158                            const char *dset,
00159                            const char *addinfo, const char *addinfo2)
00160 {
00161     char *cp;
00162 
00163     xfree(c->addinfo);
00164     c->addinfo = 0;
00165     c->error = error;
00166     if (!c->diagset || strcmp(dset, c->diagset))
00167     {
00168         xfree(c->diagset);
00169         c->diagset = xstrdup(dset);
00170         /* remove integer part from SRW diagset .. */
00171         if ((cp = strrchr(c->diagset, '/')))
00172             *cp = '\0';
00173     }
00174     if (addinfo && addinfo2)
00175     {
00176         c->addinfo = (char*) xmalloc(strlen(addinfo) + strlen(addinfo2) + 2);
00177         strcpy(c->addinfo, addinfo);
00178         strcat(c->addinfo, addinfo2);
00179     }
00180     else if (addinfo)
00181         c->addinfo = xstrdup(addinfo);
00182     if (error != ZOOM_ERROR_NONE)
00183     {
00184         yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
00185                 c, c->host_port ? c->host_port : "<>", dset, error,
00186                 addinfo ? addinfo : "",
00187                 addinfo2 ? addinfo2 : "");
00188         ZOOM_connection_remove_tasks(c);
00189     }
00190 }
00191 
00192 static int uri_to_code(const char *uri)
00193 {
00194     int code = 0;       
00195     const char *cp;
00196     if ((cp = strrchr(uri, '/')))
00197         code = atoi(cp+1);
00198     return code;
00199 }
00200 
00201 #if YAZ_HAVE_XML2
00202 static void set_HTTP_error(ZOOM_connection c, int error,
00203                            const char *addinfo, const char *addinfo2)
00204 {
00205     set_dset_error(c, error, "HTTP", addinfo, addinfo2);
00206 }
00207 
00208 static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
00209 {
00210     const char *uri = d->uri;
00211     if (uri)
00212         set_dset_error(c, uri_to_code(uri), uri, d->details, 0);
00213 }
00214 
00215 #endif
00216 
00217 
00218 static void set_ZOOM_error(ZOOM_connection c, int error,
00219                            const char *addinfo)
00220 {
00221     set_dset_error(c, error, "ZOOM", addinfo, 0);
00222 }
00223 
00224 static void clear_error(ZOOM_connection c)
00225 {
00226     /*
00227      * If an error is tied to an operation then it's ok to clear: for
00228      * example, a diagnostic returned from a search is cleared by a
00229      * subsequent search.  However, problems such as Connection Lost
00230      * or Init Refused are not cleared, because they are not
00231      * recoverable: doing another search doesn't help.
00232      */
00233 
00234     ZOOM_connection_remove_events(c);
00235     switch (c->error)
00236     {
00237     case ZOOM_ERROR_CONNECT:
00238     case ZOOM_ERROR_MEMORY:
00239     case ZOOM_ERROR_DECODE:
00240     case ZOOM_ERROR_CONNECTION_LOST:
00241     case ZOOM_ERROR_INIT:
00242     case ZOOM_ERROR_INTERNAL:
00243     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
00244         break;
00245     default:
00246         set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
00247     }
00248 }
00249 
00250 void ZOOM_connection_show_task(ZOOM_task task)
00251 {
00252     switch(task->which)
00253     {
00254     case ZOOM_TASK_SEARCH:
00255         yaz_log(YLOG_LOG, "search p=%p", task);
00256         break;
00257     case ZOOM_TASK_RETRIEVE:
00258         yaz_log(YLOG_LOG, "retrieve p=%p", task);
00259         break;
00260     case ZOOM_TASK_CONNECT:
00261         yaz_log(YLOG_LOG, "connect p=%p", task);
00262         break;
00263     case ZOOM_TASK_SCAN:
00264         yaz_log(YLOG_LOG, "scan p=%p", task);
00265         break;
00266     }
00267 }
00268 
00269 void ZOOM_connection_show_tasks(ZOOM_connection c)
00270 {
00271     ZOOM_task task;
00272     yaz_log(YLOG_LOG, "connection p=%p tasks", c);
00273     for (task = c->tasks; task; task = task->next)
00274         ZOOM_connection_show_task(task);
00275 }
00276 
00277 ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which)
00278 {
00279     ZOOM_task *taskp = &c->tasks;
00280     while (*taskp)
00281         taskp = &(*taskp)->next;
00282     *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
00283     (*taskp)->running = 0;
00284     (*taskp)->which = which;
00285     (*taskp)->next = 0;
00286     clear_error(c);
00287     return *taskp;
00288 }
00289 
00290 ZOOM_API(int) ZOOM_connection_is_idle(ZOOM_connection c)
00291 {
00292     return c->tasks ? 0 : 1;
00293 }
00294 
00295 ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
00296 {
00297     ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
00298 
00299     task->next = c->tasks;
00300     c->tasks = task;
00301 
00302     task->running = 0;
00303     task->which = which;
00304     clear_error(c);
00305     return task;
00306 }
00307 
00308 void ZOOM_connection_remove_task(ZOOM_connection c)
00309 {
00310     ZOOM_task task = c->tasks;
00311 
00312     if (task)
00313     {
00314         c->tasks = task->next;
00315         switch (task->which)
00316         {
00317         case ZOOM_TASK_SEARCH:
00318             resultset_destroy(task->u.search.resultset);
00319             xfree(task->u.search.syntax);
00320             xfree(task->u.search.elementSetName);
00321             break;
00322         case ZOOM_TASK_RETRIEVE:
00323             resultset_destroy(task->u.retrieve.resultset);
00324             xfree(task->u.retrieve.syntax);
00325             xfree(task->u.retrieve.elementSetName);
00326             break;
00327         case ZOOM_TASK_CONNECT:
00328             break;
00329         case ZOOM_TASK_SCAN:
00330             ZOOM_scanset_destroy(task->u.scan.scan);
00331             break;
00332         case ZOOM_TASK_PACKAGE:
00333             ZOOM_package_destroy(task->u.package);
00334             break;
00335         case ZOOM_TASK_SORT:
00336             resultset_destroy(task->u.sort.resultset);
00337             ZOOM_query_destroy(task->u.sort.q);
00338             break;
00339         default:
00340             assert(0);
00341         }
00342         xfree(task);
00343 
00344         if (!c->tasks)
00345         {
00346             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
00347             ZOOM_connection_put_event(c, event);
00348         }
00349     }
00350 }
00351 
00352 static int ZOOM_connection_exec_task(ZOOM_connection c);
00353 
00354 void ZOOM_connection_remove_tasks(ZOOM_connection c)
00355 {
00356     while (c->tasks)
00357         ZOOM_connection_remove_task(c);
00358 }
00359 
00360 static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
00361                                        const char *syntax,
00362                                        const char *elementSetName);
00363 
00364 ZOOM_API(ZOOM_connection)
00365     ZOOM_connection_create(ZOOM_options options)
00366 {
00367     ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
00368 
00369     initlog();
00370 
00371     yaz_log(log_api, "%p ZOOM_connection_create", c);
00372 
00373     c->proto = PROTO_Z3950;
00374     c->cs = 0;
00375     ZOOM_connection_set_mask(c, 0);
00376     c->reconnect_ok = 0;
00377     c->state = STATE_IDLE;
00378     c->addinfo = 0;
00379     c->diagset = 0;
00380     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
00381     c->buf_in = 0;
00382     c->len_in = 0;
00383     c->buf_out = 0;
00384     c->len_out = 0;
00385     c->resultsets = 0;
00386 
00387     c->options = ZOOM_options_create_with_parent(options);
00388 
00389     c->host_port = 0;
00390     c->path = 0;
00391     c->proxy = 0;
00392     
00393     c->charset = c->lang = 0;
00394 
00395     c->cookie_out = 0;
00396     c->cookie_in = 0;
00397     c->client_IP = 0;
00398     c->tasks = 0;
00399 
00400     c->user = 0;
00401     c->group = 0;
00402     c->password = 0;
00403 
00404     c->maximum_record_size = 0;
00405     c->preferred_message_size = 0;
00406 
00407     c->odr_in = odr_createmem(ODR_DECODE);
00408     c->odr_out = odr_createmem(ODR_ENCODE);
00409 
00410     c->async = 0;
00411     c->support_named_resultsets = 0;
00412     c->last_event = ZOOM_EVENT_NONE;
00413 
00414     c->m_queue_front = 0;
00415     c->m_queue_back = 0;
00416 
00417     c->sru_version = 0;
00418     return c;
00419 }
00420 
00421 
00422 /* set database names. Take local databases (if set); otherwise
00423    take databases given in ZURL (if set); otherwise use Default */
00424 static char **set_DatabaseNames(ZOOM_connection con, ZOOM_options options,
00425                                 int *num, ODR odr)
00426 {
00427     char **databaseNames;
00428     const char *cp = ZOOM_options_get(options, "databaseName");
00429     
00430     if ((!cp || !*cp) && con->host_port)
00431     {
00432         if (strncmp(con->host_port, "unix:", 5) == 0)
00433             cp = strchr(con->host_port+5, ':');
00434         else
00435             cp = strchr(con->host_port, '/');
00436         if (cp)
00437             cp++;
00438     }
00439     if (!cp)
00440         cp = "Default";
00441     nmem_strsplit(odr_getmem(odr), "+", cp,  &databaseNames, num);
00442     return databaseNames;
00443 }
00444 
00445 ZOOM_API(ZOOM_connection)
00446     ZOOM_connection_new(const char *host, int portnum)
00447 {
00448     ZOOM_connection c = ZOOM_connection_create(0);
00449 
00450     ZOOM_connection_connect(c, host, portnum);
00451     return c;
00452 }
00453 
00454 static zoom_sru_mode get_sru_mode_from_string(const char *s)
00455 {
00456     if (!s || !*s)
00457         return zoom_sru_soap;
00458     if (!yaz_matchstr(s, "soap"))
00459         return zoom_sru_soap;
00460     else if (!yaz_matchstr(s, "get"))
00461         return zoom_sru_get;
00462     else if (!yaz_matchstr(s, "post"))
00463         return zoom_sru_post;
00464     return zoom_sru_error;
00465 }
00466 
00467 ZOOM_API(void)
00468     ZOOM_connection_connect(ZOOM_connection c,
00469                             const char *host, int portnum)
00470 {
00471     const char *val;
00472     ZOOM_task task;
00473 
00474     initlog();
00475 
00476     yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
00477             c, host ? host : "null", portnum);
00478 
00479     set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
00480     ZOOM_connection_remove_tasks(c);
00481 
00482     if (ZOOM_options_get_bool(c->options, "apdulog", 0))
00483     {
00484         c->odr_print = odr_createmem(ODR_PRINT);
00485         odr_setprint(c->odr_print, yaz_log_file());
00486     }
00487     else
00488         c->odr_print = 0;
00489 
00490     if (c->cs)
00491     {
00492         yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
00493         c->reconnect_ok = 1;
00494         return;
00495     }
00496     yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
00497     xfree(c->proxy);
00498     c->proxy = 0;
00499     val = ZOOM_options_get(c->options, "proxy");
00500     if (val && *val)
00501     {
00502         yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
00503         c->proxy = xstrdup(val);
00504     }
00505 
00506     xfree(c->charset);
00507     c->charset = 0;
00508     val = ZOOM_options_get(c->options, "charset");
00509     if (val && *val)
00510     {
00511         yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
00512         c->charset = xstrdup(val);
00513     }
00514 
00515     xfree(c->lang);
00516     val = ZOOM_options_get(c->options, "lang");
00517     if (val && *val)
00518     {
00519         yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
00520         c->lang = xstrdup(val);
00521     }
00522     else
00523         c->lang = 0;
00524 
00525     if (host)
00526     {
00527         xfree(c->host_port);
00528         if (portnum)
00529         {
00530             char hostn[128];
00531             sprintf(hostn, "%.80s:%d", host, portnum);
00532             c->host_port = xstrdup(hostn);
00533         }
00534         else
00535             c->host_port = xstrdup(host);
00536     }        
00537 
00538     {
00539         /*
00540          * If the "<scheme>:" part of the host string is preceded by one
00541          * or more comma-separated <name>=<value> pairs, these are taken
00542          * to be options to be set on the connection object.  Among other
00543          * applications, this facility can be used to embed authentication
00544          * in a host string:
00545          *          user=admin,password=secret,tcp:localhost:9999
00546          */
00547         char *remainder = c->host_port;
00548         char *pcolon = strchr(remainder, ':');
00549         char *pcomma;
00550         char *pequals;
00551         while ((pcomma = strchr(remainder, ',')) != 0 &&
00552                (pcolon == 0 || pcomma < pcolon)) {
00553             *pcomma = '\0';
00554             if ((pequals = strchr(remainder, '=')) != 0) {
00555                 *pequals = '\0';
00556                 /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/
00557                 ZOOM_connection_option_set(c, remainder, pequals+1);
00558             }
00559             remainder = pcomma+1;
00560         }
00561 
00562         if (remainder != c->host_port) {
00563             xfree(c->host_port);
00564             c->host_port = xstrdup(remainder);
00565             /*printf("# reset hp='%s'\n", remainder);*/
00566         }
00567     }
00568 
00569     val = ZOOM_options_get(c->options, "sru");
00570     c->sru_mode = get_sru_mode_from_string(val);
00571 
00572     xfree(c->sru_version);
00573     val = ZOOM_options_get(c->options, "sru_version");
00574     c->sru_version = xstrdup(val ? val : "1.2");
00575 
00576     ZOOM_options_set(c->options, "host", c->host_port);
00577 
00578     xfree(c->cookie_out);
00579     c->cookie_out = 0;
00580     val = ZOOM_options_get(c->options, "cookie");
00581     if (val && *val)
00582     { 
00583         yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
00584         c->cookie_out = xstrdup(val);
00585     }
00586 
00587     xfree(c->client_IP);
00588     c->client_IP = 0;
00589     val = ZOOM_options_get(c->options, "clientIP");
00590     if (val && *val)
00591     {
00592         yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
00593                 c, val);
00594         c->client_IP = xstrdup(val);
00595     }
00596 
00597     xfree(c->group);
00598     c->group = 0;
00599     val = ZOOM_options_get(c->options, "group");
00600     if (val && *val)
00601         c->group = xstrdup(val);
00602 
00603     xfree(c->user);
00604     c->user = 0;
00605     val = ZOOM_options_get(c->options, "user");
00606     if (val && *val)
00607         c->user = xstrdup(val);
00608 
00609     xfree(c->password);
00610     c->password = 0;
00611     val = ZOOM_options_get(c->options, "password");
00612     if (!val)
00613         val = ZOOM_options_get(c->options, "pass");
00614 
00615     if (val && *val)
00616         c->password = xstrdup(val);
00617     
00618     c->maximum_record_size =
00619         ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
00620     c->preferred_message_size =
00621         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
00622 
00623     c->async = ZOOM_options_get_bool(c->options, "async", 0);
00624     yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
00625  
00626     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
00627 
00628     if (!c->async)
00629     {
00630         while (ZOOM_event(1, &c))
00631             ;
00632     }
00633 }
00634 
00635 ZOOM_API(ZOOM_query)
00636     ZOOM_query_create(void)
00637 {
00638     ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
00639 
00640     yaz_log(log_details, "%p ZOOM_query_create", s);
00641     s->refcount = 1;
00642     s->z_query = 0;
00643     s->sort_spec = 0;
00644     s->odr = odr_createmem(ODR_ENCODE);
00645     s->query_string = 0;
00646 
00647     return s;
00648 }
00649 
00650 ZOOM_API(void)
00651     ZOOM_query_destroy(ZOOM_query s)
00652 {
00653     if (!s)
00654         return;
00655 
00656     (s->refcount)--;
00657     yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
00658     if (s->refcount == 0)
00659     {
00660         odr_destroy(s->odr);
00661         xfree(s);
00662     }
00663 }
00664 
00665 ZOOM_API(int)
00666     ZOOM_query_prefix(ZOOM_query s, const char *str)
00667 {
00668     s->query_string = odr_strdup(s->odr, str);
00669     s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
00670     s->z_query->which = Z_Query_type_1;
00671     s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
00672     if (!s->z_query->u.type_1)
00673     {
00674         yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
00675         s->z_query = 0;
00676         return -1;
00677     }
00678     yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
00679     return 0;
00680 }
00681 
00682 ZOOM_API(int)
00683     ZOOM_query_cql(ZOOM_query s, const char *str)
00684 {
00685     Z_External *ext;
00686 
00687     s->query_string = odr_strdup(s->odr, str);
00688 
00689     ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
00690     ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
00691     ext->indirect_reference = 0;
00692     ext->descriptor = 0;
00693     ext->which = Z_External_CQL;
00694     ext->u.cql = s->query_string;
00695     
00696     s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
00697     s->z_query->which = Z_Query_type_104;
00698     s->z_query->u.type_104 =  ext;
00699 
00700     yaz_log(log_details, "%p ZOOM_query_cql str=%s", s, str);
00701 
00702     return 0;
00703 }
00704 
00705 /*
00706  * Translate the CQL string client-side into RPN which is passed to
00707  * the server.  This is useful for server's that don't themselves
00708  * support CQL, for which ZOOM_query_cql() is useless.  `conn' is used
00709  * only as a place to stash diagnostics if compilation fails; if this
00710  * information is not needed, a null pointer may be used.
00711  */
00712 ZOOM_API(int)
00713     ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
00714 {
00715     char *rpn;
00716     int ret;
00717     ZOOM_connection freeme = 0;
00718 
00719     yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
00720     if (conn == 0)
00721         conn = freeme = ZOOM_connection_create(0);
00722 
00723     rpn = cql2pqf(conn, str);
00724     if (freeme != 0)
00725         ZOOM_connection_destroy(freeme);
00726     if (rpn == 0)
00727         return -1;
00728 
00729     ret = ZOOM_query_prefix(s, rpn);
00730     xfree(rpn);
00731     return ret;
00732 }
00733 
00734 /*
00735  * Analogous in every way to ZOOM_query_cql2rpn(), except that there
00736  * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
00737  * to the server, as the YAZ GFS doesn't know how to handle this.
00738  */
00739 ZOOM_API(int)
00740     ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
00741                        int *ccl_error, const char **error_string,
00742                        int *error_pos)
00743 {
00744     int ret;
00745     struct ccl_rpn_node *rpn;
00746     CCL_bibset bibset = ccl_qual_mk();
00747 
00748     if (config)
00749         ccl_qual_buf(bibset, config);
00750 
00751     rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
00752     if (!rpn)
00753     {
00754         *error_string = ccl_err_msg(*ccl_error);
00755         ret = -1;
00756     }
00757     else
00758     {
00759         WRBUF wr = wrbuf_alloc();
00760         ccl_pquery(wr, rpn);
00761         ccl_rpn_delete(rpn);
00762         ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
00763         wrbuf_destroy(wr);
00764     }
00765     ccl_qual_rm(&bibset);
00766     return ret;
00767 }
00768 
00769 ZOOM_API(int)
00770     ZOOM_query_sortby(ZOOM_query s, const char *criteria)
00771 {
00772     s->sort_spec = yaz_sort_spec(s->odr, criteria);
00773     if (!s->sort_spec)
00774     {
00775         yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
00776                 s, criteria);
00777         return -1;
00778     }
00779     yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
00780     return 0;
00781 }
00782 
00783 static zoom_ret do_write(ZOOM_connection c);
00784 
00785 ZOOM_API(void)
00786     ZOOM_connection_destroy(ZOOM_connection c)
00787 {
00788     ZOOM_resultset r;
00789     if (!c)
00790         return;
00791     yaz_log(log_api, "%p ZOOM_connection_destroy", c);
00792     if (c->cs)
00793         cs_close(c->cs);
00794     for (r = c->resultsets; r; r = r->next)
00795         r->connection = 0;
00796 
00797     xfree(c->buf_in);
00798     xfree(c->addinfo);
00799     xfree(c->diagset);
00800     odr_destroy(c->odr_in);
00801     odr_destroy(c->odr_out);
00802     if (c->odr_print)
00803     {
00804         odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
00805         odr_destroy(c->odr_print);
00806     }
00807     ZOOM_options_destroy(c->options);
00808     ZOOM_connection_remove_tasks(c);
00809     ZOOM_connection_remove_events(c);
00810     xfree(c->host_port);
00811     xfree(c->path);
00812     xfree(c->proxy);
00813     xfree(c->charset);
00814     xfree(c->lang);
00815     xfree(c->cookie_out);
00816     xfree(c->cookie_in);
00817     xfree(c->client_IP);
00818     xfree(c->user);
00819     xfree(c->group);
00820     xfree(c->password);
00821     xfree(c->sru_version);
00822     xfree(c);
00823 }
00824 
00825 void ZOOM_resultset_addref(ZOOM_resultset r)
00826 {
00827     if (r)
00828     {
00829         (r->refcount)++;
00830         yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
00831                 r, r->refcount);
00832     }
00833 }
00834 
00835 ZOOM_resultset ZOOM_resultset_create(void)
00836 {
00837     int i;
00838     ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
00839 
00840     initlog();
00841 
00842     yaz_log(log_details, "%p ZOOM_resultset_create", r);
00843     r->refcount = 1;
00844     r->size = 0;
00845     r->odr = odr_createmem(ODR_ENCODE);
00846     r->piggyback = 1;
00847     r->setname = 0;
00848     r->schema = 0;
00849     r->step = 0;
00850     for (i = 0; i<RECORD_HASH_SIZE; i++)
00851         r->record_hash[i] = 0;
00852     r->r_sort_spec = 0;
00853     r->query = 0;
00854     r->connection = 0;
00855     r->next = 0;
00856     r->databaseNames = 0;
00857     r->num_databaseNames = 0;
00858     return r;
00859 }
00860 
00861 ZOOM_API(ZOOM_resultset)
00862     ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
00863 {
00864     ZOOM_resultset r;
00865     ZOOM_query s = ZOOM_query_create();
00866 
00867     ZOOM_query_prefix(s, q);
00868 
00869     r = ZOOM_connection_search(c, s);
00870     ZOOM_query_destroy(s);
00871     return r;
00872 }
00873 
00874 ZOOM_API(ZOOM_resultset)
00875     ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
00876 {
00877     ZOOM_resultset r = ZOOM_resultset_create();
00878     ZOOM_task task;
00879     const char *cp;
00880     int start, count;
00881     const char *syntax, *elementSetName;
00882 
00883     yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
00884     r->r_sort_spec = q->sort_spec;
00885     r->query = q;
00886 
00887     r->options = ZOOM_options_create_with_parent(c->options);
00888     
00889     start = ZOOM_options_get_int(r->options, "start", 0);
00890     count = ZOOM_options_get_int(r->options, "count", 0);
00891     {
00892         /* If "presentChunk" is defined use that; otherwise "step" */
00893         const char *cp = ZOOM_options_get(r->options, "presentChunk");
00894         r->step = ZOOM_options_get_int(r->options,
00895                                        (cp != 0 ? "presentChunk": "step"), 0);
00896     }
00897     r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
00898     cp = ZOOM_options_get(r->options, "setname");
00899     if (cp)
00900         r->setname = xstrdup(cp);
00901     cp = ZOOM_options_get(r->options, "schema");
00902     if (cp)
00903         r->schema = xstrdup(cp);
00904 
00905     r->databaseNames = set_DatabaseNames(c, c->options, &r->num_databaseNames,
00906                                          r->odr);
00907     
00908     r->connection = c;
00909 
00910     r->next = c->resultsets;
00911     c->resultsets = r;
00912 
00913     
00914 
00915     if (c->host_port && c->proto == PROTO_HTTP)
00916     {
00917         if (!c->cs)
00918         {
00919             yaz_log(log_details, "ZOOM_connection_search: no comstack");
00920             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
00921         }
00922         else
00923         {
00924             yaz_log(log_details, "ZOOM_connection_search: reconnect");
00925             c->reconnect_ok = 1;
00926         }
00927     }
00928 
00929     task = ZOOM_connection_add_task(c, ZOOM_TASK_SEARCH);
00930     task->u.search.resultset = r;
00931     task->u.search.start = start;
00932     task->u.search.count = count;
00933     task->u.search.recv_search_fired = 0;
00934 
00935     syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
00936     task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
00937     elementSetName = ZOOM_options_get(r->options, "elementSetName");
00938     task->u.search.elementSetName = elementSetName 
00939         ? xstrdup(elementSetName) : 0;
00940    
00941     ZOOM_resultset_addref(r);
00942 
00943     (q->refcount)++;
00944 
00945     if (!c->async)
00946     {
00947         while (ZOOM_event(1, &c))
00948             ;
00949     }
00950     return r;
00951 }
00952 
00953 ZOOM_API(void)
00954     ZOOM_resultset_sort(ZOOM_resultset r,
00955                          const char *sort_type, const char *sort_spec)
00956 {
00957     (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
00958 }
00959 
00960 ZOOM_API(int)
00961     ZOOM_resultset_sort1(ZOOM_resultset r,
00962                          const char *sort_type, const char *sort_spec)
00963 {
00964     ZOOM_connection c = r->connection;
00965     ZOOM_task task;
00966     ZOOM_query newq;
00967 
00968     newq = ZOOM_query_create();
00969     if (ZOOM_query_sortby(newq, sort_spec) < 0)
00970         return -1;
00971 
00972     yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
00973             r, r, sort_type, sort_spec);
00974     if (!c)
00975         return 0;
00976 
00977     if (c->host_port && c->proto == PROTO_HTTP)
00978     {
00979         if (!c->cs)
00980         {
00981             yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
00982             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
00983         }
00984         else
00985         {
00986             yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
00987                     r);
00988             c->reconnect_ok = 1;
00989         }
00990     }
00991     
00992     ZOOM_resultset_cache_reset(r);
00993     task = ZOOM_connection_add_task(c, ZOOM_TASK_SORT);
00994     task->u.sort.resultset = r;
00995     task->u.sort.q = newq;
00996 
00997     ZOOM_resultset_addref(r);  
00998 
00999     if (!c->async)
01000     {
01001         while (ZOOM_event(1, &c))
01002             ;
01003     }
01004 
01005     return 0;
01006 }
01007 
01008 ZOOM_API(void)
01009     ZOOM_resultset_cache_reset(ZOOM_resultset r)
01010 {
01011     int i;
01012     for (i = 0; i<RECORD_HASH_SIZE; i++)
01013     {
01014         ZOOM_record_cache rc;
01015         for (rc = r->record_hash[i]; rc; rc = rc->next)
01016         {
01017             if (rc->rec.wrbuf_marc)
01018                 wrbuf_destroy(rc->rec.wrbuf_marc);
01019             if (rc->rec.wrbuf_iconv)
01020                 wrbuf_destroy(rc->rec.wrbuf_iconv);
01021             if (rc->rec.wrbuf_opac)
01022                 wrbuf_destroy(rc->rec.wrbuf_opac);
01023         }
01024         r->record_hash[i] = 0;
01025     }
01026 }
01027 
01028 ZOOM_API(void)
01029     ZOOM_resultset_destroy(ZOOM_resultset r)
01030 {
01031     resultset_destroy(r);
01032 }
01033 
01034 static void resultset_destroy(ZOOM_resultset r)
01035 {
01036     if (!r)
01037         return;
01038     (r->refcount)--;
01039     yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
01040             r, r, r->refcount);
01041     if (r->refcount == 0)
01042     {
01043         ZOOM_resultset_cache_reset(r);
01044 
01045         if (r->connection)
01046         {
01047             /* remove ourselves from the resultsets in connection */
01048             ZOOM_resultset *rp = &r->connection->resultsets;
01049             while (1)
01050             {
01051                 assert(*rp);   /* we must be in this list!! */
01052                 if (*rp == r)
01053                 {   /* OK, we're here - take us out of it */
01054                     *rp = (*rp)->next;
01055                     break;
01056                 }
01057                 rp = &(*rp)->next;
01058             }
01059         }
01060         ZOOM_query_destroy(r->query);
01061         ZOOM_options_destroy(r->options);
01062         odr_destroy(r->odr);
01063         xfree(r->setname);
01064         xfree(r->schema);
01065         xfree(r);
01066     }
01067 }
01068 
01069 ZOOM_API(size_t)
01070     ZOOM_resultset_size(ZOOM_resultset r)
01071 {
<