backtrace: show error if pipe fails YAZ-805
[yaz-moved-to-github.git] / ztest / ztest.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /** \file
6  * \brief yaz-ztest Generic Frontend Server
7  */
8 #if HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12
13 #include <stdio.h>
14 #include <math.h>
15 #include <stdlib.h>
16
17 #if HAVE_SYS_TIME_H
18 #include <sys/time.h>
19 #endif
20 #if HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23 #if HAVE_SYS_SELECT_H
24 #include <sys/select.h>
25 #endif
26 #ifdef WIN32
27 #include <windows.h>
28 #endif
29
30 #include <yaz/log.h>
31 #include <yaz/backend.h>
32 #include <yaz/ill.h>
33 #include <yaz/diagbib1.h>
34 #include <yaz/otherinfo.h>
35 #include <yaz/facet.h>
36 #include <yaz/backtrace.h>
37
38 #include "ztest.h"
39
40 static int log_level=0;
41 static int log_level_set=0;
42
43 struct delay {
44     double d1;
45     double d2;
46 };
47
48 struct result_set {
49     char *name;
50     char *db;
51     Odr_int hits;
52     struct delay search_delay;
53     struct delay present_delay;
54     struct delay fetch_delay;
55     struct result_set *next;
56 };
57
58 struct session_handle {
59     struct result_set *result_sets;
60 };
61
62 int ztest_search(void *handle, bend_search_rr *rr);
63 int ztest_sort(void *handle, bend_sort_rr *rr);
64 int ztest_present(void *handle, bend_present_rr *rr);
65 int ztest_esrequest(void *handle, bend_esrequest_rr *rr);
66 int ztest_delete(void *handle, bend_delete_rr *rr);
67
68 static struct result_set *get_set(struct session_handle *sh, const char *name)
69 {
70     struct result_set *set = sh->result_sets;
71     for (; set; set = set->next)
72         if (!strcmp(name, set->name))
73             return set;
74     return 0;
75 }
76
77 static void remove_sets(struct session_handle *sh)
78 {
79     struct result_set *set = sh->result_sets;
80     while (set)
81     {
82         struct result_set *set_next = set->next;
83         xfree(set->name);
84         xfree(set->db);
85         xfree(set);
86         set = set_next;
87     }
88     sh->result_sets = 0;
89 }
90
91 /** \brief use term value as hit count
92     \param s RPN structure
93     \param hash value for compuation
94     \return >= 0: search term number or -1: not found
95
96     Traverse RPN tree 'in order' and use term value as hit count.
97     Only terms  that looks a numeric is used.. Returns -1 if
98     no sub tree has a hit count term
99 */
100 static Odr_int get_term_hit(Z_RPNStructure *s, unsigned *hash)
101 {
102     Odr_int h = -1;
103     switch(s->which)
104     {
105     case Z_RPNStructure_simple:
106         if (s->u.simple->which == Z_Operand_APT)
107         {
108             Z_AttributesPlusTerm *apt = s->u.simple->u.attributesPlusTerm;
109             if (apt->term->which == Z_Term_general)
110             {
111                 Odr_oct *oct = apt->term->u.general;
112                 if (oct->len > 0 && oct->buf[0] >= '0' && oct->buf[0] <= '9')
113                 {
114                     WRBUF hits_str = wrbuf_alloc();
115                     wrbuf_write(hits_str, (const char *) oct->buf, oct->len);
116                     h = odr_atoi(wrbuf_cstr(hits_str));
117                     wrbuf_destroy(hits_str);
118                 }
119                 else
120                 {
121                     int i;
122                     for (i = 0; i < oct->len; i++)
123                         *hash = *hash * 65509 + oct->buf[i];
124                 }
125             }
126         }
127         break;
128     case Z_RPNStructure_complex:
129         h = get_term_hit(s->u.complex->s1, hash);
130         if (h == -1)
131             h = get_term_hit(s->u.complex->s2, hash);
132         break;
133     }
134     return h;
135 }
136
137 /** \brief gets hit count for numeric terms in RPN queries
138     \param q RPN Query
139     \return number of hits
140
141     This is just for testing.. A real database of course uses
142     the content of a database to establish a value.. In our case, we
143     have a way to trigger a certain hit count. Good for testing of
144     client applications etc
145 */
146 static Odr_int get_hit_count(Z_Query *q)
147 {
148     if (q->which == Z_Query_type_1 || q->which == Z_Query_type_101)
149     {
150         unsigned hash = 0;
151         Odr_int h = -1;
152         h = get_term_hit(q->u.type_1->RPNStructure, &hash);
153         if (h == -1)
154             h = hash % 24;
155         return h;
156     }
157     else if (q->which == Z_Query_type_104 &&
158              q->u.type_104->which == Z_External_CQL)
159     {
160         unsigned hash = 0;
161         const char *cql = q->u.type_104->u.cql;
162         int i;
163         for (i = 0; cql[i]; i++)
164             hash = hash * 65509 + cql[i];
165         return hash % 24;
166     }
167     else
168         return 24;
169 }
170
171 /** \brief checks if it's a dummy Slow database
172     \param basename database name to check
173     \param association backend association (or NULL if not available)
174     \retval 1 is slow database
175     \retval 0 is not a slow database
176
177     The Slow database is for testing.. It allows us to simulate
178     a slow server...
179 */
180 static int check_slow(const char *basename, bend_association association)
181 {
182     if (strncmp(basename, "Slow", 4) == 0)
183     {
184 #if HAVE_UNISTD_H
185         int i, w = 3;
186         if (basename[4])
187             sscanf(basename+4, "%d", &w);
188         /* wait up to 3 seconds and check if connection is still alive */
189         for (i = 0; i < w; i++)
190         {
191             if (association && !bend_assoc_is_alive(association))
192             {
193                 yaz_log(YLOG_LOG, "search aborted");
194                 break;
195             }
196             sleep(1);
197         }
198 #endif
199         return 1;
200     }
201     return 0;
202 }
203
204 static int strcmp_prefix(const char *s, const char *p)
205 {
206     size_t l = strlen(p);
207     if (strlen(s) >= l && !memcmp(s, p, l))
208         return 1;
209     return 0;
210 }
211
212 static void init_delay(struct delay *delayp)
213 {
214     delayp->d1 = delayp->d2 = 0.0;
215 }
216
217 static int parse_delay(struct delay *delayp, const char *value)
218 {
219     if (sscanf(value, "%lf:%lf", &delayp->d1, &delayp->d2) == 2)
220         ;
221     else if (sscanf(value, "%lf", &delayp->d1) == 1)
222         delayp->d2 = 0.0;
223     else
224         return -1;
225     return 0;
226 }
227
228 static void ztest_sleep(double d)
229 {
230 #ifdef WIN32
231     Sleep( (DWORD) (d * 1000));
232 #else
233     struct timeval tv;
234     tv.tv_sec = d;
235     tv.tv_usec = (d - (long) d) * 1000000;
236     select(0, 0, 0, 0, &tv);
237 #endif
238 }
239
240 static void do_delay(const struct delay *delayp)
241 {
242     double d = delayp->d1;
243
244     if (d > 0.0)
245     {
246         if (delayp->d2 > d)
247             d += (rand()) * (delayp->d2 - d) / RAND_MAX;
248         ztest_sleep(d);
249     }
250 }
251
252 static void addterms(ODR odr, Z_FacetField *facet_field, const char *facet_name)
253 {
254     int index;
255     int freq = 100;
256     int length = strlen(facet_name) + 10;
257     char *key = odr_malloc(odr, length);
258     key[0] = '\0';
259     for (index = 0; index < facet_field->num_terms; index++)
260     {
261         Z_FacetTerm *facet_term;
262         sprintf(key, "%s%d", facet_name, index);
263         yaz_log(YLOG_DEBUG, "facet add term %s %d %s", facet_name, index, key);
264
265         facet_term = facet_term_create_cstr(odr, key, freq);
266         freq = freq - 10 ;
267         facet_field_term_set(odr, facet_field, facet_term, index);
268     }
269 }
270
271 Z_OtherInformation *build_facet_response(ODR odr, Z_FacetList *facet_list) {
272     int index, new_index = 0;
273     Z_FacetList *new_list = facet_list_create(odr, facet_list->num);
274
275     for (index = 0; index < facet_list->num; index++) {
276         struct yaz_facet_attr attrvalues;
277         yaz_facet_attr_init(&attrvalues);
278         attrvalues.limit = 10;
279         yaz_facet_attr_get_z_attributes(facet_list->elements[index]->attributes,
280                                         &attrvalues);
281         yaz_log(YLOG_LOG, "Attributes: %s limit=%d start=%d sort=%d",
282                 attrvalues.useattr ? attrvalues.useattr : "NONE",
283                 attrvalues.limit,
284                 attrvalues.start,
285                 attrvalues.sortorder);
286         if (attrvalues.errstring)
287             yaz_log(YLOG_LOG, "Error parsing attributes: %s", attrvalues.errstring);
288         if (attrvalues.limit > 0 && attrvalues.useattr) {
289             new_list->elements[new_index] = facet_field_create(odr, facet_list->elements[index]->attributes, attrvalues.limit);
290             addterms(odr, new_list->elements[new_index], attrvalues.useattr);
291             new_index++;
292         }
293         else {
294             yaz_log(YLOG_DEBUG, "Facet: skipping %s due to 0 limit.", attrvalues.useattr);
295         }
296
297     }
298     new_list->num = new_index;
299     if (new_index > 0) {
300         Z_OtherInformation *oi = odr_malloc(odr, sizeof(*oi));
301         Z_OtherInformationUnit *oiu = odr_malloc(odr, sizeof(*oiu));
302         oi->num_elements = 1;
303         oi->list = odr_malloc(odr, oi->num_elements * sizeof(*oi->list));
304         oiu->category = 0;
305         oiu->which = Z_OtherInfo_externallyDefinedInfo;
306         oiu->information.externallyDefinedInfo = odr_malloc(odr, sizeof(*oiu->information.externallyDefinedInfo));
307         oiu->information.externallyDefinedInfo->direct_reference = odr_oiddup(odr, yaz_oid_userinfo_facet_1);
308         oiu->information.externallyDefinedInfo->descriptor = 0;
309         oiu->information.externallyDefinedInfo->indirect_reference = 0;
310         oiu->information.externallyDefinedInfo->which = Z_External_userFacets;
311         oiu->information.externallyDefinedInfo->u.facetList = new_list;
312         oi->list[0] = oiu;
313         return oi;
314     }
315     return 0;
316 }
317
318 static void echo_extra_args(ODR stream,
319                             Z_SRW_extra_arg *extra_args, char **extra_response)
320 {
321     if (extra_args)
322     {
323         Z_SRW_extra_arg *a;
324         WRBUF response_xml = wrbuf_alloc();
325         wrbuf_puts(response_xml, "<extra>");
326         for (a = extra_args; a; a = a->next)
327         {
328             wrbuf_puts(response_xml, "<extra name=\"");
329             wrbuf_xmlputs(response_xml, a->name);
330             wrbuf_puts(response_xml, "\"");
331             if (a->value)
332             {
333                 wrbuf_puts(response_xml, " value=\"");
334                 wrbuf_xmlputs(response_xml, a->value);
335                 wrbuf_puts(response_xml, "\"");
336             }
337             wrbuf_puts(response_xml, "/>");
338         }
339         wrbuf_puts(response_xml, "</extra>");
340         *extra_response = odr_strdup(stream, wrbuf_cstr(response_xml));
341         wrbuf_destroy(response_xml);
342     }
343
344 }
345
346 int ztest_search(void *handle, bend_search_rr *rr)
347 {
348     struct session_handle *sh = (struct session_handle*) handle;
349     struct result_set *new_set;
350     const char *db, *db_sep;
351
352     if (rr->num_bases != 1)
353     {
354         rr->errcode = YAZ_BIB1_COMBI_OF_SPECIFIED_DATABASES_UNSUPP;
355         return 0;
356     }
357
358     db = rr->basenames[0];
359
360     /* Allow Default, db.* and Slow */
361     if (strcmp_prefix(db, "Default"))
362         ;  /* Default is OK in our test */
363     else if (strcmp_prefix(db, "db"))
364         ;  /* db.* is OK in our test */
365     else if (check_slow(rr->basenames[0], rr->association))
366     {
367         rr->estimated_hit_count = 1;
368     }
369     else
370     {
371         rr->errcode = YAZ_BIB1_DATABASE_UNAVAILABLE;
372         rr->errstring = rr->basenames[0];
373         return 0;
374     }
375
376     new_set = get_set(sh, rr->setname);
377     if (new_set)
378     {
379         if (!rr->replace_set)
380         {
381             rr->errcode = YAZ_BIB1_RESULT_SET_EXISTS_AND_REPLACE_INDICATOR_OFF;
382             return 0;
383         }
384         xfree(new_set->db);
385     }
386     else
387     {
388         new_set = xmalloc(sizeof(*new_set));
389         new_set->next = sh->result_sets;
390         sh->result_sets = new_set;
391         new_set->name = xstrdup(rr->setname);
392     }
393     new_set->hits = 0;
394     new_set->db = xstrdup(db);
395     init_delay(&new_set->search_delay);
396     init_delay(&new_set->present_delay);
397     init_delay(&new_set->fetch_delay);
398
399     db_sep = strchr(db, '?');
400     if (db_sep)
401     {
402         char **names;
403         char **values;
404         int no_parms = yaz_uri_to_array(db_sep+1, rr->stream, &names, &values);
405         int i;
406         for (i = 0; i < no_parms; i++)
407         {
408             const char *name = names[i];
409             const char *value = values[i];
410             if (!strcmp(name, "seed"))
411                 srand(atoi(value));
412             else if (!strcmp(name, "search-delay"))
413                 parse_delay(&new_set->search_delay, value);
414             else if (!strcmp(name, "present-delay"))
415                 parse_delay(&new_set->present_delay, value);
416             else if (!strcmp(name, "fetch-delay"))
417                 parse_delay(&new_set->fetch_delay, value);
418             else
419             {
420                 rr->errcode = YAZ_BIB1_SERVICE_UNSUPP_FOR_THIS_DATABASE;
421                 rr->errstring = odr_strdup(rr->stream, name);
422             }
423         }
424     }
425
426     echo_extra_args(rr->stream, rr->extra_args, &rr->extra_response_data);
427     rr->hits = get_hit_count(rr->query);
428
429     if (1)
430     {
431         Z_FacetList *facet_list = yaz_oi_get_facetlist(&rr->search_input);
432         if (facet_list) {
433             yaz_log(YLOG_LOG, "%d Facets in search request.", facet_list->num);
434             rr->search_info = build_facet_response(rr->stream, facet_list);
435         }
436         else
437             yaz_log(YLOG_DEBUG, "No facets parsed search request.");
438
439     }
440     do_delay(&new_set->search_delay);
441     new_set->hits = rr->hits;
442
443     return 0;
444 }
445
446
447 /* this huge function handles extended services */
448 int ztest_esrequest(void *handle, bend_esrequest_rr *rr)
449 {
450     if (rr->esr->packageName)
451         yaz_log(log_level, "packagename: %s", rr->esr->packageName);
452     yaz_log(log_level, "Waitaction: " ODR_INT_PRINTF, *rr->esr->waitAction);
453
454
455     yaz_log(log_level, "function: " ODR_INT_PRINTF, *rr->esr->function);
456
457     if (!rr->esr->taskSpecificParameters)
458     {
459         yaz_log(log_level, "No task specific parameters");
460     }
461     else if (rr->esr->taskSpecificParameters->which == Z_External_itemOrder)
462     {
463         Z_ItemOrder *it = rr->esr->taskSpecificParameters->u.itemOrder;
464         yaz_log(log_level, "Received ItemOrder");
465         if (it->which == Z_IOItemOrder_esRequest)
466         {
467             Z_IORequest *ir = it->u.esRequest;
468             Z_IOOriginPartToKeep *k = ir->toKeep;
469             Z_IOOriginPartNotToKeep *n = ir->notToKeep;
470             const char *xml_in_response = 0;
471
472             if (k && k->contact)
473             {
474                 if (k->contact->name)
475                     yaz_log(log_level, "contact name %s", k->contact->name);
476                 if (k->contact->phone)
477                     yaz_log(log_level, "contact phone %s", k->contact->phone);
478                 if (k->contact->email)
479                     yaz_log(log_level, "contact email %s", k->contact->email);
480             }
481             if (k->addlBilling)
482             {
483                 yaz_log(log_level, "Billing info (not shown)");
484             }
485
486             if (n->resultSetItem)
487             {
488                 yaz_log(log_level, "resultsetItem");
489                 yaz_log(log_level, "setId: %s", n->resultSetItem->resultSetId);
490                 yaz_log(log_level, "item: " ODR_INT_PRINTF, *n->resultSetItem->item);
491             }
492             if (n->itemRequest)
493             {
494                 Z_External *r = (Z_External*) n->itemRequest;
495                 ILL_ItemRequest *item_req = 0;
496                 ILL_APDU *ill_apdu = 0;
497                 if (r->direct_reference)
498                 {
499                     char oid_name_str[OID_STR_MAX];
500                     oid_class oclass;
501                     const char *oid_name =
502                         yaz_oid_to_string_buf(r->direct_reference,
503                                               &oclass, oid_name_str);
504                     if (oid_name)
505                         yaz_log(log_level, "OID %s", oid_name);
506                     if (!oid_oidcmp(r->direct_reference, yaz_oid_recsyn_xml))
507                     {
508                         yaz_log(log_level, "ILL XML request");
509                         if (r->which == Z_External_octet)
510                             yaz_log(log_level, "%.*s",
511                                     r->u.octet_aligned->len,
512                                     r->u.octet_aligned->buf);
513                         xml_in_response = "<dummy>x</dummy>";
514                     }
515                     if (!oid_oidcmp(r->direct_reference,
516                                     yaz_oid_general_isoill_1))
517                     {
518                         yaz_log(log_level, "Decode ItemRequest begin");
519                         if (r->which == ODR_EXTERNAL_single)
520                         {
521                             odr_setbuf(rr->decode,
522                                        (char *) r->u.single_ASN1_type->buf,
523                                        r->u.single_ASN1_type->len, 0);
524
525                             if (!ill_ItemRequest(rr->decode, &item_req, 0, 0))
526                             {
527                                 yaz_log(log_level,
528                                         "Couldn't decode ItemRequest %s near %ld",
529                                         odr_errmsg(odr_geterror(rr->decode)),
530                                         (long) odr_offset(rr->decode));
531                             }
532                             else
533                                 yaz_log(log_level, "Decode ItemRequest OK");
534                             if (rr->print)
535                             {
536                                 ill_ItemRequest(rr->print, &item_req, 0,
537                                                 "ItemRequest");
538                                 odr_reset(rr->print);
539                             }
540                         }
541                         if (!item_req && r->which == ODR_EXTERNAL_single)
542                         {
543                             yaz_log(log_level, "Decode ILL APDU begin");
544                             odr_setbuf(rr->decode,
545                                        (char*) r->u.single_ASN1_type->buf,
546                                        r->u.single_ASN1_type->len, 0);
547
548                             if (!ill_APDU(rr->decode, &ill_apdu, 0, 0))
549                             {
550                                 yaz_log(log_level,
551                                         "Couldn't decode ILL APDU %s near %ld",
552                                         odr_errmsg(odr_geterror(rr->decode)),
553                                         (long) odr_offset(rr->decode));
554                                 yaz_log(log_level, "PDU dump:");
555                                 odr_dumpBER(yaz_log_file(),
556                                             (char *) r->u.single_ASN1_type->buf,
557                                             r->u.single_ASN1_type->len);
558                             }
559                             else
560                                 yaz_log(log_level, "Decode ILL APDU OK");
561                             if (rr->print)
562                             {
563                                 ill_APDU(rr->print, &ill_apdu, 0,
564                                          "ILL APDU");
565                                 odr_reset(rr->print);
566                             }
567                         }
568                     }
569                 }
570                 if (item_req)
571                 {
572                     yaz_log(log_level, "ILL protocol version = "
573                             ODR_INT_PRINTF,
574                             *item_req->protocol_version_num);
575                 }
576             }
577             if (k)
578             {
579
580                 Z_External *ext = (Z_External *)
581                     odr_malloc(rr->stream, sizeof(*ext));
582                 Z_IUOriginPartToKeep *keep = (Z_IUOriginPartToKeep *)
583                     odr_malloc(rr->stream, sizeof(*keep));
584                 Z_IOTargetPart *targetPart = (Z_IOTargetPart *)
585                     odr_malloc(rr->stream, sizeof(*targetPart));
586
587                 rr->taskPackage = (Z_TaskPackage *)
588                     odr_malloc(rr->stream, sizeof(*rr->taskPackage));
589                 rr->taskPackage->packageType =
590                     odr_oiddup(rr->stream, rr->esr->packageType);
591                 rr->taskPackage->packageName = 0;
592                 rr->taskPackage->userId = 0;
593                 rr->taskPackage->retentionTime = 0;
594                 rr->taskPackage->permissions = 0;
595                 rr->taskPackage->description = 0;
596                 rr->taskPackage->targetReference =
597                     odr_create_Odr_oct(rr->stream, "911", 3);
598                 rr->taskPackage->creationDateTime = 0;
599                 rr->taskPackage->taskStatus = odr_intdup(rr->stream, 0);
600                 rr->taskPackage->packageDiagnostics = 0;
601                 rr->taskPackage->taskSpecificParameters = ext;
602
603                 ext->direct_reference =
604                     odr_oiddup(rr->stream, rr->esr->packageType);
605                 ext->indirect_reference = 0;
606                 ext->descriptor = 0;
607                 ext->which = Z_External_itemOrder;
608                 ext->u.itemOrder = (Z_ItemOrder *)
609                     odr_malloc(rr->stream, sizeof(*ext->u.update));
610                 ext->u.itemOrder->which = Z_IOItemOrder_taskPackage;
611                 ext->u.itemOrder->u.taskPackage =  (Z_IOTaskPackage *)
612                     odr_malloc(rr->stream, sizeof(Z_IOTaskPackage));
613                 ext->u.itemOrder->u.taskPackage->originPart = k;
614                 ext->u.itemOrder->u.taskPackage->targetPart = targetPart;
615
616                 if (xml_in_response)
617                     targetPart->itemRequest =
618                         z_ext_record_xml(rr->stream, xml_in_response,
619                                          strlen(xml_in_response));
620                 else
621                     targetPart->itemRequest = 0;
622
623                 targetPart->statusOrErrorReport = 0;
624                 targetPart->auxiliaryStatus = 0;
625             }
626         }
627     }
628     else if (rr->esr->taskSpecificParameters->which == Z_External_update)
629     {
630         Z_IUUpdate *up = rr->esr->taskSpecificParameters->u.update;
631         yaz_log(log_level, "Received DB Update");
632         if (up->which == Z_IUUpdate_esRequest)
633         {
634             Z_IUUpdateEsRequest *esRequest = up->u.esRequest;
635             Z_IUOriginPartToKeep *toKeep = esRequest->toKeep;
636             Z_IUSuppliedRecords *notToKeep = esRequest->notToKeep;
637
638             yaz_log(log_level, "action");
639             if (toKeep->action)
640             {
641                 switch (*toKeep->action)
642                 {
643                 case Z_IUOriginPartToKeep_recordInsert:
644                     yaz_log(log_level, " recordInsert");
645                     break;
646                 case Z_IUOriginPartToKeep_recordReplace:
647                     yaz_log(log_level, " recordReplace");
648                     break;
649                 case Z_IUOriginPartToKeep_recordDelete:
650                     yaz_log(log_level, " recordDelete");
651                     break;
652                 case Z_IUOriginPartToKeep_elementUpdate:
653                     yaz_log(log_level, " elementUpdate");
654                     break;
655                 case Z_IUOriginPartToKeep_specialUpdate:
656                     yaz_log(log_level, " specialUpdate");
657                     break;
658                 default:
659                     yaz_log(log_level, " unknown (" ODR_INT_PRINTF ")",
660                             *toKeep->action);
661                 }
662             }
663             if (toKeep->databaseName)
664             {
665                 yaz_log(log_level, "database: %s", toKeep->databaseName);
666                 if (!strcmp(toKeep->databaseName, "fault"))
667                 {
668                     rr->errcode = YAZ_BIB1_DATABASE_UNAVAILABLE;
669                     rr->errstring = toKeep->databaseName;
670                 }
671                 if (!strcmp(toKeep->databaseName, "accept"))
672                     rr->errcode = -1;
673             }
674             if (toKeep)
675             {
676                 Z_External *ext = (Z_External *)
677                     odr_malloc(rr->stream, sizeof(*ext));
678                 Z_IUOriginPartToKeep *keep = (Z_IUOriginPartToKeep *)
679                     odr_malloc(rr->stream, sizeof(*keep));
680                 Z_IUTargetPart *targetPart = (Z_IUTargetPart *)
681                     odr_malloc(rr->stream, sizeof(*targetPart));
682
683                 rr->taskPackage = (Z_TaskPackage *)
684                     odr_malloc(rr->stream, sizeof(*rr->taskPackage));
685                 rr->taskPackage->packageType =
686                     odr_oiddup(rr->stream, rr->esr->packageType);
687                 rr->taskPackage->packageName = 0;
688                 rr->taskPackage->userId = 0;
689                 rr->taskPackage->retentionTime = 0;
690                 rr->taskPackage->permissions = 0;
691                 rr->taskPackage->description = 0;
692                 rr->taskPackage->targetReference =
693                     odr_create_Odr_oct(rr->stream, "123", 3);
694                 rr->taskPackage->creationDateTime = 0;
695                 rr->taskPackage->taskStatus = odr_intdup(rr->stream, 0);
696                 rr->taskPackage->packageDiagnostics = 0;
697                 rr->taskPackage->taskSpecificParameters = ext;
698
699                 ext->direct_reference =
700                     odr_oiddup(rr->stream, rr->esr->packageType);
701                 ext->indirect_reference = 0;
702                 ext->descriptor = 0;
703                 ext->which = Z_External_update;
704                 ext->u.update = (Z_IUUpdate *)
705                     odr_malloc(rr->stream, sizeof(*ext->u.update));
706                 ext->u.update->which = Z_IUUpdate_taskPackage;
707                 ext->u.update->u.taskPackage =  (Z_IUUpdateTaskPackage *)
708                     odr_malloc(rr->stream, sizeof(Z_IUUpdateTaskPackage));
709                 ext->u.update->u.taskPackage->originPart = keep;
710                 ext->u.update->u.taskPackage->targetPart = targetPart;
711
712                 keep->action = odr_intdup(rr->stream, *toKeep->action);
713                 keep->databaseName =
714                     odr_strdup(rr->stream, toKeep->databaseName);
715                 keep->schema = 0;
716                 keep->elementSetName = 0;
717                 keep->actionQualifier = 0;
718
719                 targetPart->updateStatus = odr_intdup(rr->stream, 1);
720                 targetPart->num_globalDiagnostics = 0;
721                 targetPart->globalDiagnostics = (Z_DiagRec **) odr_nullval();
722                 targetPart->num_taskPackageRecords = 1;
723                 targetPart->taskPackageRecords =
724                     (Z_IUTaskPackageRecordStructure **)
725                     odr_malloc(rr->stream,
726                                sizeof(Z_IUTaskPackageRecordStructure *));
727                 targetPart->taskPackageRecords[0] =
728                     (Z_IUTaskPackageRecordStructure *)
729                     odr_malloc(rr->stream,
730                                sizeof(Z_IUTaskPackageRecordStructure));
731
732                 targetPart->taskPackageRecords[0]->which =
733                     Z_IUTaskPackageRecordStructure_record;
734                 targetPart->taskPackageRecords[0]->u.record =
735                     z_ext_record_sutrs(rr->stream, "test", 4);
736                 targetPart->taskPackageRecords[0]->correlationInfo = 0;
737                 targetPart->taskPackageRecords[0]->recordStatus =
738                     odr_intdup(rr->stream,
739                                Z_IUTaskPackageRecordStructure_success);
740                 targetPart->taskPackageRecords[0]->num_supplementalDiagnostics
741                     = 0;
742
743                 targetPart->taskPackageRecords[0]->supplementalDiagnostics = 0;
744             }
745             if (notToKeep)
746             {
747                 int i;
748                 for (i = 0; i < notToKeep->num; i++)
749                 {
750                     Z_External *rec = notToKeep->elements[i]->record;
751
752                     if (rec->direct_reference)
753                     {
754                         char oid_name_str[OID_STR_MAX];
755                         const char *oid_name
756                             = oid_name = yaz_oid_to_string_buf(
757                                 rec->direct_reference, 0,
758                                 oid_name_str);
759                         if (oid_name)
760                             yaz_log(log_level, "record %d type %s", i,
761                                     oid_name);
762                     }
763                     switch (rec->which)
764                     {
765                     case Z_External_sutrs:
766                         if (rec->u.octet_aligned->len > 170)
767                             yaz_log(log_level, "%d bytes:\n%.168s ...",
768                                     rec->u.sutrs->len,
769                                     rec->u.sutrs->buf);
770                         else
771                             yaz_log(log_level, "%d bytes:\n%s",
772                                     rec->u.sutrs->len,
773                                     rec->u.sutrs->buf);
774                         break;
775                     case Z_External_octet        :
776                         if (rec->u.octet_aligned->len > 170)
777                             yaz_log(log_level, "%d bytes:\n%.168s ...",
778                                     rec->u.octet_aligned->len,
779                                     rec->u.octet_aligned->buf);
780                         else
781                             yaz_log(log_level, "%d bytes\n%s",
782                                     rec->u.octet_aligned->len,
783                                     rec->u.octet_aligned->buf);
784                     }
785                 }
786             }
787         }
788     }
789     return 0;
790 }
791
792 /* result set delete */
793 int ztest_delete(void *handle, bend_delete_rr *rr)
794 {
795     if (rr->num_setnames == 1 && !strcmp(rr->setnames[0], "1"))
796         rr->delete_status = Z_DeleteStatus_success;
797     else
798         rr->delete_status = Z_DeleteStatus_resultSetDidNotExist;
799     return 0;
800 }
801
802 /* Our sort handler really doesn't sort... */
803 int ztest_sort(void *handle, bend_sort_rr *rr)
804 {
805     rr->errcode = 0;
806     rr->sort_status = Z_SortResponse_success;
807     return 0;
808 }
809
810
811 /* present request handler */
812 int ztest_present(void *handle, bend_present_rr *rr)
813 {
814     struct session_handle *sh = (struct session_handle*) handle;
815     struct result_set *set = get_set(sh, rr->setname);
816
817     if (!set)
818     {
819         rr->errcode = YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
820         rr->errstring = odr_strdup(rr->stream, rr->setname);
821         return 0;
822     }
823     do_delay(&set->present_delay);
824     return 0;
825 }
826
827 /* retrieval of a single record (present, and piggy back search) */
828 int ztest_fetch(void *handle, bend_fetch_rr *r)
829 {
830     struct session_handle *sh = (struct session_handle*) handle;
831     char *cp;
832     const Odr_oid *oid = r->request_format;
833     struct result_set *set = get_set(sh, r->setname);
834     const char *esn = yaz_get_esn(r->comp);
835
836     if (!set)
837     {
838         r->errcode = YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
839         r->errstring = odr_strdup(r->stream, r->setname);
840         return 0;
841     }
842     do_delay(&set->fetch_delay);
843     r->last_in_set = 0;
844     r->basename = set->db;
845     r->output_format = r->request_format;
846
847     if (r->number < 1 || r->number > set->hits)
848     {
849         r->errcode = YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE;
850         return 0;
851     }
852     if (!oid || yaz_oid_is_iso2709(oid))
853     {
854         cp = dummy_marc_record(r->number, r->stream);
855         if (!cp)
856         {
857             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
858             r->surrogate_flag = 1;
859             return 0;
860         }
861         else
862         {
863             r->len = strlen(cp);
864             r->record = cp;
865             r->output_format = odr_oiddup(r->stream, yaz_oid_recsyn_usmarc);
866         }
867     }
868     else if (!oid_oidcmp(oid, yaz_oid_recsyn_opac))
869     {
870         cp = dummy_marc_record(r->number, r->stream);
871         if (!cp)
872         {
873             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
874             r->surrogate_flag = 1;
875             return 0;
876         }
877         r->record = (char *) dummy_opac(r->number, r->stream, cp);
878         r->len = -1;
879     }
880     else if (!oid_oidcmp(oid, yaz_oid_recsyn_sutrs))
881     {
882         /* this section returns a small record */
883         char buf[100];
884
885         sprintf(buf, "This is dummy SUTRS record number %d\n", r->number);
886
887         r->len = strlen(buf);
888         r->record = (char *) odr_malloc(r->stream, r->len+1);
889         strcpy(r->record, buf);
890     }
891     else if (!oid_oidcmp(oid, yaz_oid_recsyn_grs_1))
892     {
893         r->len = -1;
894         r->record = (char*) dummy_grs_record(r->number, r->stream);
895         if (!r->record)
896         {
897             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
898             r->surrogate_flag = 1;
899             return 0;
900         }
901     }
902     else if (!oid_oidcmp(oid, yaz_oid_recsyn_postscript))
903     {
904         char fname[20];
905         FILE *f;
906         long size;
907
908         sprintf(fname, "part.%d.ps", r->number);
909         f = fopen(fname, "rb");
910         if (!f)
911         {
912             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
913             r->surrogate_flag = 1;
914             return 0;
915         }
916         fseek(f, 0L, SEEK_END);
917         size = ftell(f);
918         if (size <= 0 || size >= 5000000)
919         {
920             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
921             r->surrogate_flag = 1;
922         }
923         fseek(f, 0L, SEEK_SET);
924         r->record = (char*) odr_malloc(r->stream, size);
925         r->len = size;
926         if (fread(r->record, size, 1, f) != 1)
927         {
928             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
929             r->surrogate_flag = 1;
930         }
931         fclose(f);
932     }
933     else if (!oid_oidcmp(oid, yaz_oid_recsyn_xml))
934     {
935         if ((cp = dummy_xml_record(r->number, r->stream, esn)))
936         {
937             r->len = strlen(cp);
938             r->record = cp;
939             r->schema = "info:srw/schema/1/marcxml-1.1";
940         }
941         else
942         {
943             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
944             r->surrogate_flag = 1;
945             return 0;
946         }
947     }
948     else if (!oid_oidcmp(oid, yaz_oid_recsyn_json))
949     {
950         if ((cp = dummy_json_record(r->number, r->stream, esn)))
951         {
952             r->len = strlen(cp);
953             r->record = cp;
954             r->schema = "info:srw/schema/1/marcxml-1.1";
955         }
956         else
957         {
958             r->errcode = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
959             r->surrogate_flag = 1;
960             return 0;
961         }
962     }
963     else
964     {
965         char buf[OID_STR_MAX];
966         r->errcode = YAZ_BIB1_RECORD_SYNTAX_UNSUPP;
967         r->errstring = odr_strdup(r->stream, oid_oid_to_dotstring(oid, buf));
968         return 0;
969     }
970     r->errcode = 0;
971     return 0;
972 }
973
974 /*
975  * silly dummy-scan what reads words from a file.
976  */
977 int ztest_scan(void *handle, bend_scan_rr *q)
978 {
979     static FILE *f = 0;
980     static struct scan_entry list[200];
981     static char entries[200][80];
982     int hits[200];
983     char term[80], *p;
984     int i, pos;
985     int term_position_req = q->term_position;
986     int num_entries_req = q->num_entries;
987
988     /* Throw Database unavailable if other than Default or Slow */
989     if (!yaz_matchstr(q->basenames[0], "Default"))
990         ;  /* Default is OK in our test */
991     else if (check_slow(q->basenames[0], 0 /* no assoc for scan */))
992         ;
993     else
994     {
995         q->errcode = YAZ_BIB1_DATABASE_UNAVAILABLE;
996         q->errstring = q->basenames[0];
997         return 0;
998     }
999
1000     q->errcode = 0;
1001     q->errstring = 0;
1002     q->entries = list;
1003     q->status = BEND_SCAN_SUCCESS;
1004     if (!f && !(f = fopen("dummy-words", "r")))
1005     {
1006         perror("dummy-words");
1007         exit(1);
1008     }
1009     if (q->num_entries > 200)
1010     {
1011         q->errcode = YAZ_BIB1_RESOURCES_EXHAUSTED_NO_RESULTS_AVAILABLE;
1012         return 0;
1013     }
1014     if (q->term)
1015     {
1016         int len;
1017         if (q->term->term->which != Z_Term_general)
1018         {
1019             q->errcode = YAZ_BIB1_TERM_TYPE_UNSUPP;
1020             return 0;
1021         }
1022         if (*q->step_size != 0)
1023         {
1024             q->errcode = YAZ_BIB1_ONLY_ZERO_STEP_SIZE_SUPPORTED_FOR_SCAN;
1025             return 0;
1026         }
1027         len = q->term->term->u.general->len;
1028         if (len >= (int ) sizeof(term))
1029             len = sizeof(term)-1;
1030         memcpy(term, q->term->term->u.general->buf, len);
1031         term[len] = '\0';
1032     }
1033     else if (q->scanClause)
1034     {
1035         strncpy(term, q->scanClause, sizeof(term)-1);
1036         term[sizeof(term)-1] = '\0';
1037     }
1038     else
1039         strcpy(term, "0");
1040
1041     for (p = term; *p; p++)
1042         if (yaz_islower(*p))
1043             *p = yaz_toupper(*p);
1044
1045     fseek(f, 0, SEEK_SET);
1046     q->num_entries = 0;
1047
1048     for (i = 0, pos = 0; fscanf(f, " %79[^:]:%d", entries[pos], &hits[pos]) == 2;
1049          i++, pos < 199 ? pos++ : (pos = 0))
1050     {
1051         if (!q->num_entries && strcmp(entries[pos], term) >= 0) /* s-point fnd */
1052         {
1053             if ((q->term_position = term_position_req) > i + 1)
1054             {
1055                 q->term_position = i + 1;
1056                 q->status = BEND_SCAN_PARTIAL;
1057             }
1058             for (; q->num_entries < q->term_position; q->num_entries++)
1059             {
1060                 int po;
1061
1062                 po = pos - q->term_position + q->num_entries+1; /* find pos */
1063                 if (po < 0)
1064                     po += 200;
1065
1066                 if (!strcmp(term, "SD") && q->num_entries == 2)
1067                 {
1068                     list[q->num_entries].term = entries[pos];
1069                     list[q->num_entries].occurrences = -1;
1070                     list[q->num_entries].errcode =
1071                         YAZ_BIB1_SCAN_UNSUPP_VALUE_OF_POSITION_IN_RESPONSE;
1072                     list[q->num_entries].errstring = "SD for Scan Term";
1073                 }
1074                 else
1075                 {
1076                     list[q->num_entries].term = entries[po];
1077                     list[q->num_entries].occurrences = hits[po];
1078                 }
1079             }
1080         }
1081         else if (q->num_entries)
1082         {
1083             list[q->num_entries].term = entries[pos];
1084             list[q->num_entries].occurrences = hits[pos];
1085             q->num_entries++;
1086         }
1087         if (q->num_entries >= num_entries_req)
1088             break;
1089     }
1090     echo_extra_args(q->stream, q->extra_args, &q->extra_response_data);
1091     if (feof(f))
1092         q->status = BEND_SCAN_PARTIAL;
1093     return 0;
1094 }
1095
1096 int ztest_explain(void *handle, bend_explain_rr *rr)
1097 {
1098     if (rr->database && !strcmp(rr->database, "Default"))
1099     {
1100         rr->explain_buf = "<explain>\n"
1101             "\t<serverInfo>\n"
1102             "\t\t<host>localhost</host>\n"
1103             "\t\t<port>210</port>\n"
1104             "\t</serverInfo>\n"
1105             "</explain>\n";
1106     }
1107     return 0;
1108 }
1109
1110 int ztest_update(void *handle, bend_update_rr *rr)
1111 {
1112     rr->operation_status = "success";
1113     return 0;
1114 }
1115
1116 bend_initresult *bend_init(bend_initrequest *q)
1117 {
1118     bend_initresult *r = (bend_initresult *)
1119         odr_malloc(q->stream, sizeof(*r));
1120     struct session_handle *sh = xmalloc(sizeof(*sh));
1121
1122     sh->result_sets = 0;
1123
1124     if (!log_level_set)
1125     {
1126         log_level=yaz_log_module_level("ztest");
1127         log_level_set=1;
1128     }
1129
1130     r->errcode = 0;
1131     r->errstring = 0;
1132     r->handle = sh;                         /* tell GFS about our handle */
1133     q->bend_sort = ztest_sort;              /* register sort handler */
1134     q->bend_search = ztest_search;          /* register search handler */
1135     q->bend_present = ztest_present;        /* register present handle */
1136     q->bend_esrequest = ztest_esrequest;
1137     q->bend_delete = ztest_delete;
1138     q->bend_fetch = ztest_fetch;
1139     q->bend_scan = ztest_scan;
1140 #if 0
1141     q->bend_explain = ztest_explain;
1142 #endif
1143     q->bend_srw_scan = ztest_scan;
1144     q->bend_srw_update = ztest_update;
1145
1146     q->query_charset = "ISO-8859-1";
1147     q->records_in_same_charset = 0;
1148
1149     return r;
1150 }
1151
1152 void bend_close(void *handle)
1153 {
1154     struct session_handle *sh = (struct session_handle*) handle;
1155     remove_sets(sh);
1156     xfree(sh);              /* release our session */
1157     return;
1158 }
1159
1160 int main(int argc, char **argv)
1161 {
1162     yaz_enable_panic_backtrace(argv[0]);
1163
1164     return statserv_main(argc, argv, bend_init, bend_close);
1165 }
1166 /*
1167  * Local variables:
1168  * c-basic-offset: 4
1169  * c-file-style: "Stroustrup"
1170  * indent-tabs-mode: nil
1171  * End:
1172  * vim: shiftwidth=4 tabstop=8 expandtab
1173  */
1174