FML support. Bug fixes. Profile for drewdb.
[egate.git] / kernel / urp.c
1 /* Gateway kernel
2  * Europagate, 1995
3  *
4  * $Log: urp.c,v $
5  * Revision 1.7  1995/02/20 21:16:20  adam
6  * FML support. Bug fixes. Profile for drewdb.
7  *
8  * Revision 1.6  1995/02/17  14:41:14  quinn
9  * Added simple display of records.
10  *
11  * Revision 1.5  1995/02/17  14:22:13  adam
12  * First steps of CCL show command. Not finished yet.
13  *
14  * Revision 1.4  1995/02/17  09:08:36  adam
15  * Reply with subject. CCL base command implemented.
16  *
17  * Revision 1.3  1995/02/16  18:35:09  adam
18  * First use of Zdist library. Search requests are supported.
19  * Present requests are not supported yet.
20  *
21  * Revision 1.2  1995/02/16  13:21:00  adam
22  * Organization of resource files for targets and conversion
23  * language implemented.
24  *
25  * Revision 1.1  1995/02/15  17:45:30  adam
26  * First version of email gateway kernel. Email requests are read
27  * from stdin. The output is transferred to an MTA if 'From' is
28  * found in the header - or stdout if absent. No Z39.50 client is used.
29  *
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <assert.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "kernel.h"
40
41 #define LINE_MAX 256
42
43 static int reopen_target (void)
44 {
45     const char *v;
46     if (info.zass)
47         gw_log (GW_LOG_WARN, "urp", "Zass free...");
48     info.zass = zass_open (info.hostname, info.port);
49     if (!info.zass)
50     {
51         fprintf (reply_fd, "%s %s:%d\n", 
52                  gw_res_get (info.kernel_res, "gw.err.connect",
53                              "Cannot connect to target"),
54                  info.hostname, info.port);
55         return -1;
56     }
57     v = gw_res_get (info.kernel_res, "gw.description", NULL);
58     if (v)
59         fprintf (reply_fd, "%s\n", v);
60     fprintf (reply_fd, "%s %s:%d\n",
61              gw_res_get (info.kernel_res, "gw.msg.connect",
62                          "Connection established to"),
63              info.hostname, info.port);
64     if (*info.databases)
65         fprintf (reply_fd, "%s:\n%s\n",
66                  gw_res_get (info.kernel_res, "gw.msg.databases",
67                              "Available databases"),
68                  info.databases);
69     return 0;
70 }
71
72 static char line_buf[LINE_MAX+1];
73
74 static struct command_word {
75     char *default_value;
76     char *resource_suffix;
77 } command_tab [] = 
78 {
79 {   "find", "find"},
80 {   "show", "show"},
81 {   "base", "base" },
82 {   "help", "help" },
83 {   "info", "info" },
84 {   "continue", "continue" },
85 {   "status", "status" },
86 {   "cancel", "cancel" },
87 {   "target", "target" },
88 {   NULL, NULL }
89 };
90
91 static int command_search (struct command_word *tab, struct ccl_token *cmd,
92 const char *resource_prefix)
93 {
94     int no = 1;
95
96     assert (resource_prefix);
97     assert (tab);
98     assert (cmd);
99     while (tab->default_value)
100     {
101         char *cp, command_names[60];
102         char resource_name[60];
103         const char *v;
104
105         sprintf (resource_name, "%s%s", resource_prefix,
106                  tab->resource_suffix);
107         v = gw_res_get (info.kernel_res, resource_name, tab->default_value);
108         assert (v);
109         strcpy (command_names, v);
110         cp = command_names;
111         while (1)
112         {
113             char *split;
114
115             if ((split = strchr (cp, ' ')))
116                 *split = '\0';
117             if (cmd->len == strlen(cp) &&
118                 !memcmp (cmd->name, cp, cmd->len))
119                 return no;
120             if (!split)
121                 break;
122             cp = split+1;
123         }        
124         no++;
125         tab++;
126     }
127     return 0;
128 }
129
130 static struct error_no_struct {
131     int no;
132     char *resource_name;
133 } error_ccl_tab[] = {
134 {  CCL_ERR_OK, "ok"},
135 {  CCL_ERR_TERM_EXPECTED, "term.expected" },
136 {  CCL_ERR_RP_EXPECTED, "rp.expected" },
137 {  CCL_ERR_SETNAME_EXPECTED, "setname.expected" },
138 {  CCL_ERR_OP_EXPECTED, "op.expected" },
139 {  CCL_ERR_BAD_RP, "bad.rp" },
140 {  CCL_ERR_UNKNOWN_QUAL, "unknown.qual" },
141 {  CCL_ERR_DOUBLE_QUAL, "double.qual" },
142 {  CCL_ERR_EQ_EXPECTED, "eq.expected" },
143 {  CCL_ERR_BAD_RELATION, "bad.relation" },
144 {  CCL_ERR_TRUNC_NOT_LEFT, "trunc.not.left" },
145 {  CCL_ERR_TRUNC_NOT_BOTH, "trunc.not.both" },
146 {  CCL_ERR_TRUNC_NOT_RIGHT, "trunc.not.right" },
147 {  0, NULL }
148 };
149
150 static char *error_no_search (struct error_no_struct *tab, int no)
151 {
152     struct error_no_struct *p = tab;
153     while (p->resource_name)
154     {
155         if (no == p->no)
156             return p->resource_name;
157         p++;
158     }
159     return NULL;
160 }
161
162 static int email_header (FILE *inf, char *from_str, char *subject_str)
163 {
164     *from_str = '\0';
165     *subject_str = '\0';
166     while (fgets (line_buf, LINE_MAX, inf))
167     {
168         if (line_buf[0] == '\n')
169             return 0;
170         if (strncmp (line_buf, "From ", 5) == 0)
171             sscanf (line_buf+4, "%s", from_str);
172         if (strncmp (line_buf, "Subject: ", 9) == 0 &&
173             sscanf (line_buf+9, "%s", subject_str+1) == 1)
174             strcpy (subject_str, line_buf+9);
175     }
176     return 1;
177 }
178
179 static int exec_find (struct ccl_token *list)
180 {
181     const struct zass_searchent *p;
182
183     struct ccl_rpn_node *rpn;
184     int error;
185     const char *pos;
186
187     rpn = ccl_find (info.bibset, list, &error, &pos);
188     if (!rpn)
189     {
190         const char *v = NULL, *n;
191         char name[128];
192
193         fprintf (reply_fd, "  %*s^ - ", pos - line_buf, " ");
194
195         n = error_no_search (error_ccl_tab, error);
196         if (n)
197         {
198             sprintf (name, "gw.err.%s", n);
199             v = gw_res_get (info.kernel_res, name, NULL);
200         }
201         if (!v)
202             v = ccl_err_msg (error);
203         fprintf (reply_fd, "%s\n", v);
204         return -1;
205     }
206     ccl_pr_tree (rpn, reply_fd);
207     fprintf (reply_fd, "\n");
208
209     if (!info.zass)
210         return -2;
211     if (!*info.databases)
212     {
213         fprintf (reply_fd, "%s\n",
214                  gw_res_get (info.kernel_res, "gw.err.no.database",
215                              "You must select database"));
216         return -3;
217     }
218     gw_log (GW_LOG_DEBUG, "urp", "Searching in database %s",
219             info.databases);
220     p = zass_search (info.zass, rpn, "Default", info.databases);
221     if (!p)
222         return -1;
223     if (p->errcode != -1)
224         fprintf (reply_fd, "%s %d: %s\n",
225                  gw_res_get (info.kernel_res, "gw.msg.z39errcode",
226                              "Z39.50 error code"),
227                  p->errcode, p->errstring);
228     else
229         fprintf (reply_fd, "%d %s\n", p->num,
230                  gw_res_get (info.kernel_res, "gw.msg.hits", "hit(s)"));
231     return 0;
232 }
233
234 static int exec_target (struct ccl_token *list)
235 {
236     int len;
237     if (list->kind == CCL_TOK_EOL)
238         return -1;
239     len = list->len;
240     memcpy (info.target, list->name, len);
241     info.target [len] = '\0';
242
243     read_kernel_res ();
244     return reopen_target ();
245 }
246
247 static int exec_base (struct ccl_token *list)
248 {
249     struct ccl_token *li = list;
250     int len = 0;
251
252     if (list->kind == CCL_TOK_EOL)
253         return -1;
254     free (info.databases);
255     while (li->kind != CCL_TOK_EOL)
256     {
257         len += li->len + 1;
258         li = li->next;
259         if (li->kind == CCL_TOK_COMMA)
260             li = li->next;
261     }
262     info.databases = malloc (len);
263     assert (info.databases);
264     len = 0;
265     li = list;
266     while (li->kind != CCL_TOK_EOL)
267     {
268         memcpy (info.databases+len, li->name, li->len);
269         len += li->len;
270         info.databases[len++] = ',';
271         li = li->next;
272         if (li->kind == CCL_TOK_COMMA)
273             li = li->next;
274     }
275     info.databases[len-1] = '\0';
276     return 0;
277 }
278
279 struct command_word show_tab [] = 
280 {
281 {   "f", "format"},
282 {   "p", "position"},
283 {   NULL, NULL }
284 };
285
286 static int exec_show (struct ccl_token *list)
287 {
288     const struct zass_presentent *zp;
289     char num_str[20];
290     struct ccl_token *set_token = NULL;
291     struct ccl_token *format_token = NULL;
292     struct ccl_token *li = list;
293
294     if (list->kind == CCL_TOK_EOL)
295         return -1;
296     if (!info.zass)
297         return -2;
298     while (li->kind != CCL_TOK_EOL)
299     {
300         int modifier_no = 0;
301         if (li->next->kind == CCL_TOK_EQ)
302         {
303             if (li->kind == CCL_TOK_SET)    /* set = <name> ? */
304             {
305                 li = li->next->next;
306                 set_token = li;
307             }
308             else 
309             {
310                 modifier_no = command_search (show_tab, li, "ccl.token.");
311                 if (!modifier_no)
312                 {
313                     fprintf (reply_fd, "Unknown modifier in show\n");
314                     return -1;
315                 }
316                 li = li->next->next;
317                 if (modifier_no == 1)       /* f = <name> ? */
318                     format_token = li;
319                 else if (modifier_no == 2)  /* p = <name> ? */
320                 {
321                     if (li->kind != CCL_TOK_EOL   /* p = <name> - <name> ? */
322                         && li->next->kind == CCL_TOK_MINUS
323                         && li->next->next != CCL_TOK_EOL)
324                         li = li->next->next;
325                 }
326             }
327             li = li->next;
328         }
329         else
330             li = li->next;
331     }
332     if (set_token)
333         gw_log (GW_LOG_DEBUG, "urp", "Got set=%.*s", set_token->len,
334                 set_token->name);
335     if (format_token)
336         gw_log (GW_LOG_DEBUG, "urp", "Got format=%.*s", format_token->len,
337                 format_token->name);
338
339     li = list;
340     while (li->kind != CCL_TOK_EOL)
341     {
342         int modifier_no = 0;
343         int offset = 0;
344         int number = 0;
345         int len;
346         if (li->next->kind == CCL_TOK_EQ && li->kind != CCL_TOK_SET)
347         {
348             modifier_no = command_search (show_tab, li, "ccl.token.");
349             li = li->next->next;
350             if (modifier_no == 2)  /* p = <name> ? */
351             {
352                 if (li->kind != CCL_TOK_EOL   /* p = <name> - <name> ? */
353                     && li->next->kind == CCL_TOK_MINUS
354                     && li->next->next != CCL_TOK_EOL)
355                 {
356                     len = li->len;
357                     memcpy (num_str, li->name, len);
358                     num_str [len] = '\0';
359                     offset = atoi (num_str);
360                     li = li->next->next;
361
362                     len = li->len;
363                     memcpy (num_str, li->name, len);
364                     num_str [len] = '\0';
365                     number = atoi (num_str) - offset + 1;
366                 }
367                 else
368                 {
369                     len = li->len;
370                     memcpy (num_str, li->name, len);
371                     num_str [len] = '\0';
372                     offset = atoi (num_str);
373                     number = 1;
374                 }
375             }
376             li = li->next;
377         }
378         else
379         {
380             len = li->len;
381             memcpy (num_str, li->name, len);
382             num_str[len] = '\0';
383             number = atoi (num_str);
384             offset = 1;
385             li = li->next;
386         }
387         if (offset > 0 && number > 0)
388         {
389             if (set_token)
390             {
391                 len = set_token->len;
392                 memcpy (num_str, set_token->name, len);
393                 num_str[len] = '\0';
394             }
395             else
396                 strcpy (num_str, "Default");
397             gw_log (GW_LOG_DEBUG, "urp", "zass_present of %d records from"
398                     " offset %d in set %s", number, offset, num_str);
399             zp = zass_present(info.zass, num_str, offset, number);
400             if (zp && zp->presentstatus == ZASS_PRES_SUCCESS)
401             {
402                 int i;
403                 zass_record *pp;
404                 
405                 fprintf (reply_fd, gw_res_get (info.kernel_res,
406                                                "gw.msg.records",
407                                                "Got %d records"),
408                          zp->num);
409                 fprintf (reply_fd, "\n");
410                 for (i = 1, pp = zp->records; pp; pp = pp->next, i++)
411                 {
412                     Iso2709Rec rec;
413 #if USE_FML
414                     const char *arg_ar[5];
415 #endif
416
417                     rec = iso2709_cvt (pp->record);
418                     fprintf (reply_fd, "--- %d/%d ---\n", i, zp->num);
419 #if USE_FML
420                     if (format_token)
421                     {
422                         len = format_token->len;
423                         memcpy (num_str, format_token->name, len);
424                         num_str[len] = '\0';
425                     }
426                     if (format_token && 
427                         (!strcmp (num_str, "0") || !strcmp (num_str, "1")))
428                     {
429                         arg_ar[0] = "\\f";
430                         arg_ar[1] = num_str;
431                         arg_ar[2] = " \\list";
432                         arg_ar[3] = marc_to_str (info.fml, rec);
433                         arg_ar[4] = NULL;
434                         fml_exec_call_argv (info.fml, arg_ar);
435                     }
436                     else
437                         iso2709_display (rec, reply_fd);
438 #else
439                     iso2709_display (rec, reply_fd);
440 #endif
441                     iso2709_rm (rec);
442                 }
443             }
444         }
445     }
446 #if 0
447     len = list->len;
448     memcpy (num_str, list->name, len);
449     num_str[len] = '\0';
450
451     num = atoi (num_str);
452     if (!num)
453         return -3;
454     gw_log (GW_LOG_DEBUG, "urp", "zass_present of %d records", num);
455     zp = zass_present(info.zass, "Default", 1, num);
456     if (zp)
457     {
458         int i;
459         zass_record *pp;
460
461         fprintf (reply_fd, gw_res_get (info.kernel_res,
462                                        "gw.msg.records", "Got %d records"),
463                  zp->num);
464         fprintf (reply_fd, "\n");
465         for (i = 1, pp = zp->records; pp; pp = pp->next, i++)
466         {
467 #if USE_FML
468             const char *arg_ar[3];
469 #endif
470             Iso2709Rec rec = iso2709_cvt (pp->record);
471
472             fprintf (reply_fd, "--- %d/%d ---\n", i, zp->num);
473 #if USE_FML
474             arg_ar[0] = "\\f0 \\list";
475             arg_ar[1] = marc_to_str (info.fml, rec);
476             arg_ar[2] = NULL;
477             fml_exec_call_argv (info.fml, arg_ar);
478 #else
479             iso2709_display (rec, reply_fd);
480 #endif
481             iso2709_rm (rec);
482         }
483     }
484 #endif
485     return 0;
486 }
487
488 static int exec_command (const char *str)
489 {
490     struct ccl_token *cmd = ccl_tokenize (str);
491     int no;
492
493     if (cmd->kind != CCL_TOK_EOL &&
494         (no = command_search (command_tab, cmd, "ccl.command.")))
495     {
496         if (!info.zass && no != 9)
497             reopen_target ();
498         fprintf (reply_fd, "\n> %s", str);
499         switch (no)
500         {
501         case 1:
502             return exec_find (cmd->next);
503         case 2:
504             return exec_show (cmd->next);
505         case 3:
506             return exec_base (cmd->next);
507         case 9:
508             return exec_target (cmd->next);
509         default:
510             fprintf (reply_fd, "%s\n",
511                      gw_res_get (info.kernel_res, "gw.err.unimplemented",
512                                  "Not implemented yet"));
513         }
514     }
515     else
516     {
517         fprintf (reply_fd, "\n> %s", str);
518         fprintf (reply_fd, "  ^ %s\n", 
519                  gw_res_get (info.kernel_res, "gw.err.unknown.command",
520                              "unknown command"));
521     }
522     return 0;
523 }
524
525 int urp (FILE *inf)
526 {
527     char from_str[128];
528     char subject_str[128];
529     int command_no = 0;
530     char *reply_fname = NULL;
531
532     if (email_header (inf, from_str, subject_str))
533     {
534         gw_log (GW_LOG_WARN, "urp", "No message body");
535         return -1;
536     }
537     if (*from_str)
538     {
539         reply_fname = tempnam (gw_res_get (info.kernel_res,
540                                            "gw.reply.tmp.dir", NULL),
541                                gw_res_get (info.kernel_res,
542                                            "gw.reply.tmp.prefix", "gwr"));
543                                                  
544         reply_fd = fopen (reply_fname, "w");
545         if (!reply_fd)
546         {
547             gw_log (GW_LOG_FATAL, "urp", "Cannot create %s",
548                     reply_fname);
549             return -1;
550         }
551         fprintf (reply_fd, "Subject: ");
552         if (*subject_str)
553             fprintf (reply_fd, "Z39.50 Re: %s", subject_str);
554         else
555             fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
556                                                    "gw.msg.subject",
557                                                    "Your Query"));
558         fprintf (reply_fd, "\n");
559     }
560     else
561         gw_log (GW_LOG_WARN, "urp", "No From in email header");
562     fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res, "gw.msg.greeting",
563                                            "Email->Z39.50 gateway"));
564     while (fgets (line_buf, LINE_MAX, inf))
565     {
566         if (line_buf[0] == '\n')
567             break;
568         ccl_token_and = gw_res_get (info.kernel_res, "ccl.token.and", "and");
569         ccl_token_or = gw_res_get (info.kernel_res, "ccl.token.or", "or");
570         ccl_token_not = gw_res_get (info.kernel_res, "ccl.token.not", "not");
571         ccl_token_set = gw_res_get (info.kernel_res, "ccl.token.set", "set");
572         if (isalpha (line_buf[0]))
573             exec_command (line_buf);
574         command_no++;
575     }
576     if (!command_no)
577         fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
578                                                "gw.err.nullbody",
579                                                "No body"));
580     if (*from_str)
581     {
582         const char *mta;
583         char cmd[256];
584         int mta_code;
585
586         assert (reply_fname);
587         fclose (reply_fd);
588         reply_fd = stdout;
589
590         mta = gw_res_get (info.kernel_res, "gw.reply.mta",
591                           "/usr/lib/sendmail");
592         sprintf (cmd, "%s %s < %s", mta, from_str, reply_fname);
593         
594         mta_code = system (cmd);
595         if (mta_code)
596             gw_log (GW_LOG_FATAL, "urp", "Reply '%s' got exit code %d",
597                     cmd, mta_code);
598         unlink (reply_fname);        
599     }
600     return 0;
601 }