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