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