Implemented various stuff for client and proxy. Updated calls
[yazpp-moved-to-github.git] / src / yaz-client.cpp
1 /*
2  * Copyright (c) 1998-1999, Index Data.
3  * See the file LICENSE for details.
4  * Sebastian Hammer, Adam Dickmeiss
5  * 
6  * $Log: yaz-client.cpp,v $
7  * Revision 1.6  1999-04-20 10:30:05  adam
8  * Implemented various stuff for client and proxy. Updated calls
9  * to ODR to reflect new name parameter.
10  *
11  * Revision 1.5  1999/04/09 11:46:57  adam
12  * Added object Yaz_Z_Assoc. Much more functional client.
13  *
14  * Revision 1.4  1999/03/23 14:17:57  adam
15  * More work on timeout handling. Work on yaz-client.
16  *
17  * Revision 1.3  1999/02/02 14:01:18  adam
18  * First WIN32 port of YAZ++.
19  *
20  * Revision 1.2  1999/01/28 13:08:42  adam
21  * Yaz_PDU_Assoc better encapsulated. Memory leak fix in
22  * yaz-socket-manager.cc.
23  *
24  * Revision 1.1.1.1  1999/01/28 09:41:07  adam
25  * First implementation of YAZ++.
26  *
27  */
28
29 #include <log.h>
30 #include <options.h>
31 #include <diagbib1.h>
32 #include <yaz-ir-assoc.h>
33 #include <yaz-pdu-assoc.h>
34 #include <yaz-socket-manager.h>
35
36 extern "C" {
37 #if HAVE_READLINE_READLINE_H
38 #include <readline/readline.h>
39 #endif
40 #if HAVE_READLINE_HISTORY_H
41 #include <readline/history.h>
42 #endif
43 }
44
45 class YAZ_EXPORT MyClient : public Yaz_IR_Assoc {
46 private:
47     int m_interactive_flag;
48     char m_thisCommand[1024];
49     char m_lastCommand[1024];
50     int m_setOffset;
51     Yaz_SocketManager *m_socketManager;
52 public:
53     MyClient(IYaz_PDU_Observable *the_PDU_Observable,
54              Yaz_SocketManager *the_SocketManager);
55     IYaz_PDU_Observer *clone(IYaz_PDU_Observable *the_PDU_Observable);
56     int args(Yaz_SocketManager *socketManager, int argc, char **argv);
57     int interactive(Yaz_SocketManager *socketManager);
58     int wait();
59     void recv_initResponse(Z_InitResponse *initResponse);
60     void recv_searchResponse(Z_SearchResponse *searchResponse);
61     void recv_presentResponse(Z_PresentResponse *presentResponse);
62     void recv_records (Z_Records *records);
63     void recv_diagrecs(Z_DiagRec **pp, int num);
64     void recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset);
65     void recv_record(Z_DatabaseRecord *record, int offset,
66                      const char *databaseName);
67     void recv_textRecord(int type, const char *buf, size_t len);
68     void recv_genericRecord(Z_GenericRecord *r);
69     void display_genericRecord(Z_GenericRecord *r, int level);
70     void display_variant(Z_Variant *v, int level);
71     int processCommand(const char *cmd);
72     const char *MyClient::getCommand();
73     int cmd_open(char *host);
74     int cmd_quit(char *args);
75     int cmd_close(char *args);
76     int cmd_find(char *args);
77     int cmd_show(char *args);
78     int cmd_cookie(char *args);
79 };
80
81 IYaz_PDU_Observer *MyClient::clone(IYaz_PDU_Observable *the_PDU_Observable)
82
83     return new MyClient(the_PDU_Observable, m_socketManager);
84 }
85
86 MyClient::MyClient(IYaz_PDU_Observable *the_PDU_Observable,
87                    Yaz_SocketManager *the_socketManager) :
88     Yaz_IR_Assoc (the_PDU_Observable)
89 {
90     m_setOffset = 1;
91     m_interactive_flag = 1;
92     m_thisCommand[0] = '\0';
93     m_lastCommand[0] = '\0';
94     m_socketManager = the_socketManager;
95 }
96
97 void usage(char *prog)
98 {
99     fprintf (stderr, "%s: [-v log] [-p proxy] [zurl]\n", prog);
100     exit (1);
101 }
102
103 void MyClient::recv_initResponse(Z_InitResponse *initResponse)
104 {
105     printf ("Got InitResponse. Status ");
106     if (*initResponse->result)
107         printf ("Ok\n");
108     else
109         printf ("Fail\n");
110 }
111
112 void MyClient::recv_diagrecs(Z_DiagRec **pp, int num)
113 {
114     int i;
115     oident *ent;
116     Z_DefaultDiagFormat *r;
117
118     printf("Diagnostic message(s) from database:\n");
119     for (i = 0; i<num; i++)
120     {
121         Z_DiagRec *p = pp[i];
122         if (p->which != Z_DiagRec_defaultFormat)
123         {
124             printf("Diagnostic record not in default format.\n");
125             return;
126         }
127         else
128             r = p->u.defaultFormat;
129         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
130             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
131             printf("Missing or unknown diagset\n");
132         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
133 #ifdef ASN_COMPILED
134         switch (r->which)
135         {
136         case Z_DefaultDiagFormat_v2Addinfo:
137             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
138             break;
139         case Z_DefaultDiagFormat_v3Addinfo:
140             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
141             break;
142         }
143 #else
144         if (r->addinfo && *r->addinfo)
145             printf(" -- '%s'\n", r->addinfo);
146         else
147             printf("\n");
148 #endif
149     }
150 }
151
152 void MyClient::recv_textRecord(int type, const char *buf, size_t len)
153 {
154     fwrite (buf, 1, len, stdout);
155     fputc ('\n', stdout);
156 }
157
158 void MyClient::display_variant(Z_Variant *v, int level)
159 {
160     int i;
161
162     for (i = 0; i < v->num_triples; i++)
163     {
164         printf("%*sclass=%d,type=%d", level * 4, "", *v->triples[i]->zclass,
165             *v->triples[i]->type);
166         if (v->triples[i]->which == Z_Triple_internationalString)
167             printf(",value=%s\n", v->triples[i]->value.internationalString);
168         else
169             printf("\n");
170     }
171 }
172
173 void MyClient::display_genericRecord(Z_GenericRecord *r, int level)
174 {
175     int i;
176
177     if (!r)
178         return;
179     for (i = 0; i < r->num_elements; i++)
180     {
181         Z_TaggedElement *t;
182
183         printf("%*s", level * 4, "");
184         t = r->elements[i];
185         printf("(");
186         if (t->tagType)
187             printf("%d,", *t->tagType);
188         else
189             printf("?,");
190         if (t->tagValue->which == Z_StringOrNumeric_numeric)
191             printf("%d) ", *t->tagValue->u.numeric);
192         else
193             printf("%s) ", t->tagValue->u.string);
194         if (t->content->which == Z_ElementData_subtree)
195         {
196             printf("\n");
197             display_genericRecord(t->content->u.subtree, level+1);
198         }
199         else if (t->content->which == Z_ElementData_string)
200             printf("%s\n", t->content->u.string);
201         else if (t->content->which == Z_ElementData_numeric)
202             printf("%d\n", *t->content->u.numeric);
203         else if (t->content->which == Z_ElementData_oid)
204         {
205             int *ip = t->content->u.oid;
206             oident *oent;
207
208             if ((oent = oid_getentbyoid(t->content->u.oid)))
209                 printf("OID: %s\n", oent->desc);
210             else
211             {
212                 printf("{");
213                 while (ip && *ip >= 0)
214                     printf(" %d", *(ip++));
215                 printf(" }\n");
216             }
217         }
218         else if (t->content->which == Z_ElementData_noDataRequested)
219             printf("[No data requested]\n");
220         else if (t->content->which == Z_ElementData_elementEmpty)
221             printf("[Element empty]\n");
222         else if (t->content->which == Z_ElementData_elementNotThere)
223             printf("[Element not there]\n");
224         else
225             printf("??????\n");
226         if (t->appliedVariant)
227             display_variant(t->appliedVariant, level+1);
228         if (t->metaData && t->metaData->supportedVariants)
229         {
230             int c;
231
232             printf("%*s---- variant list\n", (level+1)*4, "");
233             for (c = 0; c < t->metaData->num_supportedVariants; c++)
234             {
235                 printf("%*svariant #%d\n", (level+1)*4, "", c);
236                 display_variant(t->metaData->supportedVariants[c], level + 2);
237             }
238         }
239     }
240 }
241
242 void MyClient::recv_genericRecord(Z_GenericRecord *r)
243 {
244     display_genericRecord(r, 0);
245 }
246
247 void MyClient::recv_record(Z_DatabaseRecord *record, int offset,
248                            const char *databaseName)
249 {
250     Z_External *r = (Z_External*) record;
251     oident *ent = oid_getentbyoid(r->direct_reference);
252
253     /*
254      * Tell the user what we got.
255      */
256     if (r->direct_reference)
257     {
258         printf("Record type: ");
259         if (ent)
260             printf("%s\n", ent->desc);
261     }
262     /* Check if this is a known, ASN.1 type tucked away in an octet string */
263     Z_ext_typeent *etype = z_ext_getentbyref(ent->value);
264     if (ent && (r->which == Z_External_octet || r->which == Z_External_single)
265         && (etype = z_ext_getentbyref(ent->value)))
266
267     {
268         void *rr;
269         /*
270          * Call the given decoder to process the record.
271          */
272         odr_setbuf(odr_decode(), (char*)record->u.octet_aligned->buf,
273                    record->u.octet_aligned->len, 0);
274         if (!(*etype->fun)(odr_decode(), (char **)&rr, 0, 0))
275         {
276             odr_perror(odr_decode(), "Decoding constructed record.");
277             fprintf(stderr, "[Near %d]\n", odr_offset(odr_decode()));
278             fprintf(stderr, "Packet dump:\n---------\n");
279             odr_dumpBER(stderr, (char*)record->u.octet_aligned->buf,
280                         record->u.octet_aligned->len);
281             fprintf(stderr, "---------\n");
282         }
283         if (etype->what == Z_External_sutrs)
284         {
285             Z_SUTRS *sutrs = (Z_SUTRS *) rr;
286             recv_textRecord ((int) VAL_SUTRS, (const char *) sutrs->buf,
287                              (size_t) sutrs->len);
288         }
289         return;
290     }
291     if (r->which == Z_External_octet && record->u.octet_aligned->len)
292     {
293         recv_textRecord((int) ent->value,
294                         (const char *) record->u.octet_aligned->buf,
295                         (size_t) record->u.octet_aligned->len);
296     }
297     else if (ent && ent->value == VAL_SUTRS && r->which == Z_External_sutrs)
298         recv_textRecord((int) VAL_SUTRS, (const char *) r->u.sutrs->buf,
299                         (size_t) r->u.sutrs->len);
300     else if (ent && ent->value == VAL_GRS1 && r->which == Z_External_grs1)
301         recv_genericRecord(r->u.grs1);
302     else 
303     {
304         printf("Unknown record representation.\n");
305         if (!z_External(odr_print(), &r, 0))
306         {
307             odr_perror(odr_print(), "Printing external");
308             odr_reset(odr_print());
309         }
310     }    
311 }
312
313 void MyClient::recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset)
314 {
315     if (zpr->databaseName)
316         printf("[%s]", zpr->databaseName);
317     if (zpr->which == Z_NamePlusRecord_surrogateDiagnostic)
318         recv_diagrecs(&zpr->u.surrogateDiagnostic, 1);
319     else
320         recv_record(zpr->u.databaseRecord, offset, zpr->databaseName);
321 }
322
323 void MyClient::recv_records (Z_Records *records)
324 {
325     Z_DiagRec dr, *dr_p = &dr;
326     int i;
327     switch (records->which)
328     {
329     case Z_Records_DBOSD:
330         for (i = 0; i < records->u.databaseOrSurDiagnostics->num_records; i++)
331             recv_namePlusRecord(records->u.databaseOrSurDiagnostics->
332                                 records[i], i + m_setOffset);
333         m_setOffset += records->u.databaseOrSurDiagnostics->num_records;
334         break;
335     case Z_Records_NSD:
336 #ifdef ASN_COMPILED
337         dr.which = Z_DiagRec_defaultFormat;
338         dr.u.defaultFormat = records->u.nonSurrogateDiagnostic;
339         recv_diagrecs (&dr_p, 1);
340 #else
341         recv_diagrecs (&records->u.nonSurrogateDiagnostic, 1);
342 #endif
343         break;
344     case Z_Records_multipleNSD:
345         recv_diagrecs (records->u.multipleNonSurDiagnostics->diagRecs,
346                        records->u.multipleNonSurDiagnostics->num_diagRecs);
347         break;
348     }
349 }
350
351 void MyClient::recv_searchResponse(Z_SearchResponse *searchResponse)
352 {
353     printf ("Got SearchResponse. Status ");
354     if (!*searchResponse->searchStatus)
355     {
356         printf ("Fail\n");
357         return;
358     }
359     printf ("Ok\n");
360     printf ("Hits: %d\n", *searchResponse->resultCount);
361     if (searchResponse->records)
362         recv_records (searchResponse->records);
363 }
364
365 void MyClient::recv_presentResponse(Z_PresentResponse *presentResponse)
366 {
367     printf ("Got PresentResponse\n");
368     recv_records (presentResponse->records);
369 }
370
371 int MyClient::wait()
372 {
373     set_lastReceived(0);
374     while (m_socketManager->processEvent() > 0)
375     {
376         if (get_lastReceived())
377             return 1;
378     }
379     return 0;
380 }
381
382 #define C_PROMPT "Z>"
383
384 int MyClient::cmd_open(char *host)
385 {
386     client (host);
387     if (send_initRequest() >= 0)
388         wait();
389     else
390         close();
391     return 1;
392 }
393
394 int MyClient::cmd_quit(char *args)
395 {
396     return 0;
397 }
398
399 int MyClient::cmd_close(char *args)
400 {
401     close();
402     return 1;
403 }
404
405 int MyClient::cmd_find(char *args)
406 {
407     Yaz_Z_Query query;
408
409     if (query.set_rpn(args) <= 0)
410     {
411         printf ("Bad RPN query\n");
412         return 1;
413     }
414     if (send_searchRequest(&query) >= 0)
415         wait();
416     return 1;
417 }
418
419 int MyClient::cmd_show(char *args)
420 {
421     int start = m_setOffset, number = 1;
422
423     sscanf (args, "%d %d", &start, &number);
424     if (send_presentRequest(start, number) >= 0)
425         wait();
426     return 1;
427 }
428
429 int MyClient::cmd_cookie(char *args)
430 {
431     set_cookie(args);
432     return 1;
433 }
434
435 int MyClient::processCommand(const char *commandLine)
436 {
437     char cmdStr[1024], cmdArgs[1024];
438     cmdArgs[0] = '\0';
439     cmdStr[0] = '\0';
440     static struct {
441         char *cmd;
442         int (MyClient::*fun)(char *arg);
443         char *ad;
444     } cmd[] = {
445         {"open", &cmd_open, "<host>[':'<port>][/<database>]"},
446         {"quit", &cmd_quit, ""},
447         {"close", &cmd_close, ""},
448         {"find", &cmd_find, "<query>"},
449         {"show", &cmd_show, "[<start> [<number>]]"},
450         {"cookie", &cmd_cookie, "<cookie>"},
451         {0,0,0}
452     };
453     
454     if (sscanf(commandLine, "%s %[^;]", cmdStr, cmdArgs) < 1)
455         return 1;
456     int i;
457     for (i = 0; cmd[i].cmd; i++)
458         if (!strncmp(cmd[i].cmd, cmdStr, strlen(cmdStr)))
459             break;
460     
461     int res = 1;
462     if (cmd[i].cmd) // Invoke command handler
463         res = (this->*cmd[i].fun)(cmdArgs);
464     else            // Dump help screen
465     {
466         printf("Unknown command: %s.\n", cmdStr);
467         printf("Currently recognized commands:\n");
468         for (i = 0; cmd[i].cmd; i++)
469             printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
470     }
471     return res;
472 }
473
474 const char *MyClient::getCommand()
475 {
476 #if HAVE_READLINE_READLINE_H
477     // Read using GNU readline
478     char *line_in;
479     line_in=readline(C_PROMPT);
480     if (!line_in)
481         return 0;
482 #if HAVE_READLINE_HISTORY_H
483     if (*line_in)
484         add_history(line_in);
485 #endif
486     strncpy(m_thisCommand,line_in, 1023);
487     m_thisCommand[1023] = '\0';
488     free (line_in);
489 #else    
490     // Read using fgets(3)
491     printf (C_PROMPT);
492     fflush(stdout);
493     if (!fgets(m_thisCommand, 1023, stdin))
494         return 0;
495 #endif
496     // Remove trailing whitespace
497     char *cp = m_thisCommand + strlen(m_thisCommand);
498     while (cp != m_thisCommand && strchr("\t \n", cp[-1]))
499         cp--;
500     *cp = '\0';
501     cp = m_thisCommand;
502     // Remove leading spaces...
503     while (*cp && strchr ("\t \n", *cp))
504         cp++;
505     // Save command if non-empty
506     if (*cp != '\0')
507         strcpy (m_lastCommand, cp);
508     return m_lastCommand;
509 }
510
511 int MyClient::interactive(Yaz_SocketManager *socketManager)
512 {
513     const char *cmd;
514     if (!m_interactive_flag)
515         return 0;
516     while ((cmd = getCommand()))
517     {
518         if (!processCommand(cmd))
519             break;
520     }
521     return 0;
522 }
523
524 int MyClient::args(Yaz_SocketManager *socketManager, int argc, char **argv)
525 {
526     char *host = 0;
527     char *proxy = 0;
528     char *arg;
529     char *prog = argv[0];
530     int ret;
531
532     while ((ret = options("p:v:q", argv, argc, &arg)) != -2)
533     {
534         switch (ret)
535         {
536         case 0:
537             if (host)
538             {
539                 usage(prog);
540                 return 1;
541             }
542             host = arg;
543             break;
544         case 'p':
545             if (proxy)
546             {
547                 usage(prog);
548                 return 1;
549             }
550             set_proxy(arg);
551             break;
552         case 'v':
553             log_init_level (log_mask_str(arg));
554             break;
555         case 'q':
556             m_interactive_flag = 0;
557             break;
558         default:
559             usage(prog);
560             return 1;
561         }
562     }
563     if (host)
564     {
565         client (host);
566         send_initRequest();
567         wait ();
568     }
569     return 0;
570 }
571
572 int main(int argc, char **argv)
573 {
574     Yaz_SocketManager mySocketManager;
575     Yaz_PDU_Assoc *some = new Yaz_PDU_Assoc(&mySocketManager, 0);
576
577     MyClient z(some, &mySocketManager);
578
579     if (z.args(&mySocketManager, argc, argv))
580         exit (1);
581     if (z.interactive(&mySocketManager))
582         exit (1);
583 }