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