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