5 * Revision 1.11 1995/02/22 15:22:33 adam
6 * Much more checking of run-time state. Show command never retrieves
7 * more records than indicated by the previous search request. Help
8 * command available. The maximum number of records retrieved can be
11 * Revision 1.10 1995/02/22 08:51:35 adam
12 * Output function can be customized in fml, which is used to print
13 * the reply to reply_fd.
15 * Revision 1.9 1995/02/21 17:46:21 adam
18 * Revision 1.8 1995/02/21 12:12:00 adam
19 * Diagnostic record with error info. observed.
21 * Revision 1.7 1995/02/20 21:16:20 adam
22 * FML support. Bug fixes. Profile for drewdb.
24 * Revision 1.6 1995/02/17 14:41:14 quinn
25 * Added simple display of records.
27 * Revision 1.5 1995/02/17 14:22:13 adam
28 * First steps of CCL show command. Not finished yet.
30 * Revision 1.4 1995/02/17 09:08:36 adam
31 * Reply with subject. CCL base command implemented.
33 * Revision 1.3 1995/02/16 18:35:09 adam
34 * First use of Zdist library. Search requests are supported.
35 * Present requests are not supported yet.
37 * Revision 1.2 1995/02/16 13:21:00 adam
38 * Organization of resource files for targets and conversion
39 * language implemented.
41 * Revision 1.1 1995/02/15 17:45:30 adam
42 * First version of email gateway kernel. Email requests are read
43 * from stdin. The output is transferred to an MTA if 'From' is
44 * found in the header - or stdout if absent. No Z39.50 client is used.
59 static void put_esc_str (const char *s)
64 if (*s == '\\' && s[1])
69 fputc ('\n', reply_fd);
72 fputc ('\t', reply_fd);
82 if (*s != ' ' || !escape_flag)
90 static int reopen_target (void)
94 gw_log (GW_LOG_WARN, "urp", "Zass free...");
95 info.zass = zass_open (info.hostname, info.port);
98 fprintf (reply_fd, "%s %s:%d\n",
99 gw_res_get (info.kernel_res, "gw.err.connect",
100 "Cannot connect to target"),
101 info.hostname, info.port);
104 v = gw_res_get (info.kernel_res, "gw.description", NULL);
106 fprintf (reply_fd, "%s\n", v);
107 fprintf (reply_fd, "%s %s:%d\n",
108 gw_res_get (info.kernel_res, "gw.msg.connect",
109 "Connection established to"),
110 info.hostname, info.port);
112 fprintf (reply_fd, "%s:\n%s\n",
113 gw_res_get (info.kernel_res, "gw.msg.databases",
114 "Available databases"),
119 static char line_buf[LINE_MAX+1];
121 static struct command_word {
123 char *resource_suffix;
131 { "continue", "continue" },
132 { "status", "status" },
133 { "cancel", "cancel" },
134 { "target", "target" },
138 static int command_search (struct command_word *tab, struct ccl_token *cmd,
139 const char *resource_prefix)
143 assert (resource_prefix);
146 while (tab->default_value)
148 char *cp, command_names[60];
149 char resource_name[60];
152 sprintf (resource_name, "%s%s", resource_prefix,
153 tab->resource_suffix);
154 v = gw_res_get (info.kernel_res, resource_name, tab->default_value);
156 strcpy (command_names, v);
162 if ((split = strchr (cp, ' ')))
164 if (cmd->len == strlen(cp) &&
165 !memcmp (cmd->name, cp, cmd->len))
177 static struct error_no_struct {
180 } error_ccl_tab[] = {
182 { CCL_ERR_TERM_EXPECTED, "term.expected" },
183 { CCL_ERR_RP_EXPECTED, "rp.expected" },
184 { CCL_ERR_SETNAME_EXPECTED, "setname.expected" },
185 { CCL_ERR_OP_EXPECTED, "op.expected" },
186 { CCL_ERR_BAD_RP, "bad.rp" },
187 { CCL_ERR_UNKNOWN_QUAL, "unknown.qual" },
188 { CCL_ERR_DOUBLE_QUAL, "double.qual" },
189 { CCL_ERR_EQ_EXPECTED, "eq.expected" },
190 { CCL_ERR_BAD_RELATION, "bad.relation" },
191 { CCL_ERR_TRUNC_NOT_LEFT, "trunc.not.left" },
192 { CCL_ERR_TRUNC_NOT_BOTH, "trunc.not.both" },
193 { CCL_ERR_TRUNC_NOT_RIGHT, "trunc.not.right" },
197 static char *error_no_search (struct error_no_struct *tab, int no)
199 struct error_no_struct *p = tab;
200 while (p->resource_name)
203 return p->resource_name;
209 static int email_header (FILE *inf, char *from_str, char *subject_str)
213 while (fgets (line_buf, LINE_MAX, inf))
215 if (line_buf[0] == '\n')
217 if (strncmp (line_buf, "From ", 5) == 0)
218 sscanf (line_buf+4, "%s", from_str);
219 if (strncmp (line_buf, "Subject: ", 9) == 0 &&
220 sscanf (line_buf+9, "%s", subject_str+1) == 1)
221 strcpy (subject_str, line_buf+9);
226 static void help_general (void)
228 put_esc_str (gw_res_get (info.kernel_res, "gw.help.general",
229 "Commands available in this service:\n"));
232 static int exec_help (struct ccl_token *list)
237 put_esc_str (gw_res_get (info.kernel_res, "gw.help.target",
238 "target <name> - selects a given target\n"));
240 put_esc_str (gw_res_get (info.kernel_res, "gw.help.base",
241 "base <base>.. - selects databases\n"));
243 put_esc_str (gw_res_get (info.kernel_res, "gw.help.find",
244 "find <query> - performs a search request\n"));
246 put_esc_str (gw_res_get (info.kernel_res, "gw.help.show",
247 "show <spec> - retrieves and displays "
249 put_esc_str (gw_res_get (info.kernel_res, "gw.help.help",
250 "help - displays help\n"));
255 static int exec_find (struct ccl_token *list)
257 const struct zass_searchent *p;
258 struct gw_user_set *us;
260 struct ccl_rpn_node *rpn;
264 us = user_set_add ("Default", -1);
265 rpn = ccl_find (info.bibset, list, &error, &pos);
268 const char *v = NULL, *n;
271 fprintf (reply_fd, " %*s^ - ", pos - line_buf, " ");
273 n = error_no_search (error_ccl_tab, error);
276 sprintf (name, "gw.err.%s", n);
277 v = gw_res_get (info.kernel_res, name, NULL);
280 v = ccl_err_msg (error);
281 fprintf (reply_fd, "%s\n", v);
284 ccl_pr_tree (rpn, reply_fd);
285 fprintf (reply_fd, "\n");
287 if (!*info.databases)
289 fprintf (reply_fd, "%s\n",
290 gw_res_get (info.kernel_res, "gw.err.no.database",
291 "You must select database"));
294 gw_log (GW_LOG_DEBUG, "urp", "Searching in database %s",
297 p = zass_search (info.zass, rpn, "Default", info.databases);
300 if (p->errcode != -1)
302 fprintf (reply_fd, "%s %d: %s\n",
303 gw_res_get (info.kernel_res, "gw.msg.z39errcode",
304 "Z39.50 error code"),
305 p->errcode, p->errstring);
308 fprintf (reply_fd, "%d %s\n", p->num,
309 gw_res_get (info.kernel_res, "gw.msg.hits", "hit(s)"));
314 static int exec_target (struct ccl_token *list)
317 if (list->kind == CCL_TOK_EOL)
320 memcpy (info.target, list->name, len);
321 info.target [len] = '\0';
324 return reopen_target ();
327 static int exec_base (struct ccl_token *list)
329 struct ccl_token *li = list;
333 if (list->kind == CCL_TOK_EOL)
335 free (info.databases);
336 while (li->kind != CCL_TOK_EOL)
340 if (li->kind == CCL_TOK_COMMA)
343 info.databases = malloc (len);
344 assert (info.databases);
347 while (li->kind != CCL_TOK_EOL)
349 memcpy (info.databases+len, li->name, li->len);
351 info.databases[len++] = ',';
353 if (li->kind == CCL_TOK_COMMA)
356 info.databases[len-1] = '\0';
360 struct command_word show_tab [] =
367 static void present (const char *set, int number, int offset,
368 struct ccl_token *format_token)
370 const struct zass_presentent *zp;
375 max_number = atoi (gw_res_get (info.kernel_res, "gw.max.show",
377 if (number > max_number)
379 gw_log (GW_LOG_DEBUG, "urp", "present in set %s", set);
380 gw_log (GW_LOG_DEBUG, "urp", "present of %d records from offset %d",
382 zp = zass_present(info.zass, (char *) set, offset, number);
388 fprintf (reply_fd, gw_res_get (info.kernel_res,
392 fprintf (reply_fd, "\n");
393 for (i = 0, pp = zp->records; pp; pp = pp->next, i++)
397 const char *arg_ar[5];
399 fprintf (reply_fd, "--- %d/%d ---\n",
400 i+offset, offset+zp->num-1);
401 if (!gw_res_get (info.kernel_res, "gw.ignore.which", NULL))
403 if (pp->which == ZASS_REC_DIAG)
405 fprintf (reply_fd, "Record error %d: %s\n",
406 pp->errcode, pp->errstring);
409 else if (pp->which != ZASS_REC_USMARC)
411 fprintf (reply_fd, "Unknown record kind %d\n",
416 rec = iso2709_cvt (pp->record);
422 len = format_token->len;
423 memcpy (format_str, format_token->name, len);
424 format_str[len] = '\0';
426 if (info.fml && format_token &&
427 (!strcmp (format_str, "0") || !strcmp (format_str, "1")))
430 arg_ar[1] = format_str;
431 arg_ar[2] = " \\list";
432 arg_ar[3] = marc_to_str (info.fml, rec);
434 fml_exec_call_argv (info.fml, arg_ar);
437 iso2709_display (rec, reply_fd);
439 iso2709_display (rec, reply_fd);
444 fprintf (reply_fd, "Not a MARC record\n");
449 static int exec_show (struct ccl_token *list)
452 struct ccl_token *set_token = NULL;
453 struct ccl_token *format_token = NULL;
454 struct ccl_token *li = list;
455 int no_of_present = 0;
458 while (li->kind != CCL_TOK_EOL)
461 if (li->next->kind == CCL_TOK_EQ)
463 if (li->kind == CCL_TOK_SET) /* set = <name> ? */
470 modifier_no = command_search (show_tab, li, "ccl.token.");
473 fprintf (reply_fd, "Unknown modifier in show\n");
477 if (modifier_no == 1) /* f = <name> ? */
479 else if (modifier_no == 2) /* p = <name> ? */
481 if (li->kind != CCL_TOK_EOL /* p = <name> - <name> ? */
482 && li->next->kind == CCL_TOK_MINUS
483 && li->next->next != CCL_TOK_EOL)
489 fprintf (reply_fd, "%s\n", "Missing token after '='");
498 gw_log (GW_LOG_DEBUG, "urp", "Got set=%.*s", set_token->len,
501 gw_log (GW_LOG_DEBUG, "urp", "Got format=%.*s", format_token->len,
505 while (li->kind != CCL_TOK_EOL)
511 if (li->next->kind == CCL_TOK_EQ && li->kind != CCL_TOK_SET)
513 modifier_no = command_search (show_tab, li, "ccl.token.");
515 if (modifier_no == 2) /* p = <name> ? */
517 if (li->kind != CCL_TOK_EOL /* p = <name> - <name> ? */
518 && li->next->kind == CCL_TOK_MINUS
519 && li->next->next != CCL_TOK_EOL)
522 memcpy (tmp_str, li->name, len);
523 tmp_str [len] = '\0';
524 offset = atoi (tmp_str);
528 memcpy (tmp_str, li->name, len);
529 tmp_str [len] = '\0';
530 number = atoi (tmp_str) - offset + 1;
535 memcpy (tmp_str, li->name, len);
536 tmp_str [len] = '\0';
537 offset = atoi (tmp_str);
546 memcpy (tmp_str, li->name, len);
548 number = atoi (tmp_str);
552 if (offset > 0 && number > 0)
554 struct gw_user_set *us;
558 len = set_token->len;
559 memcpy (tmp_str, set_token->name, len);
561 us = user_set_search (tmp_str);
564 us = user_set_search (NULL);
565 if (us && us->hits != -1) /* proper result-set? */
567 if (offset <= us->hits)
569 if (offset+number-1 > us->hits)
570 number = us->hits - offset+1;
571 present (us->name, offset, number, format_token);
574 else if (!no_of_present) /* display error message once! */
576 fprintf (reply_fd, "%s\n",
577 gw_res_get (info.kernel_res, "gw.err.no.set",
578 "No result-set generated"));
583 if (!no_of_present) /* no records shown so far? */
585 struct gw_user_set *us;
588 us = user_set_search (NULL);
589 if (us && us->hits != -1) /* proper result-set? */
591 default_show = atoi (gw_res_get (info.kernel_res,
592 "gw.default.show", "20"));
593 if (us->hits > default_show)
594 present (us->name, 1, default_show, format_token);
595 else if (us->hits > 0)
596 present (us->name, 1, us->hits, format_token);
598 else /* display error message */
600 fprintf (reply_fd, "%s\n",
601 gw_res_get (info.kernel_res, "gw.err.no.set",
602 "No result-set generated"));
609 static int exec_command (const char *str)
611 struct ccl_token *cmd = ccl_tokenize (str);
614 if (cmd->kind != CCL_TOK_EOL &&
615 (no = command_search (command_tab, cmd, "ccl.command.")))
617 if (!info.zass && no != 9 && no != 4)
619 fprintf (reply_fd, "\n> %s", str);
620 if (!info.zass && (no == 1 || no == 2 || no == 3))
622 fprintf (reply_fd, "%s\n",
623 gw_res_get (info.kernel_res, "gw.err.no.target",
624 "No connection established - "
631 return exec_find (cmd->next);
633 return exec_show (cmd->next);
635 return exec_base (cmd->next);
637 return exec_help (cmd->next);
639 return exec_target (cmd->next);
641 fprintf (reply_fd, "%s\n",
642 gw_res_get (info.kernel_res, "gw.err.unimplemented",
643 "Not implemented yet"));
648 fprintf (reply_fd, "\n> %s", str);
649 fprintf (reply_fd, " ^ %s\n",
650 gw_res_get (info.kernel_res, "gw.err.unknown.command",
652 "Use help to see list of commands"));
660 char subject_str[128];
662 char *reply_fname = NULL;
664 if (email_header (inf, from_str, subject_str))
666 gw_log (GW_LOG_WARN, "urp", "No message body");
671 reply_fname = tempnam (gw_res_get (info.kernel_res,
672 "gw.reply.tmp.dir", NULL),
673 gw_res_get (info.kernel_res,
674 "gw.reply.tmp.prefix", "gwr"));
676 reply_fd = fopen (reply_fname, "w");
679 gw_log (GW_LOG_FATAL, "urp", "Cannot create %s",
683 fprintf (reply_fd, "Subject: ");
685 fprintf (reply_fd, "Z39.50 Re: %s", subject_str);
687 fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
690 fprintf (reply_fd, "\n");
693 gw_log (GW_LOG_WARN, "urp", "No From in email header");
694 fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res, "gw.msg.greeting",
695 "Email->Z39.50 gateway"));
696 while (fgets (line_buf, LINE_MAX, inf))
698 if (line_buf[0] == '\n')
700 ccl_token_and = gw_res_get (info.kernel_res, "ccl.token.and", "and");
701 ccl_token_or = gw_res_get (info.kernel_res, "ccl.token.or", "or");
702 ccl_token_not = gw_res_get (info.kernel_res, "ccl.token.not", "not");
703 ccl_token_set = gw_res_get (info.kernel_res, "ccl.token.set", "set");
704 if (isalpha (line_buf[0]))
705 exec_command (line_buf);
710 fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
721 assert (reply_fname);
725 mta = gw_res_get (info.kernel_res, "gw.reply.mta",
726 "/usr/lib/sendmail");
727 sprintf (cmd, "%s %s < %s", mta, from_str, reply_fname);
729 mta_code = system (cmd);
731 gw_log (GW_LOG_FATAL, "urp", "Reply '%s' got exit code %d",
733 unlink (reply_fname);