ZOOM now allows inspection of X509 peer certificate for verification.
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /*
2  * Copyright (c) 2002-2004, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: zoomsh.c,v 1.28 2004-04-28 22:45:00 adam Exp $
6  */
7
8 /* ZOOM-C Shell */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <ctype.h>
14
15 #include <yaz/comstack.h>
16
17 #if HAVE_READLINE_READLINE_H
18 #include <readline/readline.h> 
19 #endif
20 #if HAVE_READLINE_HISTORY_H
21 #include <readline/history.h>
22 #endif
23
24 #include <yaz/xmalloc.h>
25
26 #include <yaz/log.h>
27 #include <yaz/nmem.h>
28 #include <yaz/zoom.h>
29 #include <yaz/oid.h>
30
31 #define MAX_CON 100
32
33 #if 0
34 static void dummy()
35 {
36         cs_get_ssl(0);
37 }
38 #endif
39
40 static int next_token (const char **cpp, const char **t_start)
41 {
42     int len = 0;
43     const char *cp = *cpp;
44     while (*cp == ' ')
45         cp++;
46     if (*cp == '"')
47     {
48         cp++;
49         *t_start = cp;
50         while (*cp && *cp != '"')
51         {
52             cp++;
53             len++;
54         }
55         if (*cp)
56             cp++;
57     }
58     else
59     {
60         *t_start = cp;
61         while (*cp && *cp != ' ' && *cp != '\r' && *cp != '\n')
62         {
63             cp++;
64             len++;
65         }
66         if (len == 0)
67             len = -1;
68     }
69     *cpp = cp;
70     return len;  /* return -1 if no token was read .. */
71 }
72
73 static int next_token_copy (const char **cpp, char *buf_out, int buf_max)
74 {
75     const char *start;
76     int len = next_token (cpp, &start);
77     if (len < 0)
78     {
79         *buf_out = 0;
80         return len;
81     }
82     if (len >= buf_max)
83         len = buf_max-1;
84     memcpy (buf_out, start, len);
85     buf_out[len] = '\0';
86     return len;
87 }
88
89 static int is_command (const char *cmd_str, const char *this_str, int this_len)
90 {
91     int cmd_len = strlen(cmd_str);
92     if (cmd_len != this_len)
93         return 0;
94     if (memcmp (cmd_str, this_str, cmd_len))
95         return 0;
96     return 1;
97 }
98
99 static void cmd_set (ZOOM_connection *c, ZOOM_resultset *r,
100                      ZOOM_options options,
101                      const char **args)
102 {
103     char key[40], val[80];
104
105     if (next_token_copy (args, key, sizeof(key)) < 0)
106     {
107         printf ("missing argument for set\n");
108         return ;
109     }
110     if (next_token_copy (args, val, sizeof(val)) < 0)
111         ZOOM_options_set(options, key, 0);
112     else
113         ZOOM_options_set(options, key, val);
114 }
115
116 static void cmd_get (ZOOM_connection *c, ZOOM_resultset *r,
117                      ZOOM_options options,
118                      const char **args)
119 {
120     char key[40];
121     if (next_token_copy (args, key, sizeof(key)) < 0)
122     {
123         printf ("missing argument for get\n");
124     }
125     else
126     {
127         const char *val = ZOOM_options_get(options, key);
128         printf ("%s = %s\n", key, val ? val : "<null>");
129     }
130 }
131
132 static void cmd_close (ZOOM_connection *c, ZOOM_resultset *r,
133                        ZOOM_options options,
134                        const char **args)
135 {
136     char host[60];
137     int i;
138     next_token_copy (args, host, sizeof(host));
139     for (i = 0; i<MAX_CON; i++)
140     {
141         const char *h;
142         if (!c[i])
143             continue;
144         if ((h = ZOOM_connection_option_get(c[i], "host"))
145             && !strcmp (h, host))
146         {
147             ZOOM_connection_destroy (c[i]);
148             c[i] = 0;
149         }
150         else if (*host == '\0')
151         {
152             ZOOM_connection_destroy (c[i]);
153             c[i] = 0;
154         }
155     }
156 }
157
158 static void display_records (ZOOM_connection c,
159                              ZOOM_resultset r,
160                              int start, int count)
161 {
162     int i;
163     for (i = 0; i<count; i++)
164     {
165         int pos = i + start;
166         ZOOM_record rec = ZOOM_resultset_record (r, pos);
167         const char *db = ZOOM_record_get (rec, "database", 0);
168         int len, opac_len;
169         const char *render = ZOOM_record_get (rec, "render", &len);
170         const char *opac_render = ZOOM_record_get (rec, "opac", &opac_len);
171         const char *syntax = ZOOM_record_get (rec, "syntax", 0);
172         /* if rec is non-null, we got a record for display */
173         if (rec)
174         {
175             char oidbuf[100];
176             (void) oid_name_to_dotstring(CLASS_RECSYN, syntax, oidbuf);
177             printf ("%d %s %s (%s)\n",
178                     pos+1, (db ? db : "unknown"), syntax, oidbuf);
179             if (render)
180                 fwrite (render, 1, len, stdout);
181             printf ("\n");
182             if (opac_render)
183                 fwrite (opac_render, 1, opac_len, stdout);
184         }
185             
186     }
187 }
188
189 static void cmd_show (ZOOM_connection *c, ZOOM_resultset *r,
190                       ZOOM_options options,
191                       const char **args)
192 {
193     int i;
194     char start_str[10], count_str[10];
195
196     if (next_token_copy (args, start_str, sizeof(start_str)) >= 0)
197         ZOOM_options_set (options, "start", start_str);
198
199     if (next_token_copy (args, count_str, sizeof(count_str)) >= 0)
200         ZOOM_options_set (options, "count", count_str);
201
202     for (i = 0; i<MAX_CON; i++)
203         ZOOM_resultset_records (r[i], 0, atoi(start_str), atoi(count_str));
204     while (ZOOM_event (MAX_CON, c))
205         ;
206
207     for (i = 0; i<MAX_CON; i++)
208     {
209         int error;
210         const char *errmsg, *addinfo, *dset;
211         /* display errors if any */
212         if (!c[i])
213             continue;
214         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
215             printf ("%s error: %s (%s:%d) %s\n",
216                      ZOOM_connection_option_get(c[i], "host"), errmsg,
217                      dset, error, addinfo);
218         else if (r[i])
219         {
220             /* OK, no major errors. Display records... */
221             int start = ZOOM_options_get_int (options, "start", 0);
222             int count = ZOOM_options_get_int (options, "count", 0);
223             display_records (c[i], r[i], start, count);
224         }
225     }
226     ZOOM_options_set (options, "count", "0");
227     ZOOM_options_set (options, "start", "0");
228 }
229
230 static void cmd_ext (ZOOM_connection *c, ZOOM_resultset *r,
231                      ZOOM_options options,
232                      const char **args)
233 {
234     ZOOM_package p[MAX_CON];
235     char ext_type_str[10];
236     
237     int i;
238
239     if (next_token_copy (args, ext_type_str, sizeof(ext_type_str)) < 0)
240         return;
241     
242     for (i = 0; i<MAX_CON; i++)
243     {
244         if (c[i])
245         {
246             p[i] = ZOOM_connection_package (c[i], 0);
247             ZOOM_package_send(p[i], ext_type_str);
248         }
249         else
250             p[i] = 0;
251     }
252
253     while (ZOOM_event (MAX_CON, c))
254         ;
255
256     for (i = 0; i<MAX_CON; i++)
257     {
258         int error;
259         const char *errmsg, *addinfo, *dset;
260         /* display errors if any */
261         if (!p[i])
262             continue;
263         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
264             printf ("%s error: %s (%s:%d) %s\n",
265                      ZOOM_connection_option_get(c[i], "host"), errmsg,
266                      dset, error, addinfo);
267         else if (p[i])
268         {
269             printf ("ok\n");
270         }
271         ZOOM_package_destroy (p[i]);
272     }
273 }
274
275 static void cmd_debug (ZOOM_connection *c, ZOOM_resultset *r,
276                        ZOOM_options options,
277                        const char **args)
278 {
279     yaz_log_init_level(LOG_ALL);
280 }
281
282 static void cmd_search (ZOOM_connection *c, ZOOM_resultset *r,
283                         ZOOM_options options,
284                         const char **args)
285 {
286     ZOOM_query s;
287     const char *query_str = *args;
288     int i;
289     
290     s = ZOOM_query_create ();
291     while (*query_str == ' ')
292         query_str++;
293     if (memcmp(query_str, "cql:", 4) == 0)
294     {
295         ZOOM_query_cql (s, query_str + 4);
296     }
297     else if (ZOOM_query_prefix (s, query_str))
298     {
299         printf ("Bad PQF: %s\n", query_str);
300         return;
301     }
302     for (i = 0; i<MAX_CON; i++)
303     {
304         if (c[i])
305         {
306             ZOOM_resultset_destroy (r[i]);
307             r[i] = 0;
308         }
309         if (c[i])
310             r[i] = ZOOM_connection_search (c[i], s);
311     }
312
313     while (ZOOM_event (MAX_CON, c))
314         ;
315
316     for (i = 0; i<MAX_CON; i++)
317     {
318         int error;
319         const char *errmsg, *addinfo, *dset;
320         /* display errors if any */
321         if (!c[i])
322             continue;
323         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
324             printf ("%s error: %s (%s:%d) %s\n",
325                     ZOOM_connection_option_get(c[i], "host"), errmsg,
326                     dset, error, addinfo);
327         else if (r[i])
328         {
329             /* OK, no major errors. Look at the result count */
330             int start = ZOOM_options_get_int (options, "start", 0);
331             int count = ZOOM_options_get_int (options, "count", 0);
332
333             printf ("%s: %d hits\n", ZOOM_connection_option_get(c[i], "host"),
334                     ZOOM_resultset_size(r[i]));
335             /* and display */
336             display_records (c[i], r[i], start, count);
337         }
338     }
339     ZOOM_query_destroy (s);
340 }
341
342 static void cmd_scan (ZOOM_connection *c, ZOOM_resultset *r,
343                       ZOOM_options options,
344                       const char **args)
345 {
346     const char *start_term = *args;
347     int i;
348     ZOOM_scanset s[MAX_CON];
349     
350     while (*start_term == ' ')
351         start_term++;
352
353     for (i = 0; i<MAX_CON; i++)
354     {
355         if (c[i])
356             s[i] = ZOOM_connection_scan(c[i], start_term);
357         else
358             s[i] = 0;
359     }
360     while (ZOOM_event(MAX_CON, c))
361         ;
362     for (i = 0; i<MAX_CON; i++)
363     {
364         if (s[i]) {
365             size_t p, sz = ZOOM_scanset_size(s[i]);
366             for (p = 0; p < sz; p++)
367             {
368                 int occ = 0;
369                 int len = 0;
370                 const char *term = ZOOM_scanset_display_term(s[i], p,
371                                 &occ, &len);
372                 fwrite(term, 1, len, stdout);
373                 printf (" %d\n", occ);
374             }            
375             ZOOM_scanset_destroy(s[i]);
376         }
377     }
378 }
379
380 static void cmd_sort (ZOOM_connection *c, ZOOM_resultset *r,
381                       ZOOM_options options,
382                       const char **args)
383 {
384     const char *sort_spec = *args;
385     int i;
386     
387     while (*sort_spec == ' ')
388         sort_spec++;
389     
390     for (i = 0; i<MAX_CON; i++)
391     {
392         if (r[i])
393             ZOOM_resultset_sort(r[i], "yaz", sort_spec);
394     }
395     while (ZOOM_event(MAX_CON, c))
396         ;
397 }
398
399 static void cmd_help (ZOOM_connection *c, ZOOM_resultset *r,
400                       ZOOM_options options,
401                       const char **args)
402 {
403     printf ("connect <zurl>\n");
404     printf ("search <pqf>\n");
405     printf ("show [<start> [<count>]\n");
406     printf ("scan <term>\n");
407     printf ("quit\n");
408     printf ("close <zurl>\n");
409     printf ("set <option> [<value>]\n");
410     printf ("get <option>\n");
411     printf ("\n");
412     printf ("options:\n");
413     printf (" start\n");
414     printf (" count\n");
415     printf (" databaseName\n");
416     printf (" preferredRecordSyntax\n");
417     printf (" proxy\n");
418     printf (" elementSetName\n");
419     printf (" maximumRecordSize\n");
420     printf (" preferredRecordSize\n");
421     printf (" async\n");
422     printf (" piggyback\n");
423     printf (" group\n");
424     printf (" user\n");
425     printf (" password\n");
426     printf (" implementationName\n");
427     printf (" charset\n");
428     printf (" lang\n");
429 }
430
431 static void cmd_connect (ZOOM_connection *c, ZOOM_resultset *r,
432                          ZOOM_options options,
433                          const char **args)
434 {
435     int error;
436     const char *errmsg, *addinfo, *dset;
437     char host[60];
438     int j, i;
439     if (next_token_copy (args, host, sizeof(host)) < 0)
440     {
441         printf ("missing host after connect\n");
442         return ;
443     }
444     for (j = -1, i = 0; i<MAX_CON; i++)
445     {
446         const char *h;
447         if (c[i] && (h = ZOOM_connection_option_get(c[i], "host")) &&
448             !strcmp (h, host))
449         {
450             ZOOM_connection_destroy (c[i]);
451             break;
452         }
453         else if (c[i] == 0 && j == -1)
454             j = i;
455     }
456     if (i == MAX_CON)  /* no match .. */
457     {
458         if (j == -1)
459         {
460             printf ("no more connection available\n");
461             return;
462         }
463         i = j;   /* OK, use this one is available */
464     }
465     c[i] = ZOOM_connection_create (options);
466     ZOOM_connection_connect (c[i], host, 0);
467         
468     if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
469        printf ("%s error: %s (%s:%d) %s\n",
470             ZOOM_connection_option_get(c[i], "host"), errmsg,
471             dset, error, addinfo);
472 }
473
474 static int cmd_parse (ZOOM_connection *c, ZOOM_resultset *r,
475                       ZOOM_options options, 
476                       const char **buf)
477 {
478     int cmd_len;
479     const char *cmd_str;
480
481     cmd_len = next_token (buf, &cmd_str);
482     if (cmd_len < 0)
483         return 1;
484     if (is_command ("quit", cmd_str, cmd_len))
485         return 0;
486     else if (is_command ("set", cmd_str, cmd_len))
487         cmd_set (c, r, options, buf);
488     else if (is_command ("get", cmd_str, cmd_len))
489         cmd_get (c, r, options, buf);
490     else if (is_command ("connect", cmd_str, cmd_len))
491         cmd_connect (c, r, options, buf);
492     else if (is_command ("open", cmd_str, cmd_len))
493         cmd_connect (c, r, options, buf);
494     else if (is_command ("search", cmd_str, cmd_len))
495         cmd_search (c, r, options, buf);
496     else if (is_command ("find", cmd_str, cmd_len))
497         cmd_search (c, r, options, buf);
498     else if (is_command ("show", cmd_str, cmd_len))
499         cmd_show (c, r, options, buf);
500     else if (is_command ("close", cmd_str, cmd_len))
501         cmd_close (c, r, options, buf);
502     else if (is_command ("help", cmd_str, cmd_len))
503         cmd_help(c, r, options, buf);
504     else if (is_command ("ext", cmd_str, cmd_len))
505         cmd_ext(c, r, options, buf);
506     else if (is_command ("debug", cmd_str, cmd_len))
507         cmd_debug(c, r, options, buf);
508     else if (is_command ("scan", cmd_str, cmd_len))
509         cmd_scan(c, r, options, buf);
510     else if (is_command ("sort", cmd_str, cmd_len))
511         cmd_sort(c, r, options, buf);
512     else
513         printf ("unknown command %.*s\n", cmd_len, cmd_str);
514     return 2;
515 }
516
517 void shell(ZOOM_connection *c, ZOOM_resultset *r,
518            ZOOM_options options)
519 {
520     while (1)
521     {
522         char buf[1000];
523         char *cp;
524         const char *bp = buf;
525 #if HAVE_READLINE_READLINE_H
526         char* line_in;
527         line_in=readline("ZOOM>");
528         if (!line_in)
529             break;
530 #if HAVE_READLINE_HISTORY_H
531         if (*line_in)
532             add_history(line_in);
533 #endif
534         if(strlen(line_in) > 999) {
535             printf("Input line too long\n");
536             break;
537         };
538         strcpy(buf,line_in);
539         free (line_in);
540 #else    
541         printf ("ZOOM>"); fflush (stdout);
542         if (!fgets (buf, 999, stdin))
543             break;
544 #endif 
545         if ((cp = strchr(buf, '\n')))
546             *cp = '\0';
547         if (!cmd_parse (c, r, options, &bp))
548             break;
549     }
550 }
551
552 int main (int argc, char **argv)
553 {
554     ZOOM_options options = ZOOM_options_create();
555     int i, res;
556     ZOOM_connection z39_con[MAX_CON];
557     ZOOM_resultset  z39_res[MAX_CON];
558
559     if (0)
560     {
561         COMSTACK cs = 0;
562         cs_get_ssl(cs);
563         cs_create_host(0, 0, 0);
564     }
565     nmem_init();
566     for (i = 0; i<MAX_CON; i++)
567     {
568         z39_con[i] = 0;
569         z39_res[i] = 0;
570     }
571
572     for (i = 0; i<MAX_CON; i++)
573         z39_con[i] = 0;
574
575     res = 1;
576     for (i = 1; i<argc; i++)
577     {
578         const char *bp = argv[i];
579         res = cmd_parse(z39_con, z39_res, options, &bp);
580         if (res == 0)  /* received quit */
581             break;
582     }
583     if (res)  /* do cmdline shell only if not quitting */
584         shell(z39_con, z39_res, options);
585     ZOOM_options_destroy(options);
586
587     for (i = 0; i<MAX_CON; i++)
588     {
589         ZOOM_connection_destroy(z39_con[i]);
590         ZOOM_resultset_destroy(z39_res[i]);
591     }
592     nmem_exit();
593     exit (0);
594 }