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