ed79aca6ba5dbed5f934de95101099d2132fabf0
[egate.git] / kernel / urp.c
1 /* Gateway kernel - User Request Processor
2  * Europagate, 1995
3  *
4  * $Log: urp.c,v $
5  * Revision 1.30  1995/04/20 16:10:47  adam
6  * Modified to work with non-blocking zass-api. Not using non-blocking
7  * facility yet.
8  *
9  * Revision 1.29  1995/04/19  16:01:58  adam
10  * Some hacks to get the FIFO communication work!! Isn't reliable.
11  * Resource gw.account added - default account info.
12  *
13  * Revision 1.28  1995/04/19  13:19:09  adam
14  * New command: account - for authentication.
15  *
16  * Revision 1.27  1995/04/19  10:46:19  adam
17  * Persistency works much better now. New command: status - history-like
18  *
19  * Revision 1.26  1995/04/19  07:31:12  adam
20  * First work on Z39.50 persistence.
21  *
22  * Revision 1.25  1995/04/17  09:34:33  adam
23  * Timeout (idletime) adjustable. Minor changes in kernel.
24  *
25  * Revision 1.24  1995/03/28  11:42:35  adam
26  * First use of string-queue utility.
27  *
28  * Revision 1.23  1995/03/28  08:01:28  adam
29  * FIFO existence is used to test for a running kernel.
30  *
31  * Revision 1.22  1995/03/27  12:51:05  adam
32  * New log level in use: GW_LOG_ERRNO.
33  *
34  * Revision 1.21  1995/03/27  08:24:04  adam
35  * First use of gip interface and gw-db.
36  * First work on eti program.
37  *
38  * Revision 1.20  1995/03/03  17:19:17  adam
39  * Smarter presentation. Bug fix in email header interpretation.
40  *
41  * Revision 1.19  1995/03/02  09:32:11  adam
42  * New presentation formats. f0=full, f1=brief, f2=mid
43  *
44  * Revision 1.18  1995/03/01  14:32:26  adam
45  * Better diagnostics. Default is, that only one database selected when
46  * several are known.
47  *
48  * Revision 1.17  1995/02/28  13:16:26  adam
49  * Configurable From: added.
50  *
51  * Revision 1.16  1995/02/23  10:08:20  adam
52  * Added logging of all user commands.
53  *
54  * Revision 1.15  1995/02/23  08:32:17  adam
55  * Changed header.
56  *
57  * Revision 1.13  1995/02/22  16:54:42  adam
58  * Qualifiers of LOC target updated. More logging messages.
59  *
60  * Revision 1.12  1995/02/22  15:51:51  adam
61  * Bug fix: swap of parameter number and offset in function present.
62  *
63  * Revision 1.11  1995/02/22  15:22:33  adam
64  * Much more checking of run-time state. Show command never retrieves
65  * more records than indicated by the previous search request. Help
66  * command available. The maximum number of records retrieved can be
67  * controlled now.
68  *
69  * Revision 1.10  1995/02/22  08:51:35  adam
70  * Output function can be customized in fml, which is used to print
71  * the reply to reply_fd.
72  *
73  * Revision 1.9  1995/02/21  17:46:21  adam
74  * Minor changes.
75  *
76  * Revision 1.8  1995/02/21  12:12:00  adam
77  * Diagnostic record with error info. observed.
78  *
79  * Revision 1.7  1995/02/20  21:16:20  adam
80  * FML support. Bug fixes. Profile for drewdb.
81  *
82  * Revision 1.6  1995/02/17  14:41:14  quinn
83  * Added simple display of records.
84  *
85  * Revision 1.5  1995/02/17  14:22:13  adam
86  * First steps of CCL show command. Not finished yet.
87  *
88  * Revision 1.4  1995/02/17  09:08:36  adam
89  * Reply with subject. CCL base command implemented.
90  *
91  * Revision 1.3  1995/02/16  18:35:09  adam
92  * First use of Zdist library. Search requests are supported.
93  * Present requests are not supported yet.
94  *
95  * Revision 1.2  1995/02/16  13:21:00  adam
96  * Organization of resource files for targets and conversion
97  * language implemented.
98  *
99  * Revision 1.1  1995/02/15  17:45:30  adam
100  * First version of email gateway kernel. Email requests are read
101  * from stdin. The output is transferred to an MTA if 'From' is
102  * found in the header - or stdout if absent. No Z39.50 client is used.
103  *
104  */
105
106 #include <stdio.h>
107 #include <stdlib.h>
108 #include <assert.h>
109 #include <ctype.h>
110 #include <string.h>
111 #include <unistd.h>
112 #include <fcntl.h>
113
114 #include <ttyemit.h>
115 #include <strqueue.h>
116 #include "kernel.h"
117
118 static char line_buf[LINE_MAX+1];
119
120 static void put_esc_str (const char *s)
121 {
122     while (*s)
123         tty_emit (*s++);
124 }
125
126 int lgets (char *buf, int max, int fd)
127 {
128     int r, no = 0;
129
130     --max;
131     while (no <= max)
132     {
133         if ((r=read (fd, buf+no, 1)) != 1)
134         {
135             if (r == -1)
136                 gw_log (GW_LOG_WARN|GW_LOG_ERRNO, KERNEL_LOG, "read fail");
137             else
138                 gw_log (GW_LOG_WARN, KERNEL_LOG, "read eof");
139             buf[no] = '\0';
140             return 0;
141         }
142         if (buf[no] == 1)
143             return 0;     
144         if (buf[no++] == '\n')
145             break;
146     }
147     buf[no] = '\0';     
148     return 1;
149 }
150
151 int reopen_target (void)
152 {
153     const char *v;
154     if (info.zass)
155         gw_log (GW_LOG_WARN, KERNEL_LOG, "Zass free...");
156     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "reopen_target");
157     info.zass = zass_open (info.hostname, info.port, NULL, /* complete */
158                            *info.account ? info.account : NULL);
159     if (!info.zass)
160     {
161         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Cannot connect to target %s:%d",
162                 info.hostname, info.port);
163         fprintf (reply_fd, "%s %s:%d\n", 
164                  gw_res_get (info.kernel_res, "gw.err.connect",
165                              "Cannot connect to target"),
166                  info.hostname, info.port);
167         return -1;
168     }
169     v = gw_res_get (info.kernel_res, "gw.description", NULL);
170     if (v)
171     {
172         put_esc_str (v);
173         fprintf (reply_fd, "\n");
174     }
175     fprintf (reply_fd, "%s %s:%d\n",
176              gw_res_get (info.kernel_res, "gw.msg.connect",
177                          "Connection established to"),
178              info.hostname, info.port);
179     if (*info.databases)
180         fprintf (reply_fd, "%s:\n%s\n",
181                  gw_res_get (info.kernel_res, "gw.msg.databases",
182                              "Available databases"),
183                  info.databases);
184     if (*info.database)
185         fprintf (reply_fd, "%s:\n%s\n",
186                  gw_res_get (info.kernel_res, "gw.msg.database",
187                              "Selected databases"),
188                  info.database);
189     if (info.setno >= 0)
190         fprintf (reply_fd, "set=%d\n", info.setno);
191     else
192         fprintf (reply_fd, "set=Default\n");
193     return 0;
194 }
195
196
197 static struct command_word {
198     char *default_value;
199     char *resource_suffix;
200 } command_tab [] = 
201 {
202 {   "find", "find"},
203 {   "show", "show"},
204 {   "base", "base" },
205 {   "help", "help" },
206 {   "info", "info" },
207 {   "continue", "continue" },
208 {   "status", "status" },
209 {   "cancel", "cancel" },
210 {   "target", "target" },
211 {   "stop",   "stop" },
212 {   "account", "account" },
213 {   NULL, NULL }
214 };
215
216 static int command_search (struct command_word *tab, struct ccl_token *cmd,
217 const char *resource_prefix)
218 {
219     int no = 1;
220
221     assert (resource_prefix);
222     assert (tab);
223     assert (cmd);
224     while (tab->default_value)
225     {
226         char *cp, command_names[60];
227         char resource_name[60];
228         const char *v;
229
230         sprintf (resource_name, "%s%s", resource_prefix,
231                  tab->resource_suffix);
232         v = gw_res_get (info.kernel_res, resource_name, tab->default_value);
233         assert (v);
234         strcpy (command_names, v);
235         cp = command_names;
236         while (1)
237         {
238             char *split;
239
240             if ((split = strchr (cp, ' ')))
241                 *split = '\0';
242             if (cmd->len == strlen(cp) &&
243                 !memcmp (cmd->name, cp, cmd->len))
244                 return no;
245             if (!split)
246                 break;
247             cp = split+1;
248         }        
249         no++;
250         tab++;
251     }
252     return 0;
253 }
254
255 static struct error_no_struct {
256     int no;
257     char *resource_name;
258 } error_ccl_tab[] = {
259 {  CCL_ERR_OK, "ok"},
260 {  CCL_ERR_TERM_EXPECTED, "term.expected" },
261 {  CCL_ERR_RP_EXPECTED, "rp.expected" },
262 {  CCL_ERR_SETNAME_EXPECTED, "setname.expected" },
263 {  CCL_ERR_OP_EXPECTED, "op.expected" },
264 {  CCL_ERR_BAD_RP, "bad.rp" },
265 {  CCL_ERR_UNKNOWN_QUAL, "unknown.qual" },
266 {  CCL_ERR_DOUBLE_QUAL, "double.qual" },
267 {  CCL_ERR_EQ_EXPECTED, "eq.expected" },
268 {  CCL_ERR_BAD_RELATION, "bad.relation" },
269 {  CCL_ERR_TRUNC_NOT_LEFT, "trunc.not.left" },
270 {  CCL_ERR_TRUNC_NOT_BOTH, "trunc.not.both" },
271 {  CCL_ERR_TRUNC_NOT_RIGHT, "trunc.not.right" },
272 {  0, NULL }
273 };
274
275 static char *error_no_search (struct error_no_struct *tab, int no)
276 {
277     struct error_no_struct *p = tab;
278     while (p->resource_name)
279     {
280         if (no == p->no)
281             return p->resource_name;
282         p++;
283     }
284     return NULL;
285 }
286
287 static int email_header (struct str_queue *sq,
288                          char *from_str, char *subject_str)
289 {
290     *from_str = '\0';
291     *subject_str = '\0';    
292     while (str_queue_deq (sq, line_buf, LINE_MAX))
293     {
294         if (line_buf[0] == '\n')
295             return 0;
296         if (memcmp (line_buf, "From ", 5) == 0)
297             sscanf (line_buf+4, "%s", from_str);
298         if (memcmp (line_buf, "Subject: ", 9) == 0 &&
299             sscanf (line_buf+9, "%s", subject_str+1) == 1)
300             strcpy (subject_str, line_buf+9);
301     }
302     return 1;
303 }
304
305 static void help_general (void)
306 {
307     put_esc_str (gw_res_get (info.kernel_res, "gw.help.general",
308                              "Commands available in this service:\n"));
309 }
310
311 static int exec_help (struct ccl_token *list)
312 {
313     static char *sep = "-------------------------------\\n";
314     help_general ();
315
316 #if 1
317     put_esc_str (sep);
318     put_esc_str (gw_res_get (info.kernel_res, "gw.help.target",
319                              "target <name> - selects a given target\n"));
320
321     put_esc_str (sep);
322     put_esc_str (gw_res_get (info.kernel_res, "gw.help.base",
323                              "base <base>..  - selects databases\n"));
324
325     put_esc_str (sep);
326     put_esc_str (gw_res_get (info.kernel_res, "gw.help.find",
327                              "find <query>   - performs a search request\n"));
328
329     put_esc_str (sep);
330     put_esc_str (gw_res_get (info.kernel_res, "gw.help.show",
331                              "show <spec>    - retrieves and displays "
332                              "records\n"));
333 #endif
334     return 0;
335 }
336
337 static void display_diag_error (int code, const char *addinfo)
338 {
339     static char str[20];
340
341     sprintf (str, "gw.bib1.diag.%d", code);
342     fprintf (reply_fd, "%s %d:\n %s: '%s'\n",
343              gw_res_get (info.kernel_res, "gw.msg.z39errcode", 
344                          "Z39.50 Error"),
345              code,
346              gw_res_get (info.kernel_res, str, ""), addinfo);
347 }
348
349 static int exec_find (struct ccl_token *list, const char *search_str)
350 {
351     const struct zass_searchent *p;
352     struct gw_user_set *us;
353     char setname[32];
354
355     struct ccl_rpn_node *rpn;
356     int error;
357     const char *pos;
358
359     if (info.setno == -1)
360         strcpy (setname, "Default");
361     else
362         sprintf (setname, "%d", info.setno);
363     rpn = ccl_find (info.bibset, list, &error, &pos);
364     if (!rpn)
365     {
366         const char *v = NULL, *n;
367         char name[128];
368
369         fprintf (reply_fd, "  %*s^ - ", pos - line_buf, " ");
370
371         n = error_no_search (error_ccl_tab, error);
372         if (n)
373         {
374             sprintf (name, "gw.err.%s", n);
375             v = gw_res_get (info.kernel_res, name, NULL);
376         }
377         if (!v)
378             v = ccl_err_msg (error);
379         fprintf (reply_fd, "%s\n", v);
380         return -1;
381     }
382     ccl_pr_tree (rpn, reply_fd);
383     fprintf (reply_fd, "\n");
384
385     if (!*info.database )
386     {
387         fprintf (reply_fd, "%s\n",
388                  gw_res_get (info.kernel_res, "gw.err.no.database",
389                              "You must select database"));
390         return -3;
391     }
392     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Searching in database %s",
393             info.database );
394     assert (info.zass);
395     fprintf (reply_fd, "Searching in database %s\n", info.database);
396     p = zass_p_search (info.zass, rpn, setname, info.database, info.sets);
397     if (!p)
398     {
399         fprintf (reply_fd, "Search fail\n");
400         return -1;
401     }
402     if (p->errcode != -1)
403     {
404         display_diag_error (p->errcode, p->errstring);
405         return -2;
406     }
407     fprintf (reply_fd, "%d %s\n", p->num,
408              gw_res_get (info.kernel_res, "gw.msg.hits", "hit(s)"));
409     us = user_set_add (setname, p->num, info.database, rpn, 1, search_str);
410     fprintf (reply_fd, "Result-set %s created\n", setname);
411     if (info.setno >= 0)
412         info.setno++;
413     return 0;
414 }
415
416 static int exec_account (struct ccl_token *list)
417 {
418     if (list->kind != CCL_TOK_EOL)
419     {
420         int len = list->len;
421         memcpy (info.account, list->name, len);
422         info.target[len] = '\0';
423     }
424     else
425         *info.account = '\0';
426     return 0;
427 }
428
429 static int exec_target (struct ccl_token *list)
430 {
431     int len;
432     if (list->kind == CCL_TOK_EOL)
433         return -1;
434     len = list->len;
435     memcpy (info.target, list->name, len);
436     info.target [len] = '\0';
437
438     read_kernel_res ();
439     return reopen_target ();
440 }
441
442 static void exec_status_r (struct gw_user_set *sp)
443 {
444     if (!sp)
445         return;
446     exec_status_r (sp->prev);
447     fprintf (reply_fd, "%6s %7d %12.12s   %.50s\n", sp->name, sp->hits,
448              sp->database, sp->search_str);
449 }
450
451 static int exec_status (struct ccl_token *list)
452 {
453     fprintf (reply_fd, "  Name     Hits    Database    Find\n");
454     exec_status_r (info.sets);
455     return 0;
456 }
457
458 static int exec_base (struct ccl_token *list)
459 {
460     struct ccl_token *li = list;
461     int len = 0;
462
463     assert (info.zass);
464     if (list->kind == CCL_TOK_EOL)
465         return -1;
466     free (info.database);
467     while (li->kind != CCL_TOK_EOL)
468     {
469         len += li->len + 1;
470         li = li->next;
471         if (li->kind == CCL_TOK_COMMA)
472             li = li->next;
473     }
474     info.database  = malloc (len);
475     assert (info.database );
476     len = 0;
477     li = list;
478     while (li->kind != CCL_TOK_EOL)
479     {
480         memcpy (info.database+len, li->name, li->len);
481         len += li->len;
482         info.database[len++] = ',';
483         li = li->next;
484         if (li->kind == CCL_TOK_COMMA)
485             li = li->next;
486     }
487     info.database[len-1] = '\0';
488     return 0;
489 }
490
491 struct command_word show_tab [] = 
492 {
493 {   "f", "format"},
494 {   "p", "position"},
495 {   NULL, NULL }
496 };
497
498 static void present (const char *set, int offset, int number,
499                      struct ccl_token *format_token)
500 {
501     const struct zass_presentent *zp;
502     int len;
503     int max_number;
504     char format_str[16];
505     
506     max_number = atoi (gw_res_get (info.kernel_res, "gw.max.show", 
507                                    "200"));
508     if (number > max_number)
509         number = max_number;
510     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "present in set %s", set);
511     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "present of %d records from offset %d",
512             number, offset);
513     zp = zass_p_present(info.zass, (char *) set, offset, number);
514     if (zp)
515     {
516         int i;
517         zass_record *pp;
518         char path[128];
519         int  record_log_fd = -1;
520         const char *record_log_name;
521
522         record_log_name = gw_res_get (info.kernel_res, "gw.marc.log",
523                                       NULL);
524         if (record_log_name)
525         {
526             sprintf (path, "%s/%s", gw_res_get (info.kernel_res,
527                                                 "gw.path", "."),
528                      record_log_name );
529             record_log_fd = open (path, O_WRONLY|O_CREAT|O_APPEND, 0666);
530             if (record_log_fd == -1)
531                 gw_log (GW_LOG_WARN|GW_LOG_ERRNO, "Cannot open %s", path);
532         }
533         fprintf (reply_fd, gw_res_get (info.kernel_res,
534                                        "gw.msg.records",
535                                        "Got %d records from set %s"),
536                  zp->num, set);
537         fprintf (reply_fd, "\n");
538         for (i = 0, pp = zp->records; pp; pp = pp->next, i++)
539         {
540             Iso2709Rec rec;
541 #if USE_FML
542             const char *arg_ar[5];
543 #endif
544             fprintf (reply_fd, "--- %d/%d ---\n",
545                      i+offset, offset+zp->num-1);
546             if (!gw_res_get (info.kernel_res, "gw.ignore.which", NULL))
547             {
548                 if (pp->which == ZASS_REC_DIAG)
549                 {
550                     display_diag_error (pp->errcode, pp->errstring);
551                     continue;
552                 }
553                 else if (pp->which != ZASS_REC_USMARC)
554                 {
555                     fprintf (reply_fd, "Unknown record kind %d\n",
556                              pp->which);
557                     continue;
558                 }
559             }
560             if (record_log_fd != -1)
561                 write (record_log_fd, pp->record, strlen(pp->record));
562             rec = iso2709_cvt (pp->record);
563             if (rec)
564             {
565 #if USE_FML
566                 if (format_token)
567                 {
568                     len = format_token->len;
569                     if (len >= sizeof(format_str))
570                         len = sizeof(format_str)-1;
571                     memcpy (format_str, format_token->name, len);
572                     format_str[len] = '\0';
573                 }
574                 if (info.fml && format_token && 
575                     (!strcmp (format_str, "0") || !strcmp (format_str, "1")
576                      || !strcmp(format_str, "2")))
577                 {
578                     arg_ar[0] = "\\f";
579                     arg_ar[1] = format_str;
580                     arg_ar[2] = " \\list";
581                     arg_ar[3] = marc_to_str (info.fml, rec);
582                     arg_ar[4] = NULL;
583                     fml_exec_call_argv (info.fml, arg_ar);
584                 }
585                 else
586                     iso2709_display (rec, reply_fd);
587 #else
588                 iso2709_display (rec, reply_fd);
589 #endif
590                 iso2709_rm (rec);
591             }
592             else
593                 fprintf (reply_fd, "Not a MARC record\n");
594         }
595         if (record_log_fd != -1)
596             close (record_log_fd);
597     }
598 }
599
600 static int exec_show (struct ccl_token *list)
601 {
602     char tmp_str[20];
603     struct ccl_token *set_token = NULL;
604     struct ccl_token *format_token = NULL;
605     struct ccl_token *li = list;
606     int no_of_present = 0;
607
608     assert (info.zass);
609     while (li->kind != CCL_TOK_EOL)
610     {
611         int modifier_no = 0;
612         if (li->next->kind == CCL_TOK_EQ)
613         {
614             if (li->kind == CCL_TOK_SET)    /* set = <name> ? */
615             {
616                 li = li->next->next;
617                 set_token = li;
618             }
619             else 
620             {
621                 modifier_no = command_search (show_tab, li, "ccl.token.");
622                 if (!modifier_no)
623                 {
624                     fprintf (reply_fd, "Unknown modifier in show\n");
625                     return -1;
626                 }
627                 li = li->next->next;
628                 if (modifier_no == 1)       /* f = <name> ? */
629                     format_token = li;
630                 else if (modifier_no == 2)  /* p = <name> ? */
631                 {
632                     if (li->kind != CCL_TOK_EOL   /* p = <name> - <name> ? */
633                         && li->next->kind == CCL_TOK_MINUS
634                         && li->next->next != CCL_TOK_EOL)
635                         li = li->next->next;
636                 }
637             }
638             if (!li->next)
639             {
640                 fprintf (reply_fd, "%s\n", "Missing token after '='");
641                 return -2;
642             }
643             li = li->next;
644         }
645         else
646             li = li->next;
647     }
648     if (set_token)
649         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Got set=%.*s", set_token->len,
650                 set_token->name);
651     if (format_token)
652         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Got format=%.*s", format_token->len,
653                 format_token->name);
654
655     li = list;
656     while (li->kind != CCL_TOK_EOL)
657     {
658         int modifier_no = 0;
659         int offset = 0;
660         int number = 0;
661         int len;
662         if (li->next->kind == CCL_TOK_EQ && li->kind != CCL_TOK_SET)
663         {
664             modifier_no = command_search (show_tab, li, "ccl.token.");
665             li = li->next->next;
666             if (modifier_no == 2)  /* p = <name> ? */
667             {
668                 if (li->kind != CCL_TOK_EOL   /* p = <name> - <name> ? */
669                     && li->next->kind == CCL_TOK_MINUS
670                     && li->next->next != CCL_TOK_EOL)
671                 {
672                     len = li->len;
673                     memcpy (tmp_str, li->name, len);
674                     tmp_str [len] = '\0';
675                     offset = atoi (tmp_str);
676                     li = li->next->next;
677
678                     len = li->len;
679                     memcpy (tmp_str, li->name, len);
680                     tmp_str [len] = '\0';
681                     number = atoi (tmp_str) - offset + 1;
682                 }
683                 else
684                 {
685                     len = li->len;
686                     memcpy (tmp_str, li->name, len);
687                     tmp_str [len] = '\0';
688                     offset = atoi (tmp_str);
689                     number = 1;
690                 }
691             }
692             li = li->next;
693         }
694         else
695         {
696             len = li->len;
697             memcpy (tmp_str, li->name, len);
698             tmp_str[len] = '\0';
699             number = atoi (tmp_str);
700             offset = 1;
701             li = li->next;
702         }
703         if (offset > 0 && number > 0)
704         {
705             struct gw_user_set *us;
706
707             if (set_token)
708             {
709                 len = set_token->len;
710                 memcpy (tmp_str, set_token->name, len);
711                 tmp_str[len] = '\0';
712                 us = user_set_search (tmp_str);
713             }
714             else
715                 us = user_set_search (NULL);
716             if (us && us->hits != -1) /* proper result-set? */
717             {
718                 if (offset <= us->hits)
719                 {
720                     if (offset+number-1 > us->hits)
721                         number = us->hits - offset+1;
722                     present (us->name, offset, number, format_token);
723                 }
724             }
725             else if (!no_of_present) /* display error message once! */
726             {
727                 fprintf (reply_fd, "%s\n",
728                          gw_res_get (info.kernel_res, "gw.err.no.set",
729                                      "No result-set generated"));
730             }
731             no_of_present++;
732         }
733     }
734     if (!no_of_present) /* no records shown so far? */
735     {
736         struct gw_user_set *us;
737         int default_show;
738         
739         us = user_set_search (NULL);
740         if (us && us->hits != -1)    /* proper result-set? */
741         {
742             default_show = atoi (gw_res_get (info.kernel_res,
743                                              "gw.default.show", "20"));
744             if (us->hits > default_show)
745                 present (us->name, 1, default_show, format_token);
746             else if (us->hits > 0)
747                 present (us->name, 1, us->hits, format_token);
748         }
749         else                         /* display error message */
750         {
751             fprintf (reply_fd, "%s\n",
752                      gw_res_get (info.kernel_res, "gw.err.no.set",
753                                  "No result-set generated"));
754             return -3;
755         }        
756     }
757     return 0;
758 }
759
760 static int exec_command (const char *str)
761 {
762     struct ccl_token *cmd = ccl_tokenize (str);
763     int no;
764
765     if (cmd->kind != CCL_TOK_EOL &&
766         (no = command_search (command_tab, cmd, "ccl.command.")))
767     {
768         if (!info.zass && no != 9 && no != 4 && no != 11 && no != 7)
769             reopen_target ();
770         fprintf (reply_fd, "\n> %s\n", str);
771         if (!info.zass && (no == 1 || no == 2 || no == 3))
772         {
773             fprintf (reply_fd, "%s\n",
774                      gw_res_get (info.kernel_res, "gw.err.no.target",
775                                  "No connection established - "
776                                  "command ignored"));
777             return 0;
778         }
779 #if 0
780         ccl_token_and = gw_res_get (info.kernel_res, "ccl.token.and", "and");
781         ccl_token_or = gw_res_get (info.kernel_res, "ccl.token.or", "or");
782         ccl_token_not = gw_res_get (info.kernel_res, "ccl.token.not", "not");
783         ccl_token_set = gw_res_get (info.kernel_res, "ccl.token.set", "set");
784 #endif
785         switch (no)
786         {
787         case 1:
788             return exec_find (cmd->next, str);
789         case 2:
790             return exec_show (cmd->next);
791         case 3:
792             return exec_base (cmd->next);
793         case 4:
794             return exec_help (cmd->next);
795         case 7:
796             return exec_status (cmd->next);
797         case 9:
798             return exec_target (cmd->next);
799         case 11:
800             return exec_account (cmd->next);
801         default:
802             fprintf (reply_fd, "%s\n",
803                      gw_res_get (info.kernel_res, "gw.err.unimplemented",
804                                  "Not implemented yet"));
805         }
806     }
807     else
808     {
809         fprintf (reply_fd, "\n> %s\n", str);
810         fprintf (reply_fd, "  ^ %s\n", 
811                  gw_res_get (info.kernel_res, "gw.err.unknown.command",
812                              "unknown command. "
813                              "Use help to see list of commands"));
814     }
815     return 0;
816 }
817
818 int urp_start (int continuation, struct str_queue *queue)
819 {
820     char subject_str[128];
821
822     info.command_no = 0;
823     info.reply_fname = NULL;
824
825     if (email_header (queue, info.from_str, subject_str))
826     {
827         gw_log (GW_LOG_WARN, KERNEL_LOG, "No message body");
828         return -1;
829     }
830     tty_init (stdout, 40, 70);
831     if (*info.from_str)
832     {
833         info.reply_fname = tempnam (gw_res_get (info.kernel_res,
834                                            "gw.reply.tmp.dir", NULL),
835                                gw_res_get (info.kernel_res,
836                                            "gw.reply.tmp.prefix", "gwr"));
837                                                  
838         reply_fd = fopen (info.reply_fname, "w");
839         if (!reply_fd)
840         {
841             gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, KERNEL_LOG, "Cannot create %s",
842                     info.reply_fname);
843             return -1;
844         }
845         tty_init (reply_fd, 0, 0);
846         fprintf (reply_fd, "From: %s\n",
847                  gw_res_get (info.kernel_res, "gw.msg.from","Email-gateway"));
848         fprintf (reply_fd, "Subject: ");
849         if (*subject_str)
850             fprintf (reply_fd, "Z39.50 Re: %s", subject_str);
851         else
852             fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
853                                                    "gw.msg.subject",
854                                                    "Your Query"));
855         fprintf (reply_fd, "\n");
856         gw_log (GW_LOG_ACCT, KERNEL_LOG, "User start %s", info.from_str);
857     }
858     else
859         gw_log (GW_LOG_WARN, KERNEL_LOG, "No From in email header");
860     fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res, "gw.msg.greeting",
861                                            "Email->Z39.50 gateway"));
862     if (continuation)
863         fprintf (reply_fd, "%s\n", gw_res_get (info.kernel_res,
864                                                "gw.msg.cont",
865                                                "Continued..."));
866     return 0;
867 }
868
869 int urp_command (struct str_queue *queue)
870 {
871     char *cp;
872
873     while (str_queue_deq (queue, line_buf, LINE_MAX))
874     {
875         if (line_buf[0] == '\n')
876             if (info.command_no)
877             {
878                 while (str_queue_deq (queue, 0, 0))
879                     ;
880                 break;
881             }
882             else 
883                 continue;
884         if ((cp = strchr (line_buf, '\n')))
885             *cp = '\0';
886         gw_log (GW_LOG_ACCT, KERNEL_LOG, "cmd: %s", line_buf);
887         if (isalpha (line_buf[0]))
888             exec_command (line_buf);
889         info.command_no++;
890     }
891     return 0;
892 }
893
894 void urp_end (void)
895 {
896     if (!info.command_no)
897     {
898         fprintf (reply_fd, "%s\n", 
899                  gw_res_get (info.kernel_res, "gw.err.nullbody", "No body"));
900         help_general ();
901     }
902     if (*info.from_str)
903     {
904         const char *mta;
905         char cmd[256];
906         int mta_code;
907
908         assert (info.reply_fname);
909         fclose (reply_fd);
910         reply_fd = stdout;
911
912         mta = gw_res_get (info.kernel_res, "gw.reply.mta",
913                           "/usr/lib/sendmail");
914         sprintf (cmd, "%s %s < %s", mta, info.from_str, info.reply_fname);
915         
916         mta_code = system (cmd);
917         if (mta_code)
918             gw_log (GW_LOG_FATAL, KERNEL_LOG,
919                     "Reply '%s' got exit code %d", cmd, mta_code);
920         else
921             unlink (info.reply_fname);        
922         gw_log (GW_LOG_ACCT, KERNEL_LOG, "User end %s", info.from_str);
923     }
924 }
925