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