Release 1.6.4
[yazpp-moved-to-github.git] / src / yaz-my-client.cpp
1 /* This file is part of the yazpp toolkit.
2  * Copyright (C) Index Data 
3  * See the file LICENSE for details.
4  */
5
6 #if HAVE_CONFIG_H
7 #include <config.h>
8 #endif
9 #include <stdlib.h>
10 #include <yaz/log.h>
11 #include <yaz/options.h>
12 #include <yaz/diagbib1.h>
13 #include <yaz/marcdisp.h>
14 #include <yazpp/ir-assoc.h>
15 #include <yazpp/pdu-assoc.h>
16 #include <yazpp/socket-manager.h>
17 #include <yaz/oid_db.h>
18
19 extern "C" {
20 #if HAVE_READLINE_READLINE_H
21 #include <readline/readline.h>
22 #endif
23 #if HAVE_READLINE_HISTORY_H
24 #include <readline/history.h>
25 #endif
26 }
27
28 using namespace yazpp_1;
29
30 class YAZ_EXPORT MyClient : public IR_Assoc {
31 private:
32     int m_interactive_flag;
33     char m_thisCommand[1024];
34     char m_lastCommand[1024];
35     int m_setOffset;
36     SocketManager *m_socketManager;
37 public:
38     MyClient(IPDU_Observable *the_PDU_Observable,
39              SocketManager *the_SocketManager);
40     IPDU_Observer *sessionNotify(
41         IPDU_Observable *the_PDU_Observable, int fd);
42     int args(SocketManager *socketManager, int argc, char **argv);
43     int interactive(SocketManager *socketManager);
44     int wait();
45     void recv_initResponse(Z_InitResponse *initResponse);
46     void recv_searchResponse(Z_SearchResponse *searchResponse);
47     void recv_presentResponse(Z_PresentResponse *presentResponse);
48     void recv_records (Z_Records *records);
49     void recv_diagrecs(Z_DiagRec **pp, int num);
50     void recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset);
51     void recv_record(Z_DatabaseRecord *record, int offset,
52                      const char *databaseName);
53     void recv_textRecord(const char *buf, size_t len);
54     void recv_genericRecord(Z_GenericRecord *r);
55     void connectNotify();
56     void failNotify();
57     void timeoutNotify();
58     char *get_cookie (Z_OtherInformation **oi);
59     int processCommand(const char *cmd);
60     const char *getCommand();
61     int cmd_open(char *host);
62     int cmd_connect(char *host);
63     int cmd_quit(char *args);
64     int cmd_close(char *args);
65     int cmd_find(char *args);
66     int cmd_show(char *args);
67     int cmd_cookie(char *args);
68     int cmd_init(char *args);
69     int cmd_format(char *args);
70     int cmd_proxy(char *args);
71 };
72
73
74 void MyClient::connectNotify()
75 {
76     printf ("Connection accepted by target\n");
77     set_lastReceived(-1);
78 }
79
80 void MyClient::timeoutNotify()
81 {
82     printf ("Connection timeout\n");
83     close();
84 }
85
86 void MyClient::failNotify()
87 {
88     printf ("Connection closed by target\n");
89     set_lastReceived(-1);
90 }
91
92 IPDU_Observer *MyClient::sessionNotify(IPDU_Observable *the_PDU_Observable,
93                                        int fd)
94 {
95     return new MyClient(the_PDU_Observable, m_socketManager);
96 }
97
98 MyClient::MyClient(IPDU_Observable *the_PDU_Observable,
99                    SocketManager *the_socketManager) :
100     IR_Assoc (the_PDU_Observable)
101 {
102     m_setOffset = 1;
103     m_interactive_flag = 1;
104     m_thisCommand[0] = '\0';
105     m_lastCommand[0] = '\0';
106     m_socketManager = the_socketManager;
107 }
108
109 void usage(char *prog)
110 {
111     fprintf (stderr, "%s: [-v log] [-c cookie] [-p proxy] [zurl]\n", prog);
112     exit (1);
113 }
114
115 char *MyClient::get_cookie(Z_OtherInformation **otherInfo)
116 {
117     Z_OtherInformationUnit *oi =
118         update_otherInformation(otherInfo, 0, yaz_oid_userinfo_cookie, 1, 1);
119
120     if (oi && oi->which == Z_OtherInfo_characterInfo)
121         return oi->information.characterInfo;
122     return 0;
123 }
124
125 void MyClient::recv_initResponse(Z_InitResponse *initResponse)
126 {
127     printf ("Got InitResponse. Status ");
128     if (*initResponse->result)
129     {
130         printf ("Ok\n");
131
132         const char *p = get_cookie (&initResponse->otherInfo);
133         if (p)
134         {
135             printf ("cookie = %s\n", p);
136             set_cookie(p);
137         }
138     }
139     else
140         printf ("Fail\n");
141 }
142
143 void MyClient::recv_diagrecs(Z_DiagRec **pp, int num)
144 {
145     int i;
146     Z_DefaultDiagFormat *r;
147
148     printf("Diagnostic message(s) from database:\n");
149     for (i = 0; i<num; i++)
150     {
151         Z_DiagRec *p = pp[i];
152         if (p->which != Z_DiagRec_defaultFormat)
153         {
154             printf("Diagnostic record not in default format.\n");
155             return;
156         }
157         else
158             r = p->u.defaultFormat;
159              printf("    [" ODR_INT_PRINTF "] %s", *r->condition, diagbib1_str(*r->condition));
160         switch (r->which)
161         {
162         case Z_DefaultDiagFormat_v2Addinfo:
163             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
164             break;
165         case Z_DefaultDiagFormat_v3Addinfo:
166             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
167             break;
168         }
169     }
170 }
171
172 void MyClient::recv_textRecord(const char *buf, size_t len)
173 {
174     fwrite (buf, 1, len, stdout);
175     fputc ('\n', stdout);
176 }
177
178 void MyClient::recv_genericRecord(Z_GenericRecord *r)
179 {
180     WRBUF w = wrbuf_alloc();
181     yaz_display_grs1(w, r, 0);
182     fwrite(wrbuf_buf(w), 1, wrbuf_len(w), stdout);
183     wrbuf_destroy(w);
184 }
185
186 void MyClient::recv_record(Z_DatabaseRecord *record, int offset,
187                            const char *databaseName)
188 {
189     Z_External *r = (Z_External*) record;
190     /*
191      * Tell the user what we got.
192      */
193     if (r->direct_reference)
194     {
195         char name_oid_str[OID_STR_MAX];
196         const char *name_oid = yaz_oid_to_string_buf(r->direct_reference, 0,
197                                                      name_oid_str);
198         printf("Record type: %s\n", name_oid ? name_oid : "unknown");
199     }
200     if (r->which == Z_External_octet && record->u.octet_aligned->len)
201     {
202         if (yaz_oid_is_iso2709(r->direct_reference))
203         {
204             yaz_marc_t mt = yaz_marc_create();
205
206             const char *result_buf;
207             size_t result_size;
208             yaz_marc_decode_buf(mt, (const char *)
209                                 record->u.octet_aligned->buf,
210                                 record->u.octet_aligned->len,
211                                 &result_buf, &result_size);
212             fwrite(result_buf, 1, result_size, stdout);
213             yaz_marc_destroy(mt);
214         }
215         else
216         {
217             recv_textRecord((const char *) record->u.octet_aligned->buf,
218                             (size_t) record->u.octet_aligned->len);
219         }
220     }
221     else if (r->which == Z_External_sutrs)
222         recv_textRecord((const char *) r->u.sutrs->buf,
223                         (size_t) r->u.sutrs->len);
224     else if (r->which == Z_External_grs1)
225         recv_genericRecord(r->u.grs1);
226     else
227     {
228         printf("Unknown record representation.\n");
229         if (!z_External(odr_print(), &r, 0, 0))
230         {
231             odr_perror(odr_print(), "Printing external");
232             odr_reset(odr_print());
233         }
234     }
235 }
236
237 void MyClient::recv_namePlusRecord (Z_NamePlusRecord *zpr, int offset)
238 {
239     if (zpr->databaseName)
240         printf("[%s]", zpr->databaseName);
241     if (zpr->which == Z_NamePlusRecord_surrogateDiagnostic)
242         recv_diagrecs(&zpr->u.surrogateDiagnostic, 1);
243     else
244         recv_record(zpr->u.databaseRecord, offset, zpr->databaseName);
245 }
246
247 void MyClient::recv_records (Z_Records *records)
248 {
249     Z_DiagRec dr, *dr_p = &dr;
250     if (!records)
251         return;
252     int i;
253     switch (records->which)
254     {
255     case Z_Records_DBOSD:
256         for (i = 0; i < records->u.databaseOrSurDiagnostics->num_records; i++)
257             recv_namePlusRecord(records->u.databaseOrSurDiagnostics->
258                                 records[i], i + m_setOffset);
259         m_setOffset += records->u.databaseOrSurDiagnostics->num_records;
260         break;
261     case Z_Records_NSD:
262         dr.which = Z_DiagRec_defaultFormat;
263         dr.u.defaultFormat = records->u.nonSurrogateDiagnostic;
264         recv_diagrecs (&dr_p, 1);
265         break;
266     case Z_Records_multipleNSD:
267         recv_diagrecs (records->u.multipleNonSurDiagnostics->diagRecs,
268                        records->u.multipleNonSurDiagnostics->num_diagRecs);
269         break;
270     }
271 }
272
273 void MyClient::recv_searchResponse(Z_SearchResponse *searchResponse)
274 {
275     printf ("Got SearchResponse. Status ");
276     if (!*searchResponse->searchStatus)
277     {
278         printf ("Fail\n");
279     }
280     else
281     {
282         printf ("Ok\n");
283         printf ("Hits: " ODR_INT_PRINTF "\n", *searchResponse->resultCount);
284     }
285     recv_records (searchResponse->records);
286 }
287
288 void MyClient::recv_presentResponse(Z_PresentResponse *presentResponse)
289 {
290     printf ("Got PresentResponse\n");
291     recv_records (presentResponse->records);
292 }
293
294 int MyClient::wait()
295 {
296     set_lastReceived(0);
297     while (m_socketManager->processEvent() > 0)
298     {
299         if (get_lastReceived())
300             return 1;
301     }
302     return 0;
303 }
304
305
306 #define C_PROMPT "Z>"
307
308 int MyClient::cmd_connect(char *host)
309 {
310     client (host);
311     timeout (10);
312     wait ();
313     timeout (-1);
314     return 1;
315 }
316
317 int MyClient::cmd_open(char *host)
318 {
319     client (host);
320     timeout (10);
321     wait ();
322     timeout (-1);
323     send_initRequest();
324     wait ();
325     return 1;
326 }
327
328 int MyClient::cmd_init(char *args)
329 {
330     if (send_initRequest() >= 0)
331         wait();
332     else
333         close();
334     return 1;
335 }
336
337 int MyClient::cmd_quit(char *args)
338 {
339     return 0;
340 }
341
342 int MyClient::cmd_close(char *args)
343 {
344     close();
345     return 1;
346 }
347
348 int MyClient::cmd_find(char *args)
349 {
350     Yaz_Z_Query query;
351
352     if (query.set_rpn(args) <= 0)
353     {
354         printf ("Bad RPN query\n");
355         return 1;
356     }
357     if (send_searchRequest(&query) >= 0)
358         wait();
359     else
360         printf ("Not connected\n");
361     return 1;
362 }
363
364 int MyClient::cmd_show(char *args)
365 {
366     int start = m_setOffset, number = 1;
367
368     sscanf (args, "%d %d", &start, &number);
369     m_setOffset = start;
370     if (send_presentRequest(start, number) >= 0)
371         wait();
372     else
373         printf ("Not connected\n");
374     return 1;
375 }
376
377 int MyClient::cmd_cookie(char *args)
378 {
379     set_cookie(*args ? args : 0);
380     return 1;
381 }
382
383 int MyClient::cmd_format(char *args)
384 {
385     set_preferredRecordSyntax(args);
386     return 1;
387 }
388
389 int MyClient::cmd_proxy(char *args)
390 {
391     set_proxy(args);
392     return 1;
393 }
394
395 int MyClient::processCommand(const char *commandLine)
396 {
397     char cmdStr[1024], cmdArgs[1024];
398     cmdArgs[0] = '\0';
399     cmdStr[0] = '\0';
400     static struct {
401         const char *cmd;
402         int (MyClient::*fun)(char *arg);
403         const char *ad;
404     } cmd[] = {
405         {"open", &MyClient::cmd_open, "<host>[':'<port>][/<database>]"},
406         {"connect", &MyClient::cmd_connect, "<host>[':'<port>][/<database>]"},
407         {"quit", &MyClient::cmd_quit, ""},
408         {"close", &MyClient::cmd_close, ""},
409         {"find", &MyClient::cmd_find, "<query>"},
410         {"show", &MyClient::cmd_show, "[<start> [<number>]]"},
411         {"cookie", &MyClient::cmd_cookie, "<cookie>"},
412         {"init", &MyClient::cmd_init, ""},
413         {"format", &MyClient::cmd_format, "<record-syntax>"},
414         {"proxy", &MyClient::cmd_proxy, "<host>:[':'<port>]"},
415         {0,0,0}
416     };
417
418     if (sscanf(commandLine, "%s %[^;]", cmdStr, cmdArgs) < 1)
419         return 1;
420     int i;
421     for (i = 0; cmd[i].cmd; i++)
422         if (!strncmp(cmd[i].cmd, cmdStr, strlen(cmdStr)))
423             break;
424
425     int res = 1;
426     if (cmd[i].cmd) // Invoke command handler
427         res = (this->*cmd[i].fun)(cmdArgs);
428     else            // Dump help screen
429     {
430         printf("Unknown command: %s.\n", cmdStr);
431         printf("Currently recognized commands:\n");
432         for (i = 0; cmd[i].cmd; i++)
433             printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
434     }
435     return res;
436 }
437
438 const char *MyClient::getCommand()
439 {
440 #if HAVE_READLINE_READLINE_H
441     // Read using GNU readline
442     char *line_in;
443     line_in=readline(C_PROMPT);
444     if (!line_in)
445         return 0;
446 #if HAVE_READLINE_HISTORY_H
447     if (*line_in)
448         add_history(line_in);
449 #endif
450     strncpy(m_thisCommand,line_in, 1023);
451     m_thisCommand[1023] = '\0';
452     free (line_in);
453 #else
454     // Read using fgets(3)
455     printf (C_PROMPT);
456     fflush(stdout);
457     if (!fgets(m_thisCommand, 1023, stdin))
458         return 0;
459 #endif
460     // Remove trailing whitespace
461     char *cp = m_thisCommand + strlen(m_thisCommand);
462     while (cp != m_thisCommand && strchr("\t \n", cp[-1]))
463         cp--;
464     *cp = '\0';
465     cp = m_thisCommand;
466     // Remove leading spaces...
467     while (*cp && strchr ("\t \n", *cp))
468         cp++;
469     // Save command if non-empty
470     if (*cp != '\0')
471         strcpy (m_lastCommand, cp);
472     return m_lastCommand;
473 }
474
475 int MyClient::interactive(SocketManager *socketManager)
476 {
477     const char *cmd;
478     if (!m_interactive_flag)
479         return 0;
480     while ((cmd = getCommand()))
481     {
482         if (!processCommand(cmd))
483             break;
484     }
485     return 0;
486 }
487
488 int MyClient::args(SocketManager *socketManager, int argc, char **argv)
489 {
490     char *host = 0;
491     char *proxy = 0;
492     char *arg;
493     char *prog = argv[0];
494     int ret;
495
496     while ((ret = options("c:p:v:q", argv, argc, &arg)) != -2)
497     {
498         switch (ret)
499         {
500         case 0:
501             if (host)
502             {
503                 usage(prog);
504                 return 1;
505             }
506             host = arg;
507             break;
508         case 'p':
509             if (proxy)
510             {
511                 usage(prog);
512                 return 1;
513             }
514             set_proxy(arg);
515             break;
516         case 'c':
517             set_cookie(arg);
518             break;
519         case 'v':
520             yaz_log_init_level (yaz_log_mask_str(arg));
521             break;
522         case 'q':
523             m_interactive_flag = 0;
524             break;
525         default:
526             usage(prog);
527             return 1;
528         }
529     }
530     if (host)
531     {
532         client (host);
533         timeout (10);
534         wait ();
535         timeout (-1);
536         send_initRequest();
537         wait ();
538     }
539     return 0;
540 }
541
542 int main(int argc, char **argv)
543 {
544     SocketManager mySocketManager;
545     PDU_Assoc *some = new PDU_Assoc(&mySocketManager);
546
547     MyClient z(some, &mySocketManager);
548
549     if (z.args(&mySocketManager, argc, argv))
550         exit (1);
551     if (z.interactive(&mySocketManager))
552         exit (1);
553     return 0;
554 }
555 /*
556  * Local variables:
557  * c-basic-offset: 4
558  * c-file-style: "Stroustrup"
559  * indent-tabs-mode: nil
560  * End:
561  * vim: shiftwidth=4 tabstop=8 expandtab
562  */
563