Client uses prefix query notation.
[yaz-moved-to-github.git] / client / client.c
1 /*
2  * Copyright (c) 1995, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: client.c,v $
7  * Revision 1.4  1995-05-22 15:30:13  adam
8  * Client uses prefix query notation.
9  *
10  * Revision 1.3  1995/05/22  15:06:53  quinn
11  * *** empty log message ***
12  *
13  * Revision 1.2  1995/05/22  14:56:40  quinn
14  * *** empty log message ***
15  *
16  * Revision 1.1  1995/05/22  11:30:31  quinn
17  * Added prettier client.
18  *
19  *
20  */
21
22 /*
23  * This is the obligatory little toy client, whose primary purpose is
24  * to illustrate the use of the YAZ service-level API.
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/time.h>
30 #include <assert.h>
31
32 #include <comstack.h>
33 #include <tcpip.h>
34 #ifdef USE_XTIMOSI
35 #include <xmosi.h>
36 #endif
37
38 #include <proto.h>
39 #include <oid.h>
40
41 #include <marcdisp.h>
42
43 #ifdef RPN_QUERY
44 #ifdef PREFIX_QUERY
45 #include <pquery.h>
46 #else
47 #include <yaz-ccl.h>
48 #endif
49 #endif
50
51 #include "../version.h"
52
53 #define C_PROMPT "Z> "
54
55 static ODR out, in, print;              /* encoding and decoding streams */
56 static COMSTACK conn = 0;               /* our z-association */
57 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
58 static char database[512] = "Default";  /* Database name */
59 static int setnumber = 0;               /* current result set number */
60 static int smallSetUpperBound = 0;
61 static int largeSetLowerBound = 1;
62 static int mediumSetPresentNumber = 0;
63 static int setno = 1;                   /* current set offset */
64 static int protocol = PROTO_Z3950;      /* current app protocol */
65 #ifdef RPN_QUERY
66 #ifndef PREFIX_QUERY
67 static CCL_bibset bibset;               /* CCL bibset handle */
68 #endif
69 #endif
70
71 static void send_apdu(Z_APDU *a)
72 {
73     char *buf;
74     int len;
75
76     if (!z_APDU(out, &a, 0))
77     {
78         odr_perror(out, "Encoding APDU");
79         exit(1);
80     }
81     buf = odr_getbuf(out, &len, 0);
82     odr_reset(out); /* release the APDU */
83     if (cs_put(conn, buf, len) < 0)
84     {
85         fprintf(stderr, "cs_put: %s", cs_errlist[cs_errno(conn)]);
86         exit(1);
87     }
88 }
89
90 /* INIT SERVICE ------------------------------- */
91
92 static void send_initRequest()
93 {
94     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
95     Z_InitRequest *req = apdu->u.initRequest;
96
97     ODR_MASK_SET(req->options, Z_Options_search);
98     ODR_MASK_SET(req->options, Z_Options_present);
99
100     ODR_MASK_SET(req->protocolVersion, 0);
101     ODR_MASK_SET(req->protocolVersion, 1);
102     ODR_MASK_SET(req->protocolVersion, 2);
103
104     req->idAuthentication = auth;
105
106     send_apdu(apdu);
107     printf("Sent initrequest.\n");
108 }
109
110 static int process_initResponse(Z_InitResponse *res)
111 {
112     if (!*res->result)
113         printf("Connection rejected by target.\n");
114     else
115         printf("Connection accepted by target.\n");
116     if (res->implementationId)
117         printf("ID     : %s\n", res->implementationId);
118     if (res->implementationName)
119         printf("Name   : %s\n", res->implementationName);
120     if (res->implementationVersion)
121         printf("Version: %s\n", res->implementationVersion);
122     if (res->userInformationField)
123     {
124         printf("UserInformationfield:\n");
125         if (!odr_external(print, (Odr_external**)&res-> userInformationField,
126             0))
127         {
128             odr_perror(print, "Printing userinfo\n");
129             odr_reset(print);
130         }
131         if (res->userInformationField->which == ODR_EXTERNAL_octet)
132         {
133             printf("Guessing visiblestring:\n");
134             printf("'%s'\n", res->userInformationField->u. octet_aligned->buf);
135         }
136     }
137     return 0;
138 }
139
140 int cmd_open(char *arg)
141 {
142     void *add;
143     char type[100], addr[100];
144     CS_TYPE t;
145
146     if (conn)
147     {
148         printf("Already connected.\n");
149         return 0;
150     }
151     if (!*arg || sscanf(arg, "%[^:]:%s", type, addr) < 2)
152     {
153         fprintf(stderr, "Usage: open (osi|tcp) ':' [tsel '/']host[':'port]\n");
154         return 0;
155     }
156 #ifdef USE_XTIMOSI
157     if (!strcmp(type, "osi"))
158     {
159         if (!(add = mosi_strtoaddr(addr)))
160         {
161             perror(arg);
162             return 0;
163         }
164         t = mosi_type;
165         protocol = PROTO_SR;
166     }
167     else
168 #endif
169     if (!strcmp(type, "tcp"))
170     {
171         if (!(add = tcpip_strtoaddr(addr)))
172         {
173             perror(arg);
174             return 0;
175         }
176         t = tcpip_type;
177         protocol = PROTO_Z3950;
178     }
179     else
180     {
181         fprintf(stderr, "Bad type: %s\n", type);
182         return 0;
183     }
184     if (!(conn = cs_create(t, 1, CS_Z3950)))
185     {
186         perror("cs_create");
187         return 0;
188     }
189     printf("Connecting...");
190     fflush(stdout);
191     if (cs_connect(conn, add) < 0)
192     {
193         perror("connect");
194         cs_close(conn);
195         conn = 0;
196         return 0;
197     }
198     printf("Ok!\n");
199     send_initRequest();
200     return 2;
201 }
202
203 int cmd_authentication(char *arg)
204 {
205     static Z_IdAuthentication au;
206     static char open[256];
207
208     if (!*arg)
209     {
210         printf("Auth field set to null\n");
211         auth = 0;
212         return 1;
213     }
214     auth = &au;
215     au.which = Z_IdAuthentication_open;
216     au.u.open = open;
217     strcpy(open, arg);
218     return 1;
219 }
220
221 /* SEARCH SERVICE ------------------------------ */
222
223 void display_record(Z_DatabaseRecord *p)
224 {
225     Odr_external *r = (Odr_external*) p;
226
227     if (r->direct_reference)
228     {
229         oident *ent = oid_getentbyoid(r->direct_reference);
230
231         printf("Record type: ");
232         if (ent)
233             printf("%s\n", ent->desc);
234         else if (!odr_oid(print, &r->direct_reference, 0))
235         {
236             odr_perror(print, "print oid");
237             odr_reset(print);
238         }
239     }
240 #if 1
241     if (r->which == ODR_EXTERNAL_octet && p->u.octet_aligned->len)
242     {
243 #if 1
244         marc_display ((char*)p->u.octet_aligned->buf, stdout);
245 #else
246         FILE *ofi = fopen("dump", "a");
247         assert(ofi);
248         fwrite(p->u.octet_aligned->buf, 1, p->u.octet_aligned->len, ofi);
249         fclose(ofi);
250         printf("dumped record\n");
251 #endif
252     }
253     else
254     {
255         printf("Unknown record representation.\n");
256         if (!odr_external(print, &r, 0))
257         {
258             odr_perror(print, "Printing external");
259             odr_reset(print);
260         }
261     }
262 #endif
263 }
264
265 static void display_diagrec(Z_DiagRec *p)
266 {
267     oident *ent;
268
269     printf("Diagnostic message from database.\n");
270     if (!(ent = oid_getentbyoid(p->diagnosticSetId)) ||
271         ent->class != CLASS_DIAGSET || ent->value != VAL_BIB1)
272         printf("Missing or unknown diagset\n");
273     printf("Error condition: %d", *p->condition);
274     printf(" -- %s\n", p->addinfo ? p->addinfo : "");
275 }
276
277 static void display_nameplusrecord(Z_NamePlusRecord *p)
278 {
279     if (p->databaseName)
280         printf("[%s]", p->databaseName);
281     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
282         display_diagrec(p->u.surrogateDiagnostic);
283     else
284         display_record(p->u.databaseRecord);
285 }
286
287 static void display_records(Z_Records *p)
288 {
289     int i;
290
291     if (p->which == Z_Records_NSD)
292         display_diagrec(p->u.nonSurrogateDiagnostic);
293     else
294     {
295         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
296         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
297             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
298     }
299 }
300
301 static int send_searchRequest(char *arg)
302 {
303     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
304     Z_SearchRequest *req = apdu->u.searchRequest;
305     char *databaseNames = database;
306     Z_Query query;
307 #ifdef RPN_QUERY
308 #ifndef PREFIX_QUERY
309     struct ccl_rpn_node *rpn;
310     int error, pos;
311 #endif
312 #endif
313     char setstring[100];
314 #ifdef RPN_QUERY
315     Z_RPNQuery *RPNquery;
316     oident bib1;
317 #else
318     Odr_oct ccl_query;
319 #endif
320
321 #ifdef RPN_QUERY
322 #ifndef PREFIX_QUERY
323     rpn = ccl_find_str(bibset, arg, &error, &pos);
324     if (error)
325     {
326         printf("CCL ERROR: %s\n", ccl_err_msg(error));
327         return 0;
328     }
329 #endif
330 #endif
331
332     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
333     {
334         static unsigned char big[2100];
335         static Odr_oct bigo;
336
337         /* send a very big referenceid to test transport stack etc. */
338         memset(big, 'A', 2100);
339         bigo.len = bigo.size = 2100;
340         bigo.buf = big;
341         req->referenceId = &bigo;
342     }
343
344     if (setnumber >= 0)
345     {
346         sprintf(setstring, "%d", ++setnumber);
347         req->resultSetName = setstring;
348     }
349     *req->smallSetUpperBound = smallSetUpperBound;
350     *req->largeSetLowerBound = largeSetLowerBound;
351     *req->mediumSetPresentNumber = mediumSetPresentNumber;
352     req->num_databaseNames = 1;
353     req->databaseNames = &databaseNames;
354
355     req->query = &query;
356
357 #ifdef RPN_QUERY
358     query.which = Z_Query_type_1;
359
360 #ifndef PREFIX_QUERY
361     assert((RPNquery = ccl_rpn_query(rpn)));
362 #else
363     RPNquery = p_query_rpn (out, arg);
364     if (!RPNquery)
365     {
366         printf("Prefix query error\n");
367         return 0;
368     }
369 #endif
370     bib1.proto = protocol;
371     bib1.class = CLASS_ATTSET;
372     bib1.value = VAL_BIB1;
373     RPNquery->attributeSetId = oid_getoidbyent(&bib1);
374     query.u.type_1 = RPNquery;
375 #else
376     query.which = Z_Query_type_2;
377     query.u.type_2 = &ccl_query;
378     ccl_query.buf = (unsigned char*) arg;
379     ccl_query.len = strlen(arg);
380 #endif
381
382     send_apdu(apdu);
383     setno = 1;
384     printf("Sent searchRequest.\n");
385     return 2;
386 }
387
388 static int process_searchResponse(Z_SearchResponse *res)
389 {
390     if (res->searchStatus)
391         printf("Search was a success.\n");
392     else
393         printf("Search was a bloomin' failure.\n");
394     printf("Number of hits: %d, setno %d\n",
395         *res->resultCount, setnumber);
396     printf("records returned: %d\n",
397         *res->numberOfRecordsReturned);
398     setno += *res->numberOfRecordsReturned;
399     if (res->records)
400         display_records(res->records);
401     return 0;
402 }
403
404 static int cmd_find(char *arg)
405 {
406     if (!*arg)
407     {
408         printf("Find what?\n");
409         return 0;
410     }
411     if (!conn)
412     {
413         printf("Not connected yet\n");
414         return 0;
415     }
416     if (!send_searchRequest(arg))
417         return 0;
418     return 2;
419 }
420
421 static int cmd_ssub(char *arg)
422 {
423     if (!(smallSetUpperBound = atoi(arg)))
424         return 0;
425     return 1;
426 }
427
428 static int cmd_lslb(char *arg)
429 {
430     if (!(largeSetLowerBound = atoi(arg)))
431         return 0;
432     return 1;
433 }
434
435 static int cmd_mspn(char *arg)
436 {
437     if (!(mediumSetPresentNumber = atoi(arg)))
438         return 0;
439     return 1;
440 }
441
442 static int cmd_status(char *arg)
443 {
444     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
445     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
446     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
447     return 1;
448 }
449
450 static int cmd_base(char *arg)
451 {
452     if (!*arg)
453     {
454         printf("Usage: base <database>\n");
455         return 0;
456     }
457     strcpy(database, arg);
458     return 1;
459 }
460
461 static int cmd_setnames(char *arg)
462 {
463     if (setnumber < 0)
464     {
465         printf("Set numbering enabled.\n");
466         setnumber = 0;
467     }
468     else
469     {
470         printf("Set numbering disabled.\n");
471         setnumber = -1;
472     }
473     return 1;
474 }
475
476 /* PRESENT SERVICE ----------------------------- */
477
478 static int send_presentRequest(char *arg)
479 {
480     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
481     Z_PresentRequest *req = apdu->u.presentRequest;
482     int nos = 1;
483     char *p;
484     char setstring[100];
485
486     if ((p = strchr(arg, '+')))
487     {
488         nos = atoi(p + 1);
489         *p = 0;
490     }
491     if (*arg)
492         setno = atoi(arg);
493
494     if (setnumber >= 0)
495     {
496         sprintf(setstring, "%d", setnumber);
497         req->resultSetId = setstring;
498     }
499     req->resultSetStartPoint = &setno;
500     req->numberOfRecordsRequested = &nos;
501     send_apdu(apdu);
502     printf("Sent presentRequest (%d+%d).\n", setno, nos);
503     return 2;
504 }
505
506 static int cmd_show(char *arg)
507 {
508     if (!send_presentRequest(arg))
509         return 0;
510     return 2;
511 }
512
513 int cmd_quit(char *arg)
514 {
515     printf("See you later, alligator.\n");
516     exit(0);
517 }
518
519 static void initialize(void)
520 {
521     FILE *inf;
522
523     if (!(out = odr_createmem(ODR_ENCODE)) ||
524         !(in = odr_createmem(ODR_DECODE)) ||
525         !(print = odr_createmem(ODR_PRINT)))
526     {
527         fprintf(stderr, "failed to allocate ODR streams\n");
528         exit(1);
529     }
530     setvbuf(stdout, 0, _IONBF, 0);
531
532 #ifdef RPN_QUERY
533 #ifndef PREFIX_QUERY
534     bibset = ccl_qual_mk (); 
535     inf = fopen ("default.bib", "r");
536     if (inf)
537     {
538         ccl_qual_file (bibset, inf);
539         fclose (inf);
540     }
541 #endif
542 #endif
543 }
544
545 static int client(void)
546 {
547     static struct {
548         char *cmd;
549         int (*fun)(char *arg);
550         char *ad;
551     } cmd[] = {
552         {"open", cmd_open, "('tcp'|'osi')':'[<TSEL>'/']<HOST>[':'<PORT>]"},
553         {"quit", cmd_quit, ""},
554         {"find", cmd_find, "<CCL-QUERY>"},
555         {"base", cmd_base, "<BASE-NAME>"},
556         {"show", cmd_show, "<REC#>['+'<#RECS>]"},
557         {"authentication", cmd_authentication, "<ACCTSTRING>"},
558         {"lslb", cmd_lslb, "<largeSetLowerBound>"},
559         {"ssub", cmd_ssub, "<smallSetUpperBound>"},
560         {"mspn", cmd_mspn, "<mediumSetPresentNumber>"},
561         {"status", cmd_status, ""},
562         {"setnames", cmd_setnames, ""},
563         {0,0}
564     };
565     char *netbuffer= 0;
566     int netbufferlen = 0;
567     int i;
568     Z_APDU *apdu;
569
570     while (1)
571     {
572         int res;
573         fd_set input;
574         char line[1024], word[1024], arg[1024];
575
576         FD_ZERO(&input);
577         FD_SET(0, &input);
578         if (conn)
579             FD_SET(cs_fileno(conn), &input);
580         if ((res = select(20, &input, 0, 0, 0)) < 0)
581         {
582             perror("select");
583             exit(1);
584         }
585         if (!res)
586             continue;
587         if (FD_ISSET(0, &input))
588         {
589             /* quick & dirty way to get a command line. */
590             if (!gets(line))
591                 break;
592             if ((res = sscanf(line, "%s %[^;]", word, arg)) <= 0)
593             {
594                 printf(C_PROMPT);
595                 continue;
596             }
597             if (res == 1)
598                 *arg = 0;
599             for (i = 0; cmd[i].cmd; i++)
600                 if (!strncmp(cmd[i].cmd, word, strlen(word)))
601                 {
602                     res = (*cmd[i].fun)(arg);
603                     break;
604                 }
605             if (!cmd[i].cmd) /* dump our help-screen */
606             {
607                 printf("Unknown command: %s.\n", word);
608                 printf("Currently recognized commands:\n");
609                 for (i = 0; cmd[i].cmd; i++)
610                     printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
611                 res = 1;
612             }
613             if (res < 2)
614                 printf(C_PROMPT);
615         }
616         if (conn && FD_ISSET(cs_fileno(conn), &input))
617         {
618             do
619             {
620                 if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0)
621                 {
622                     perror("cs_get");
623                     exit(1);
624                 }
625                 if (!res)
626                 {
627                     printf("Target closed connection.\n");
628                     exit(1);
629                 }
630                 odr_reset(in); /* release APDU from last round */
631                 odr_setbuf(in, netbuffer, res, 0);
632                 if (!z_APDU(in, &apdu, 0))
633                 {
634                     odr_perror(in, "Decoding incoming APDU");
635                     exit(1);
636                 }
637 #if 0
638                 if (!z_APDU(print, &apdu, 0))
639                 {
640                     odr_perror(print, "Failed to print incoming APDU");
641                     odr_reset(print);
642                     continue;
643                 }
644 #endif
645                 switch(apdu->which)
646                 {
647                     case Z_APDU_initResponse:
648                         process_initResponse(apdu->u.initResponse);
649                         break;
650                     case Z_APDU_searchResponse:
651                         process_searchResponse(apdu->u.searchResponse);
652                         break;
653                     case Z_APDU_presentResponse:
654                         printf("Received presentResponse.\n");
655                         setno +=
656                             *apdu->u.presentResponse->numberOfRecordsReturned;
657                         if (apdu->u.presentResponse->records)
658                             display_records(apdu->u.presentResponse->records);
659                         else
660                             printf("No records.\n");
661                         break;
662                     default:
663                         printf("Received unknown APDU type (%d).\n", 
664                             apdu->which);
665                         exit(1);
666                 }
667                 printf("Z> ");
668                 fflush(stdout);
669             }
670             while (cs_more(conn));
671         }
672     }
673     return 0;
674 }
675
676 int main(int argc, char **argv)
677 {
678     initialize();
679     if (argc > 1)
680         cmd_open(argv[1]);
681     else
682         printf(C_PROMPT);
683     return client();
684 }