]> andersk Git - moira.git/blob - gen/mailhub.pc
Update comment; the illegalchars array here no longer corresponds to the
[moira.git] / gen / mailhub.pc
1 /* $Id$
2  *
3  * This generates the /usr/lib/aliases file for the mailhub.
4  *
5  * (c) Copyright 1988-1998 by the Massachusetts Institute of Technology.
6  * For copying and distribution information, please see the file
7  * <mit-copyright.h>.
8  */
9
10 #include <mit-copyright.h>
11 #include <moira.h>
12 #include <moira_site.h>
13
14 #include <sys/stat.h>
15
16 #include <ctype.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "util.h"
22
23 EXEC SQL INCLUDE sqlca;
24
25 RCSID("$Header$");
26
27 char *whoami = "mailhub.gen";
28 char *db = "moira/moira";
29 char *divide = "##############################################################";
30
31 #define MAX_LINE_WIDTH  72
32 #define MAX_ALIAS_WIDTH 592
33
34 #define FALSE 0
35 #define TRUE (!FALSE)
36
37 struct hash *users, *machines, *strings, *lists;
38 struct user {
39   char *login;
40   char *pobox;
41 };
42 struct member {
43   struct member *next;
44   char *name;
45   int list_id;
46 };
47 struct list {
48   char *name;
49   char maillist;
50   char *description;
51   char acl_t;
52   int acl_id;
53   struct member *m;
54 };
55
56 void get_info(void);
57 int check_string(char *s);
58 void output_login(int dummy, void *names, void *out);
59 void output_mlist(int id, void *list, void *out);
60 void output_membership(struct list *l, FILE *out);
61 void put_fill(FILE *aliases, char *string);
62 void do_people(void);
63
64 int incount = 0;
65
66 int main(int argc, char **argv)
67 {
68   time_t tm = time(NULL);
69   char filename[MAXPATHLEN], *targetfile;
70   struct stat sb;
71   int flag1, flag2;
72   FILE *out = stdout;
73
74   srand(tm);
75   EXEC SQL CONNECT :db;
76
77   if (argc == 2)
78     {
79       if (stat(argv[1], &sb) == 0)
80         {
81           if (ModDiff(&flag1, "users", sb.st_mtime) ||
82               ModDiff(&flag2, "list", sb.st_mtime))
83             exit(MR_DATE);
84           if (flag1 < 0 && flag2 < 0)
85             {
86               fprintf(stderr, "File %s does not need to be rebuilt.\n",
87                       argv[1]);
88               exit(MR_NO_CHANGE);
89             }
90         }
91       targetfile = argv[1];
92       sprintf(filename, "%s~", targetfile);
93       if (!(out = fopen(filename, "w")))
94         {
95           fprintf(stderr, "unable to open %s for output\n", filename);
96           exit(MR_OCONFIG);
97         }
98     }
99   else if (argc != 1)
100     {
101       fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
102       exit(MR_ARGS);
103     }
104
105   fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm));
106   fprintf(out, "# This file is automatically generated, "
107           "do not edit it directly.\n%s\n\n", divide);
108
109   get_info();
110
111   EXEC SQL COMMIT;
112
113   incount = 0;
114   fprintf(out, "\n%s\n# Mailing lists\n%s\n\n", divide, divide);
115   hash_step(lists, output_mlist, out);
116   fprintf(stderr, "Output %d lists\n", incount);
117
118   incount = 0;
119   fprintf(out, "\n%s\n# People\n%s\n\n", divide, divide);
120   hash_step(users, output_login, out);
121   fprintf(stderr, "Output %d users\n", incount);
122
123   fprintf(out, "\n%s\n# End of aliases file\n", divide);
124
125   if (fclose(out))
126     {
127       perror("close failed");
128       exit(MR_CCONFIG);
129     }
130
131   if (argc == 2)
132     fix_file(targetfile);
133   exit(MR_SUCCESS);
134 }
135
136 void get_info(void)
137 {
138   EXEC SQL BEGIN DECLARE SECTION;
139   int id, pid, iid, bid, cnt, maillistp, acl, mid;
140   char mname[MACHINE_NAME_SIZE], str[STRINGS_STRING_SIZE];
141   char login[USERS_LOGIN_SIZE], potype[USERS_POTYPE_SIZE];
142   char lname[LIST_NAME_SIZE], desc[LIST_DESCRIPTION_SIZE];
143   char type[LIST_ACL_TYPE_SIZE];
144   EXEC SQL END DECLARE SECTION;
145   char *s;
146   struct user *u;
147   struct list *l, *memberlist;
148   struct member *m;
149
150   /* The following is declarative, not executed,
151    * and so is dependent on where it is in the file,
152    * not in the order of execution of statements.
153    */
154   EXEC SQL WHENEVER SQLERROR GOTO sqlerr;
155
156   cnt = 0;
157   machines = create_hash(100);
158
159   EXEC SQL DECLARE m_cursor CURSOR FOR
160     SELECT mach_id, name
161     FROM machine
162     WHERE status = 1
163     AND ( mach_id IN ( SELECT UNIQUE pop_id FROM users ) OR
164           mach_id IN ( SELECT UNIQUE mach_id FROM filesys
165                        WHERE type = 'IMAP' ) )
166     ORDER BY mach_id;
167   EXEC SQL OPEN m_cursor;
168   while (1)
169     {
170       EXEC SQL FETCH m_cursor INTO :id, :mname;
171       if (sqlca.sqlcode)
172         break;
173       if ((s = strchr(mname, '.')))
174         *s = '\0';
175       else
176         strtrim(mname);
177 #ifdef ATHENA
178       strcat(mname, ".LOCAL");
179 #endif
180       if (hash_store(machines, id, strdup(mname)) < 0)
181         {
182           fprintf(stderr, "Out of memory!\n");
183           exit(MR_NO_MEM);
184         }
185       cnt++;
186     }
187   EXEC SQL CLOSE m_cursor;
188
189   fprintf(stderr, "Loaded %d machines\n", cnt);
190
191   cnt = 0;
192   strings = create_hash(11001);
193
194   EXEC SQL DECLARE s_cursor CURSOR FOR
195     SELECT string_id, string
196     FROM strings
197     ORDER BY string_id;
198   EXEC SQL OPEN s_cursor;
199   while (1)
200     {
201       EXEC SQL FETCH s_cursor INTO :id, :str;
202       if (sqlca.sqlcode)
203         break;
204       if (hash_store(strings, id, strdup(strtrim(str))) < 0)
205         {
206           fprintf(stderr, "Out of memory!\n");
207           exit(MR_NO_MEM);
208         }
209       cnt++;
210     }
211   EXEC SQL CLOSE s_cursor;
212
213   fprintf(stderr, "Loaded %d strings\n", cnt);
214
215   cnt = 0;
216   users = create_hash(13001);
217
218   EXEC SQL DECLARE u_cursor CURSOR FOR
219     SELECT users_id, login, potype, pop_id, imap_id, box_id
220     FROM users
221     WHERE status != 3
222     ORDER BY users_id;
223   EXEC SQL OPEN u_cursor;
224   while (1)
225     {
226       char *saddr = NULL, *paddr = NULL;
227
228       EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :iid, :bid;
229       if (sqlca.sqlcode)
230         break;
231       u = malloc(sizeof(struct user));
232       u->login = strdup(strtrim(login));
233
234       if (!strcmp(strtrim(potype), "NONE"))
235         u->pobox = NULL;
236       else
237         {
238           /* If SMTP or SPLIT, get SMTP address. */
239           if (potype[0] == 'S')
240             {
241               saddr = hash_lookup(strings, bid);
242
243               /* If SMTP, clear pid and iid. */
244               if (potype[1] == 'M')
245                 pid = iid = 0;
246             }
247
248           /* If IMAP, or SPLIT with IMAP, set pid to mach_id. */
249           if (potype[0] == 'I' || (potype[0] == 'S' && iid))
250             {
251               EXEC SQL SELECT mach_id INTO :pid FROM filesys
252                 WHERE filsys_id = :iid;
253             }
254
255           if (pid && (s = hash_lookup(machines, pid)))
256             {
257               paddr = malloc(strlen(u->login) + strlen(s) + 2);
258               sprintf(paddr, "%s@%s", u->login, s);
259             }
260
261           if (paddr && saddr)
262             {
263               u->pobox = malloc(strlen(paddr) + strlen(saddr) + 3);
264               sprintf(u->pobox, "%s, %s", paddr, saddr);
265               free(paddr);
266             }
267           else if (paddr)
268             u->pobox = paddr;
269           else
270             u->pobox = saddr;
271         }
272
273       check_string(u->login);
274       if (hash_store(users, id, u) < 0)
275         {
276           fprintf(stderr, "Out of memory!\n");
277           exit(MR_NO_MEM);
278         }
279       cnt++;
280     }
281   EXEC SQL CLOSE u_cursor;
282   fprintf(stderr, "Loaded %d users\n", cnt);
283
284   cnt = 0;
285   lists = create_hash(15000);
286
287   EXEC SQL DECLARE l_cursor CURSOR FOR
288     SELECT list_id, name, maillist, description, acl_type, acl_id
289     FROM list
290     WHERE active != 0
291     ORDER BY list_id;
292   EXEC SQL OPEN l_cursor;
293   while (1)
294     {
295       EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :desc, :type, :acl;
296       if (sqlca.sqlcode)
297         break;
298       l = malloc(sizeof(struct list));
299       l->name = strdup(strtrim(lname));
300       l->maillist = maillistp;
301       l->description = strdup(strtrim(desc));
302       l->acl_t = type[0];
303       l->acl_id = acl;
304       l->m = NULL;
305       if (hash_store(lists, id, l) < 0)
306         {
307           fprintf(stderr, "Out of memory!\n");
308           exit(MR_NO_MEM);
309         }
310       cnt++;
311     }
312   EXEC SQL CLOSE l_cursor;
313   fprintf(stderr, "Loaded %d lists\n", cnt);
314
315   cnt = 0;
316
317   EXEC SQL DECLARE m_cursor2 CURSOR FOR
318     SELECT list_id, member_type, member_id
319     FROM imembers
320     WHERE direct = 1
321     ORDER BY list_id;
322   EXEC SQL OPEN m_cursor2;
323   while (1)
324     {
325       EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
326       if (sqlca.sqlcode)
327         break;
328       cnt++;
329       if ((l = hash_lookup(lists, id)))
330         {
331           m = malloc(sizeof(struct member));
332           if (type[0] == 'U' && (u = hash_lookup(users, mid)))
333             {
334               m->list_id = 0;
335               m->name = u->login;
336               m->next = l->m;
337               l->m = m;
338             }
339           else if (type[0] == 'L' && (memberlist = hash_lookup(lists, mid)))
340             {
341               m->list_id = mid;
342               m->name = memberlist->name;
343               m->next = l->m;
344               l->m = m;
345             }
346           else if (type[0] == 'S' && (s = hash_lookup(strings, mid)))
347             {
348               m->list_id = 0;
349               m->next = l->m;
350               l->m = m;
351               m->name = s;
352             }
353         }
354     }
355   EXEC SQL CLOSE m_cursor2;
356   fprintf(stderr, "Loaded %d members\n", cnt);
357
358   EXEC SQL COMMIT;
359   return;
360 sqlerr:
361   db_error(sqlca.sqlcode);
362   exit(MR_DBMS_ERR);
363 }
364
365
366 void output_login(int dummy, void *user, void *out)
367 {
368   struct user *u = user;
369
370   incount++;
371   if (u->pobox && check_string(u->login) && u->login[0] != '#')
372     fprintf(out, "%s: %s\n", u->login, u->pobox);
373 }
374
375 int line_width, alias_width;
376
377 void output_mlist(int id, void *list, void *out)
378 {
379   struct list *l = list, *l1;
380   struct user *u;
381   int len = strlen(l->name);
382
383   if (!l->maillist || !check_string(l->name))
384     return;
385
386   /* If standard user group appears on a list, substitute in the user. */
387   if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name))
388     return;
389
390   put_fill(out, l->description);
391
392   if (l->acl_t ==  'L' && (l1 = hash_lookup(lists, l->acl_id)))
393     {
394       fprintf(out, "owner-%s: ", l->name);
395       if ((l1->maillist) && (strcmp(l->name, l1->name)))
396         fprintf(out, "%s\n", l1->name);
397       else
398         {
399           alias_width = line_width = len + 8;
400           output_membership(l1, out);
401           fprintf(out, "\n");
402         }
403     }
404   else if (l->acl_t ==  'U' && (u = hash_lookup(users, l->acl_id)))
405     fprintf(out, "owner-%s: %s\n", l->name, u->login);
406
407   fprintf(out, "%s: ", l->name);
408   alias_width = line_width = len + 2;
409   output_membership(l, out);
410   fprintf(out, "\n\n");
411   incount++;
412 }
413
414 void output_membership(struct list *l, FILE *out)
415 {
416   struct member *m;
417   struct list *l1;
418   int word_width, linestart = 1;
419   static int cont = 1;
420   char str[8];
421
422   for (m = l->m; m; m = m->next)
423     {
424       word_width = strlen(m->name);
425
426       if (!linestart && alias_width + word_width + 2 > MAX_ALIAS_WIDTH)
427         {
428           /* Make a continuation. */
429           sprintf(str, "%c%c%c%c%c%c", rand() % 26 + 97, rand() % 26 + 97,
430                   rand() % 26 + 97, rand() % 26 + 97,
431                   rand() % 26 + 97, rand() % 26 + 97);
432           fprintf(out, ",\n\tcont%d-%s\ncont%d-%s: ", cont, str, cont, str);
433           cont++;
434           alias_width = line_width = 17 + word_width;
435         }
436       else if (linestart)
437         {
438           /* First word on line, so we can't wrap. */
439           line_width += word_width;
440           alias_width = line_width;
441           linestart = 0;
442         }
443       else if (line_width + word_width + 2 > MAX_LINE_WIDTH)
444         {
445           /* Wrap. */
446           fputs(",\n\t", out);
447           alias_width += line_width + word_width + 2;
448           line_width = word_width + 8;
449         }
450       else
451         {
452           /* Continue line. */
453           line_width += word_width + 2;
454           fputs(", ", out);
455         }
456
457       if (m->list_id && (l1 = hash_lookup(lists, m->list_id)) && !l1->maillist)
458         output_membership(l1, out);
459       else
460         fputs(m->name, out);
461     }
462   if (!l->m)
463     fprintf(out, "/dev/null");
464 }
465
466 /* Write a word-wrapped list description to the aliases file as a
467  * comment. */
468 void put_fill(FILE *aliases, char *string)
469 {
470   char *c;
471   int line_width;
472   int word_width;
473
474   if (!string || !*string)
475     return;
476   fputs("#  ", aliases);
477   line_width = 3;
478
479   while (1)
480     {
481       while (*string == ' ')
482         string++;
483       c = strchr(string, ' ');
484       if (!c)
485         word_width = strlen(string);
486       else
487         {
488           word_width = c - string;
489           *c = '\0';
490         }
491
492       if (line_width + word_width > MAX_LINE_WIDTH)
493         {
494           fputs("\n#  ", aliases);
495           line_width = 3;
496           fputs(string, aliases);
497         }
498       else
499         fputs(string, aliases);
500
501       if (!c)
502         break;
503       /* add a space after the word */
504       fputc(' ', aliases);
505       word_width++;
506       line_width += word_width;
507       string += word_width;
508       /* add another if after a period */
509       if (*--c == '.')
510         {
511           fputc(' ', aliases);
512           line_width++;
513         }
514     }
515
516   fputc('\n', aliases);
517 }
518
519
520 /* Illegal chars: this no longer corresponds to the array 
521  * in setup_alis.  '+' is a valid character in a string on 
522  * a list, but is not a valid character in a listname.
523  */
524
525 static int illegalchars[] = {
526   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
527   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
528   1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, /* SPACE - / */
529   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */
530   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */
531   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */
532   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
533   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
534   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
535   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
536   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
537   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
538   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
539   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
540   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
541   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
542 };
543
544 int check_string(char *s)
545 {
546   for (; *s; s++)
547     {
548       if (isupper(*s))
549         *s = tolower(*s);
550
551       if (illegalchars[(unsigned) *s])
552         return 0;
553     }
554   return 1;
555 }
This page took 0.32631 seconds and 5 git commands to generate.