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