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