Added simple display of records.
[egate.git] / kernel / urp.c
1 /* Gateway kernel
2  * Europagate, 1995
3  *
4  * $Log: urp.c,v $
5  * Revision 1.6  1995/02/17 14:41:14  quinn
6  * Added simple display of records.
7  *
8  * Revision 1.5  1995/02/17  14:22:13  adam
9  * First steps of CCL show command. Not finished yet.
10  *
11  * Revision 1.4  1995/02/17  09:08:36  adam
12  * Reply with subject. CCL base command implemented.
13  *
14  * Revision 1.3  1995/02/16  18:35:09  adam
15  * First use of Zdist library. Search requests are supported.
16  * Present requests are not supported yet.
17  *
18  * Revision 1.2  1995/02/16  13:21:00  adam
19  * Organization of resource files for targets and conversion
20  * language implemented.
21  *
22  * Revision 1.1  1995/02/15  17:45:30  adam
23  * First version of email gateway kernel. Email requests are read
24  * from stdin. The output is transferred to an MTA if 'From' is
25  * found in the header - or stdout if absent. No Z39.50 client is used.
26  *
27  */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include "kernel.h"
37 #include <iso2709.h>
38
39 #define LINE_MAX 256
40
41 static int reopen_target (void)
42 {
43     const char *v;
44     if (info.zass)
45         gw_log (GW_LOG_WARN, "urp", "Zass free...");
46     info.zass = zass_open (info.hostname, info.port);
47     if (!info.zass)
48     {
49         fprintf (reply_fd, "%s %s:%d\n", 
50                  gw_res_get (info.kernel_res, "gw.err.connect",
51                              "Cannot connect to target"),
52                  info.hostname, info.port);
53         return -1;
54     }
55     v = gw_res_get (info.kernel_res, "gw.description", NULL);
56     if (v)
57         fprintf (reply_fd, "%s\n", v);
58     fprintf (reply_fd, "%s %s:%d\n %s\n",
59              gw_res_get (info.kernel_res, "gw.msg.databases",
60                          "Available databases on"),
61              info.hostname, info.port, info.databases);
62     return 0;
63 }
64
65 static char line_buf[LINE_MAX+1];
66
67 static struct command_word {
68     char *default_value;
69     char *resource_suffix;
70 } command_tab [] = 
71 {
72 {   "find", "find"},
73 {   "show", "show"},
74 {   "base", "base" },
75 {   "help", "help" },
76 {   "info", "info" },
77 {   "continue", "continue" },
78 {   "status", "status" },
79 {   "cancel", "cancel" },
80 {   "target", "target" },
81 {   NULL, NULL }
82 };
83
84 static int command_search (struct command_word *tab, struct ccl_token *cmd,
85 const char *resource_prefix)
86 {
87     int no = 1;
88
89     assert (resource_prefix);
90     assert (tab);
91     assert (cmd);
92     while (tab->default_value)
93     {
94         char *cp, command_names[60];
95         char resource_name[60];
96         const char *v;
97
98         sprintf (resource_name, "%s%s", resource_prefix,
99                  tab->resource_suffix);
100         v = gw_res_get (info.kernel_res, resource_name, tab->default_value);
101         assert (v);
102         strcpy (command_names, v);
103         cp = command_names;
104         while (1)
105         {
106             char *split;
107
108             if ((split = strchr (cp, ' ')))
109                 *split = '\0';
110             if (cmd->len == strlen(cp) &&
111                 !memcmp (cmd->name, cp, cmd->len))
112                 return no;
113             if (!split)
114                 break;
115             cp = split+1;
116         }        
117         no++;
118         tab++;
119     }
120     return 0;
121 }
122
123 static struct error_no_struct {
124     int no;
125     char *resource_name;
126 } error_ccl_tab[] = {
127 {  CCL_ERR_OK, "ok"},
128 {  CCL_ERR_TERM_EXPECTED, "term.expected" },
129 {  CCL_ERR_RP_EXPECTED, "rp.expected" },
130 {  CCL_ERR_SETNAME_EXPECTED, "setname.expected" },
131 {  CCL_ERR_OP_EXPECTED, "op.expected" },
132 {  CCL_ERR_BAD_RP, "bad.rp" },
133 {  CCL_ERR_UNKNOWN_QUAL, "unknown.qual" },
134 {  CCL_ERR_DOUBLE_QUAL, "double.qual" },
135 {  CCL_ERR_EQ_EXPECTED, "eq.expected" },
136 {  CCL_ERR_BAD_RELATION, "bad.relation" },
137 {  CCL_ERR_TRUNC_NOT_LEFT, "trunc.not.left" },
138 {  CCL_ERR_TRUNC_NOT_BOTH, "trunc.not.both" },
139 {  CCL_ERR_TRUNC_NOT_RIGHT, "trunc.not.right" },
140 {  0, NULL }
141 };
142
143 static char *error_no_search (struct error_no_struct *tab, int no)
144 {
145     struct error_no_struct *p = tab;
146     while (p->resource_name)
147     {
148         if (no == p->no)
149             return p->resource_name;
150         p++;
151     }
152     return NULL;
153 }
154
155 static int email_header (FILE *inf, char *from_str, char *subject_str)
156 {
157     *from_str = '\0';
158     *subject_str = '\0';
159     while (fgets (line_buf, LINE_MAX, inf))
160     {
161         if (line_buf[0] == '\n')
162             return 0;
163         if (strncmp (line_buf, "From ", 5) == 0)
164             sscanf (line_buf+4, "%s", from_str);
165         if (strncmp (line_buf, "Subject: ", 9) == 0 &&
166             sscanf (line_buf+9, "%s", subject_str+1) == 1)
167             strcpy (subject_str, line_buf+9);
168     }
169     return 1;
170 }
171
172 static int exec_find (struct ccl_token *list)
173 {
174     const struct zass_searchent *p;
175
176     struct ccl_rpn_node *rpn;
177     int error;
178     const char *pos;
179
180     rpn = ccl_find (info.bibset, list, &error, &pos);
181     if (!rpn)
182     {
183         const char *v = NULL, *n;
184         char name[128];
185
186         fprintf (reply_fd, "  %*s^ - ", pos - line_buf, " ");
187
188         n = error_no_search (error_ccl_tab, error);
189         if (n)
190         {
191             sprintf (name, "gw.err.%s", n);
192             v = gw_res_get (info.kernel_res, name, NULL);
193         }
194         if (!v)
195             v = ccl_err_msg (error);
196         fprintf (reply_fd, "%s\n", v);
197         return -1;
198     }
199     ccl_pr_tree (rpn, reply_fd);
200     fprintf (reply_fd, "\n");
201
202     if (!info.zass)
203         return -2;
204     p = zass_search (info.zass, rpn, "Default", info.databases);
205     if (!p)
206         return -1;
207     fprintf (reply_fd, "%d %s\n", p->num,
208              gw_res_get (info.kernel_res, "gw.msg.hits", "hit(s)"));
209     if (p->errcode != -1)
210         fprintf (reply_fd, "%s %d: %s\n",
211                  gw_res_get (info.kernel_res, "gw.msg.z39errcode",
212                              "Z39.50 error code"),
213                  p->errcode, p->errstring);
214     return 0;
215 }
216
217 static int exec_target (struct ccl_token *list)
218 {
219     int len;
220     if (list->kind == CCL_TOK_EOL)
221         return -1;
222     len = list->len;
223     memcpy (info.target, list->name, len);
224     info.target [len] = '\0';
225
226     read_kernel_res ();
227     return reopen_target ();
228 }
229
230 static int exec_base (struct ccl_token *list)
231 {
232     struct ccl_token *li = list;
233     int len = 0;
234
235     if (list->kind == CCL_TOK_EOL)
236         return -1;
237     free (info.databases);
238     while (li->kind != CCL_TOK_EOL)
239     {
240         len += li->len + 1;
241         li = li->next;
242         if (li->kind == CCL_TOK_COMMA)
243             li = li->next;
244     }
245     info.databases = malloc (len);
246     assert (info.databases);
247     len = 0;
248     li = list;
249     while (li->kind != CCL_TOK_EOL)
250     {
251         memcpy (info.databases+len, li->name, li->len);
252         len += li->len;
253         info.databases[len++] = ',';
254         li = li->next;
255         if (li->kind == CCL_TOK_COMMA)
256             li = li->next;
257     }
258     info.databases[len-1] = '\0';
259     return 0;
260 }
261
262 static int exec_show (struct ccl_token *list)
263 {
264     const struct zass_presentent *zp;
265     zass_record *pp;
266     char num_str[20];
267     int num;
268
269     if (list->kind == CCL_TOK_EOL)
270         return -1;
271     if (!info.zass)
272         return -2;
273     
274     memcpy (num_str, list->name, list->len);
275     num_str[list->len] = '\0';
276
277     num = atoi (num_str);
278     if (!num)
279         return -3;
280     gw_log (GW_LOG_DEBUG, "urp", "zass_present of %d records", num);
281     zp = zass_present(info.zass, "Default", 1, num);
282     if (zp)
283     {
284         fprintf(reply_fd, "Got %d records\n", zp->num);
285         for (pp = zp->records; pp; pp = pp->next)
286         {
287             Iso2709Rec p = iso2709_cvt(pp->record);
288             iso2709_display(p, reply_fd);
289             iso2709_rm(p);
290         }
291     }
292     return 0;
293 }
294
295 static int exec_command (const char *str)
296 {
297     struct ccl_token *cmd = ccl_tokenize (str);
298     int no;
299
300     if (cmd->kind != CCL_TOK_EOL &&
301         (no = command_search (command_tab, cmd, "ccl.command.")))
302     {
303         if (!info.zass && no != 9)
304             reopen_target ();
305         fprintf (reply_fd, "\n> %s", str);
306         switch (no)
307         {
308         case 1:
309             return exec_find (cmd->next);
310         case 2:
311             return exec_show (cmd->next);
312         case 3:
313             return exec_base (cmd->next);
314         case 9:
315             return exec_target (cmd->next);
316         default:
317             fprintf (reply_fd, "%s\n",
318                      gw_res_get (info.kernel_res, "gw.err.unimplemented",
319                                  "Not implemented yet"));
320         }
321     }
322     else
323     {
324         fprintf (reply_fd, "\n> %s", str);
325         fprintf (reply_fd, "  ^ %s\n", 
326                  gw_res_get (info.kernel_res, "gw.err.unknown.command",
327                              "unknown command"));
328     }
329     return 0;
330 }
331
332 int urp (FILE *inf)
333 {
334     char from_str[128];
335     char subject_str[128];
336     int command_no = 0;
337     char *reply_fname = NULL;
338
339     if (email_header (inf, from_str, subject_str))
340     {
341         gw_log (GW_LOG_WARN, "urp", "No message body");
342         return -1;
343     }
344     if (*from_str)
345     {
346         reply_fname = tempnam (gw_res_get (info.kernel_res,
347                                            "gw.reply.tmp.dir", NULL),
348                                gw_res_get (info.kernel_res,
349                                            "gw.reply.tmp.prefix", "gwr"));
350                                                  
351         reply_fd = fopen (reply_fname, "w");
352         if (!reply_fd)
353         {
354             gw_log (GW_LOG_FATAL, "urp", "Cannot create %s",
355                     reply_fname);
356             return -1;
357         }
358         fprintf (reply_fd, "Subject: ");
359         if (*subject_str)
360             fprintf (reply_fd, "Z39.50 Re: %s", subject_str);
361         else
362             fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
363                                                    "gw.msg.subject",
364                                                    "Your Z39.50 Query"));
365         fprintf (reply_fd, "\n");
366     }
367     else
368         gw_log (GW_LOG_WARN, "urp", "No From in email header");
369     fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res, "gw.msg.greeting",
370                                            "Email->Z39.50 gateway"));
371     while (fgets (line_buf, LINE_MAX, inf))
372     {
373         if (line_buf[0] == '\n')
374             break;
375         ccl_token_and = gw_res_get (info.kernel_res, "ccl.token.and", "and");
376         ccl_token_or = gw_res_get (info.kernel_res, "ccl.token.or", "or");
377         ccl_token_not = gw_res_get (info.kernel_res, "ccl.token.not", "not");
378         ccl_token_set = gw_res_get (info.kernel_res, "ccl.token.set", "set");       
379         if (isalpha (line_buf[0]))
380             exec_command (line_buf);
381         command_no++;
382     }
383     if (!command_no)
384         fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
385                                                "gw.err.nullbody",
386                                                "No body"));
387     if (*from_str)
388     {
389         const char *mta;
390         char cmd[256];
391         int mta_code;
392
393         assert (reply_fname);
394         fclose (reply_fd);
395         reply_fd = stdout;
396
397         mta = gw_res_get (info.kernel_res, "gw.reply.mta",
398                           "/usr/lib/sendmail");
399         sprintf (cmd, "%s %s < %s", mta, from_str, reply_fname);
400         
401         mta_code = system (cmd);
402         if (mta_code)
403             gw_log (GW_LOG_FATAL, "urp", "Reply '%s' got exit code %d",
404                     cmd, mta_code);
405         unlink (reply_fname);        
406     }
407     return 0;
408 }