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