Run latex
[egate.git] / res+log / gw-res.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /*
45  * Implementation of resource management.
46  *
47  * Europagate, 1994-1995.
48  *
49  * $Log: gw-res.c,v $
50  * Revision 1.7  1995/05/16 09:40:49  adam
51  * LICENSE.
52  *
53  * Revision 1.6  1995/04/19  12:12:07  adam
54  * Resource system uses only one log debug level.
55  *
56  * Revision 1.5  1995/02/23  08:32:22  adam
57  * Changed header.
58  *
59  * Revision 1.3  1995/02/21  14:00:11  adam
60  * Minor changes.
61  *
62  * Revision 1.2  1995/02/16  13:21:30  adam
63  * A few logging messages added.
64  *
65  * Revision 1.1.1.1  1995/02/09  17:27:12  adam
66  * Initial version of email gateway under CVS control.
67  *
68  * Initial:       Dec  8, 94 (Adam Dickmeiss)
69  */
70 #include <assert.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <stdarg.h>
75 #include <fcntl.h>
76 #include <unistd.h>
77 #include <time.h>
78
79 #if HAVE_FLOCK
80 #include <sys/file.h>
81 #else
82 #include <sys/types.h>
83 #endif
84
85 #include <gw-log.h>
86 #include "gw-resp.h"
87
88 /*
89    symtab_open: Create empty symbol table.
90      symbol table handle is returned.
91  */
92
93 static struct res_symtab *symtab_open (void)
94 {
95     struct res_symtab *symtab;
96
97     symtab = malloc (sizeof(*symtab));
98     if (!symtab)
99         return NULL;
100     symtab->next = NULL;
101     return symtab;
102 }
103
104 /*
105    symtab_close: Delete symbol table.
106  */
107 static void symtab_close (struct res_symtab *symtab)
108 {
109     struct res_sym_entry *entry, *entry1;
110
111     for (entry = symtab->next; entry; entry = entry1)
112     {
113         entry1 = entry->next;
114         free (entry);
115     }
116     free (symtab);
117 }
118
119 /*
120    symtab_override: Add symbol to table. 'rl' holds symbol
121      table entry info. If the symbol is already present it
122      will override old info.
123  */
124 static int symtab_override (struct res_symtab *symtab,
125                             struct res_line_info *rl)
126 {
127     struct res_sym_entry *entry;
128
129     for (entry = symtab->next; entry; entry=entry->next)
130         if (!strcmp (entry->info->name, rl->name))
131         {
132             entry->info = rl;
133             return 1;
134         }
135     entry = malloc (sizeof(*entry));
136     if (!entry)
137         return -1;
138     entry->next = symtab->next;
139     symtab->next = entry;
140     entry->info = rl;
141     return 0;
142 }
143
144 /*
145    symtab_lookup: Symbol table lookup. If successful info is returned; 
146      otherwise NULL is returned.
147  */
148 static struct res_line_info *symtab_lookup (struct res_symtab *symtab, 
149                                             const char *name)
150 {
151     struct res_sym_entry *entry;
152
153     for (entry = symtab->next; entry; entry=entry->next)
154         if (!strcmp (entry->info->name, name))
155             return entry->info;
156     return NULL;
157 }
158
159 /*
160    lock_file: File locking using fcntl.
161  */
162 #if !HAVE_FLOCK
163 static void lock_file (int fd, int type)
164 {
165     struct flock area;
166     area.l_type = type;
167     area.l_whence = SEEK_SET;
168     area.l_start = 0L;
169     area.l_len = 0L;
170     fcntl (fd, F_SETLKW, &area);
171 }
172 #endif
173
174 /*
175    gw_res_init: A resource handle is returned by this function
176      describing empty resources.
177  */
178 GwRes gw_res_init (void)
179 {
180     GwRes p;
181
182     if (!(p = malloc (sizeof(*p))))
183         return p;
184     p->files = NULL;
185     p->symtab = symtab_open ();
186     if (!p->symtab)
187     {
188         free (p);
189         return NULL;
190     }
191     return p;
192 }
193
194 /*
195    gw_res_close: The resources described by 'id' are freed.
196       No further references to 'id' are allowed.
197  */
198 void gw_res_close (GwRes id)
199 {
200     struct res_line_info *rl, *rl1;
201     struct res_file_info *rf, *rf1;
202     
203     assert (id);
204
205     symtab_close (id->symtab);
206     for (rf = id->files; rf; rf = rf1)
207     {
208         for (rl = rf->lines; rl; rl = rl1)
209         {
210             free (rl->name);
211             free (rl->value);
212             rl1 = rl->next;
213             free (rl);
214         }
215         free (rf->fname);
216         rf1 = rf->next;
217         free (rf);
218     }
219     free (id);
220 }
221
222 /*
223    add_name: add a node with line information.
224  */
225 static struct res_line_info *add_name (enum res_kind kind, 
226                                        const char *name, const char *value)
227 {
228     struct res_line_info *rl;
229
230     if (!(rl = malloc (sizeof(*rl))))
231         return NULL;
232     rl->next = NULL;
233     rl->kind = kind;
234     if (name)
235     {
236         rl->name = gw_strdup (name);
237         if (!rl->name)
238         {
239             free (rl);
240             return NULL;
241         }
242     }
243     else
244         rl->name = NULL;
245     if (value)
246     {
247         rl->value = gw_strdup (value);
248         if (!rl->value)
249         {
250             free (rl->name);
251             free (rl);
252             return NULL;
253         }
254     }
255     else
256         rl->value = NULL;
257     return rl;
258 }
259
260 /*
261    gw_res_merge: The resources described by 'id' are merged by the contents
262      of  'filename'. If a resource is duplicated (in both resources 'id'
263      and the file) the resource is set to the value specified in 'filename'.
264      This function returns 0 on success; -1 on failure ('filename'
265      could not be read)
266  */
267 int gw_res_merge (GwRes id, const char *filename)
268 {
269     FILE *inf;
270     char buffer[1024];
271     char value[2048];
272     struct res_file_info *ri;
273     struct res_line_info **rlp, *rl;
274     struct res_line_info *rl_last = NULL;
275     int err = 0;
276
277     assert (id);
278     assert (filename);
279     gw_log (RES_DEBUG, "res", "gw_res_merge");
280     gw_log (RES_DEBUG, "res", "checking %s", filename);
281     if (!(inf = fopen (filename, "r")))
282         return -1;
283 #if HAVE_FLOCK
284     flock (fileno(inf), LOCK_SH);
285 #else
286     lock_file (fileno (inf), F_RDLCK);
287 #endif
288     if (!(ri = malloc (sizeof (*ri))))
289     {
290         fclose (inf);
291         return -1;
292     }
293     if (!(ri->fname = gw_strdup (filename)))
294     {
295         free (ri);
296         fclose (inf);
297         return -2;
298     }
299     gw_log (RES_DEBUG, "res", "reading %s", filename);
300     ri->next = id->files;
301     id->files = ri;
302     rlp = &ri->lines;
303     ri->lines = NULL;
304     while (fgets (buffer, sizeof(buffer)-1, inf))
305     {
306         char *cp;
307         while ((cp = strchr (buffer, '\n')))
308             *cp = '\0';
309         if (*buffer == '#')
310         {   /* comment line */
311             rl = add_name (comment, NULL, buffer);
312             if (!rl)
313             {
314                 err = -2;
315                 break;
316             }
317             *rlp = rl;
318             rlp = &rl->next;
319         }
320         else if (*buffer == '\0' || *buffer == ' ' || *buffer == '\t')
321         {
322             int i = 0;
323             while (buffer[i] == ' ' || buffer[i] == '\t')
324                 i++;
325             if (buffer[i] == '\0')
326             {   /* empty line */
327                 rl = add_name (blank, NULL, NULL);
328                 if (!rl)
329                 {
330                     err = -2;
331                     break;
332                 }
333                 *rlp = rl;
334                 rlp = &rl->next;
335             }
336             else
337             {   /* continuation line */
338                 int j = strlen (buffer)-1;
339                 /* strip trailing blanks */
340                 while (buffer[j] == '\t' || buffer[j] == ' ')
341                     --j;
342                 buffer[j+1] = '\0';
343                 if (rl_last)
344                 {
345                     if (strlen(value)+strlen(buffer+i) >= sizeof(value)-2)
346                     {
347                         gw_log (GW_LOG_WARN, "res", "Resource `%s' is "
348                                 " truncated", rl_last->name);
349                     }
350                     else
351                     {
352                         /* effectively add one blank, then buffer */
353                         strcat (value, " ");
354                         strcat (value, buffer+i);
355                     }
356                 }
357                 else
358                     gw_log (GW_LOG_WARN, "res", "Resource file has bad "
359                             "continuation line");
360             }
361         }
362         else 
363         {   /* resource line */
364             int i = 0;
365             if (rl_last)
366             {
367                 rl_last->value = gw_strdup (value);
368                 rl_last = NULL;
369             }
370             while (buffer[i] && buffer[i] != ':')
371                 i++;
372             if (buffer[i] == ':')
373             {
374                 int j = strlen(buffer)-1;
375                 buffer[i++] = '\0';            /* terminate name */
376                 while (buffer[i] == ' ' || buffer[i] == '\t')
377                     i++;                       /* skip blanks before */
378                 while (buffer[j] == '\t' || buffer[j] == ' ')
379                     --j;                       /* skip blanks after */
380                 buffer[j+1] = '\0';            /* terminate value */
381                 strcpy (value, buffer+i);
382                 rl_last = add_name (resource, buffer, NULL);
383                 if (!rl_last)
384                     err = -2;
385                 else
386                 {
387                     *rlp = rl_last;
388                     rlp = &rl_last->next;
389                 }
390             }
391         }
392     }
393     if (rl_last)
394         rl_last->value = gw_strdup (value);
395 #if HAVE_FLOCK
396     flock (fileno (inf), LOCK_UN);
397 #else
398     lock_file (fileno (inf), F_UNLCK);
399 #endif
400     fclose (inf);
401     gw_log (RES_DEBUG, "res", "close of %s", filename);
402     for (rl = ri->lines; rl; rl = rl->next)
403     {
404         switch (rl->kind)
405         {
406         case comment:
407             gw_log (RES_DEBUG, "res", "%s", rl->value);
408             break;
409         case resource:
410             gw_log (RES_DEBUG, "res", "%s: %s", rl->name, rl->value);
411             if (symtab_override (id->symtab, rl) < 0)
412                 err = -2;
413             break;
414         case blank:
415             gw_log (RES_DEBUG, "res", "");
416             break;
417         default:
418             assert (0);
419         }
420     }
421     gw_log (RES_DEBUG, "res", "gw_res_merge returned %d", err);
422     return err;
423 }
424
425 /*
426    gw_res_get: The resource with name 'name' is checked in the resources
427      represented by 'id'. If the resource is present a pointer to the
428      value (null-terminated string) is returned. If the value is not
429      present the value of 'def' is returned.
430  */
431 const char *gw_res_get (GwRes id, const char *name, const char *def)
432 {
433     struct res_line_info *rl;
434
435     assert (id);
436     rl = symtab_lookup (id->symtab, name);
437     if (!rl)
438         return def;
439     return rl->value;
440 }
441
442 /*
443    gw_res_put: Change a resource - modify if it exists - add if not
444      already there. The resource will have impact on the file name
445      'fname'. Use gw_res_commit (see below) to actually write to the
446      resource file.
447  */
448 int gw_res_put (GwRes id, const char *name, const char *value, 
449                 const char *fname)
450 {
451     struct res_file_info *ri;
452     struct res_line_info **rlp;
453     assert (id);
454     assert (fname);
455
456     for (ri = id->files; ri; ri = ri->next)
457         if (!strcmp (ri->fname, fname))
458             break;
459     if (!ri)
460     {
461         if (!(ri = malloc (sizeof (*ri))))
462             return -1;
463         if (!(ri->fname = gw_strdup (fname)))
464         {
465             free (ri);
466             return -1;
467         }
468         ri->next = id->files;
469         id->files = ri;
470         ri->lines = NULL;
471     }
472     for (rlp = &ri->lines; *rlp; rlp = &(*rlp)->next)
473         if (!strcmp ((*rlp)->name, name))
474             break;
475     if (*rlp)
476     {
477         char *new_val = gw_strdup (value);
478         if (!new_val)
479             return -1;
480         free ((*rlp)->value);
481         (*rlp)->value = new_val;
482     }
483     else
484     {
485         *rlp = add_name (resource, name, value);
486         if (!*rlp)
487             return -1;
488         (*rlp)->next = NULL;
489         if (symtab_override (id->symtab, *rlp) < 0)
490             return -1;
491     }
492     return 0;
493 }
494
495 /*
496    gw_res_commit: Write the resource file 'fname'. If resources
497      are modified/added then these will be written now.
498  */
499 int gw_res_commit (GwRes id, const char *fname)
500 {
501     struct res_file_info *ri;
502     struct res_line_info *rl;
503     FILE *out;
504     int i, pos;
505
506     assert (id);
507     assert (fname);
508
509     for (ri = id->files; ri; ri = ri->next)
510         if (!strcmp (ri->fname, fname))
511             break;
512     if (!ri)
513         return -1;
514     if (!(out = fopen (fname, "w")))
515         return -1;
516 #if HAVE_FLOCK
517     flock (fileno (out), LOCK_EX);
518 #else
519     lock_file (fileno (out), F_WRLCK);
520 #endif
521     for (rl = ri->lines; rl; rl = rl->next)
522         switch (rl->kind)
523         {
524         case comment:
525             fputs (rl->value, out);
526         case blank:
527             fputc ('\n', out);
528             break;
529         case resource:
530             fprintf (out, "%s: ", rl->name);
531             pos = strlen(rl->name)+2;
532             i = 0;
533             while (1)
534             {
535                 int left = 78-pos;
536                 if ((int) strlen(rl->value+i) <= left)
537                     break;
538                 while (left > 0)
539                 {
540                     if (rl->value[i+left] == ' ')
541                         break;
542                     
543                     --left;
544                 }
545                 if (left > 0)
546                 {
547                     int j;
548                     for (j = 0; j<left; j++)
549                         fputc (rl->value[i+j], out);
550                     i += left+1;
551                 }
552                 else
553                     break;
554                 fprintf (out, "\n ");
555                 pos = 2;
556             }
557             fprintf (out, "%s\n", rl->value+i);
558             break;
559         default:
560             assert (0);
561         }
562     fflush (out);
563 #if HAVE_FLOCK
564     flock (fileno (out), LOCK_UN);
565 #else
566     lock_file (fileno (out), F_UNLCK);
567 #endif
568     fclose (out);
569     return 0;
570 }
571
572 /*
573    gw_res_trav: Traverse resources associated with file 'fname'. For
574      each resource the handler 'tf' is invoked with name and value.
575  */
576 int gw_res_trav (GwRes id, const char *fname, void (*tf)(const char *name,
577                                                          const char *value))
578 {
579     assert (id);
580     assert (tf);
581
582     if (fname)
583     {
584         struct res_file_info *ri;
585         struct res_line_info *rl;
586
587         for (ri = id->files; ri; ri = ri->next)
588             if (!strcmp (ri->fname, fname))
589                 break;
590         if (!ri)
591             return -1;
592         for (rl = ri->lines; rl; rl = rl->next)
593             if (rl->kind == resource)
594                 (*tf) (rl->name, rl->value);
595     }
596     else
597     {
598         struct res_sym_entry *entry;
599         
600         for (entry = id->symtab->next; entry; entry=entry->next)
601             (*tf) (entry->info->name, entry->info->value);
602     }
603     return 0;
604 }
605
606
607