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