3 * This generates the /usr/lib/aliases file for the mailhub.
5 * (c) Copyright 1988-1998 by the Massachusetts Institute of Technology.
6 * For copying and distribution information, please see the file
10 #include <mit-copyright.h>
12 #include <moira_site.h>
23 EXEC SQL INCLUDE sqlca;
27 char *whoami = "mailhub.gen";
28 char *db = "moira/moira";
29 char *divide = "##############################################################";
31 #define MAX_LINE_WIDTH 72
32 #define MAX_ALIAS_WIDTH 592
39 struct hash *users, *machines, *strings, *lists;
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);
70 int main(int argc, char **argv)
72 time_t tm = time(NULL);
73 char filename[MAXPATHLEN], *targetfile;
80 sprintf(filename, "%s~", targetfile);
81 if (!(out = fopen(filename, "w")))
83 fprintf(stderr, "unable to open %s for output\n", filename);
89 fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
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);
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);
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);
111 fprintf(out, "\n%s\n# End of aliases file\n", divide);
115 perror("close failed");
120 fix_file(targetfile);
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;
135 struct list *l, *memberlist;
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.
142 EXEC SQL WHENEVER SQLERROR GOTO sqlerr;
145 machines = create_hash(100);
147 EXEC SQL DECLARE m_cursor CURSOR FOR
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' ) )
155 EXEC SQL OPEN m_cursor;
158 EXEC SQL FETCH m_cursor INTO :id, :mname;
161 if ((s = strchr(mname, '.')))
166 strcat(mname, ".LOCAL");
168 if (hash_store(machines, id, strdup(mname)) < 0)
170 fprintf(stderr, "Out of memory!\n");
175 EXEC SQL CLOSE m_cursor;
177 EXEC SQL DECLARE e_cursor CURSOR FOR
181 AND mach_id in (SELECT UNIQUE exchange_id FROM users)
183 EXEC SQL OPEN e_cursor;
186 EXEC SQL FETCH e_cursor INTO :id, :mname;
190 if (hash_store(machines, id, strdup(mname)) < 0)
192 fprintf(stderr, "Out of memory!\n");
197 EXEC SQL CLOSE e_cursor;
199 fprintf(stderr, "Loaded %d machines\n", cnt);
202 strings = create_hash(11001);
204 EXEC SQL DECLARE s_cursor CURSOR FOR
205 SELECT string_id, string
208 EXEC SQL OPEN s_cursor;
211 EXEC SQL FETCH s_cursor INTO :id, :str;
214 if (hash_store(strings, id, strdup(strtrim(str))) < 0)
216 fprintf(stderr, "Out of memory!\n");
221 EXEC SQL CLOSE s_cursor;
223 fprintf(stderr, "Loaded %d strings\n", cnt);
226 users = create_hash(13001);
228 EXEC SQL DECLARE u_cursor CURSOR FOR
229 SELECT users_id, login, potype, pop_id, imap_id, box_id, exchange_id
233 EXEC SQL OPEN u_cursor;
236 char *saddr = NULL, *paddr = NULL;
238 EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :iid, :bid,
242 u = malloc(sizeof(struct user));
243 u->login = strdup(strtrim(login));
245 if (!strcmp(strtrim(potype), "NONE"))
249 /* If SMTP or SPLIT, get SMTP address. */
250 if (potype[0] == 'S')
252 saddr = hash_lookup(strings, bid);
254 /* If SMTP, clear pid and iid. */
255 if (potype[1] == 'M')
259 /* If IMAP, or SPLIT with IMAP, set pid to mach_id. */
260 if (potype[0] == 'I' || (potype[0] == 'S' && iid))
262 EXEC SQL SELECT mach_id INTO :pid FROM filesys
263 WHERE filsys_id = :iid;
266 /* If EXCHANGE or SPLIT with EXCHANGE, set pid to eid. */
267 if (potype[0] == 'E' || (potype[0] == 'S' && eid))
270 if (pid && (s = hash_lookup(machines, pid)))
272 paddr = malloc(strlen(u->login) + strlen(s) + 2);
273 sprintf(paddr, "%s@%s", u->login, s);
278 u->pobox = malloc(strlen(paddr) + strlen(saddr) + 3);
279 sprintf(u->pobox, "%s, %s", paddr, saddr);
288 check_string(u->login);
289 if (hash_store(users, id, u) < 0)
291 fprintf(stderr, "Out of memory!\n");
296 EXEC SQL CLOSE u_cursor;
297 fprintf(stderr, "Loaded %d users\n", cnt);
300 lists = create_hash(15000);
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,
305 FROM list l, machine m
306 WHERE active != 0 AND l.mailman_id = m.mach_id
308 EXEC SQL OPEN l_cursor;
311 EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :desc, :type, :acl,
312 :mailman, :mailman_server;
315 l = malloc(sizeof(struct list));
316 l->name = strdup(strtrim(lname));
317 l->maillist = maillistp;
318 l->description = strdup(strtrim(desc));
321 l->mailman = mailman;
322 l->mailman_server = strdup(strtrim(mailman_server));
324 if (hash_store(lists, id, l) < 0)
326 fprintf(stderr, "Out of memory!\n");
331 EXEC SQL CLOSE l_cursor;
332 fprintf(stderr, "Loaded %d lists\n", cnt);
336 EXEC SQL DECLARE m_cursor2 CURSOR FOR
337 SELECT list_id, member_type, member_id
341 EXEC SQL OPEN m_cursor2;
344 EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
348 if ((l = hash_lookup(lists, id)))
350 m = malloc(sizeof(struct member));
351 if (type[0] == 'U' && (u = hash_lookup(users, mid)))
358 else if (type[0] == 'L' && (memberlist = hash_lookup(lists, mid)))
361 m->name = memberlist->name;
365 else if (type[0] == 'S' && (s = hash_lookup(strings, mid)))
374 EXEC SQL CLOSE m_cursor2;
375 fprintf(stderr, "Loaded %d members\n", cnt);
380 db_error(sqlca.sqlcode);
385 void save_mlist(int id, void *list, void *force)
388 struct list *l = list, *l1;
390 if (l->maillist > 1 || (l->maillist == 0 && !force) ||
391 !check_string(l->name))
394 /* If user group appears on list, replace with user. */
395 if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name))
403 if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
404 save_mlist(0, l1, (void *)TRUE);
406 for (m = l->m; m; m = m->next)
408 if (m->list_id && (l1 = hash_lookup(lists, m->list_id)))
409 save_mlist(0, l1, (void *)TRUE);
413 void output_login(int dummy, void *user, void *out)
415 struct user *u = user;
418 if (u->pobox && check_string(u->login) && u->login[0] != '#')
419 fprintf(out, "%s: %s\n", u->login, u->pobox);
422 static const char *mailman_suffixes[] = { "-admin", "-owner", "-request",
423 "-bounces", "-confirm", "-join",
424 "-leave", "-subscribe",
425 "-unsubscribe", NULL };
427 void output_mlist(int id, struct list *l)
432 int line_width, alias_width, word_width, beginning;
437 put_fill(out, l->description);
439 if (l->mailman && strcmp(l->mailman_server, "[NONE]"))
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);
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);
452 fprintf(out, "%s: ", l->name);
454 alias_width = line_width = strlen(l->name) + 2;
456 for (m = l->m; m; m = m->next)
458 word_width = strlen(m->name);
460 if (!beginning && alias_width + word_width + 2 > MAX_ALIAS_WIDTH)
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);
468 alias_width = line_width = 17 + word_width;
473 /* Beginning of alias, so don't wrap. */
474 line_width += word_width;
475 alias_width = line_width;
479 else if (line_width + word_width + 2 > MAX_LINE_WIDTH)
482 fprintf(out, ",\n\t%s", m->name);
483 alias_width += line_width + word_width + 2;
484 line_width = word_width + 8;
489 line_width += word_width + 2;
490 fprintf(out, ", %s", m->name);
494 fprintf(out, "/dev/null");
495 fprintf(out, "\n\n");
499 /* Write a word-wrapped list description to the aliases file as a
501 void put_fill(FILE *aliases, char *string)
507 if (!string || !*string)
509 fputs("# ", aliases);
514 while (*string == ' ')
516 c = strchr(string, ' ');
518 word_width = strlen(string);
521 word_width = c - string;
525 if (line_width + word_width > MAX_LINE_WIDTH)
527 fputs("\n# ", aliases);
529 fputs(string, aliases);
532 fputs(string, aliases);
536 /* add a space after the word */
539 line_width += word_width;
540 string += word_width;
541 /* add another if after a period */
549 fputc('\n', aliases);
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.
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,
577 int check_string(char *s)
584 if (illegalchars[(unsigned) *s])