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
37 struct hash *users, *machines, *strings, *lists;
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);
66 int main(int argc, char **argv)
68 time_t tm = time(NULL);
69 char filename[MAXPATHLEN], *targetfile;
79 if (stat(argv[1], &sb) == 0)
81 if (ModDiff(&flag1, "users", sb.st_mtime) ||
82 ModDiff(&flag2, "list", sb.st_mtime))
84 if (flag1 < 0 && flag2 < 0)
86 fprintf(stderr, "File %s does not need to be rebuilt.\n",
92 sprintf(filename, "%s~", targetfile);
93 if (!(out = fopen(filename, "w")))
95 fprintf(stderr, "unable to open %s for output\n", filename);
101 fprintf(stderr, "usage: %s [outfile]\n", argv[0]);
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);
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);
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);
123 fprintf(out, "\n%s\n# End of aliases file\n", divide);
127 perror("close failed");
132 fix_file(targetfile);
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;
147 struct list *l, *memberlist;
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.
154 EXEC SQL WHENEVER SQLERROR GOTO sqlerr;
157 machines = create_hash(100);
159 EXEC SQL DECLARE m_cursor CURSOR FOR
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' ) )
167 EXEC SQL OPEN m_cursor;
170 EXEC SQL FETCH m_cursor INTO :id, :mname;
173 if ((s = strchr(mname, '.')))
178 strcat(mname, ".LOCAL");
180 if (hash_store(machines, id, strdup(mname)) < 0)
182 fprintf(stderr, "Out of memory!\n");
187 EXEC SQL CLOSE m_cursor;
189 fprintf(stderr, "Loaded %d machines\n", cnt);
192 strings = create_hash(11001);
194 EXEC SQL DECLARE s_cursor CURSOR FOR
195 SELECT string_id, string
198 EXEC SQL OPEN s_cursor;
201 EXEC SQL FETCH s_cursor INTO :id, :str;
204 if (hash_store(strings, id, strdup(strtrim(str))) < 0)
206 fprintf(stderr, "Out of memory!\n");
211 EXEC SQL CLOSE s_cursor;
213 fprintf(stderr, "Loaded %d strings\n", cnt);
216 users = create_hash(13001);
218 EXEC SQL DECLARE u_cursor CURSOR FOR
219 SELECT users_id, login, potype, pop_id, imap_id, box_id
223 EXEC SQL OPEN u_cursor;
226 char *saddr = NULL, *paddr = NULL;
228 EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :iid, :bid;
231 u = malloc(sizeof(struct user));
232 u->login = strdup(strtrim(login));
234 if (!strcmp(strtrim(potype), "NONE"))
238 /* If SMTP or SPLIT, get SMTP address. */
239 if (potype[0] == 'S')
241 saddr = hash_lookup(strings, bid);
243 /* If SMTP, clear pid and iid. */
244 if (potype[1] == 'M')
248 /* If IMAP, or SPLIT with IMAP, set pid to mach_id. */
249 if (potype[0] == 'I' || (potype[0] == 'S' && iid))
251 EXEC SQL SELECT mach_id INTO :pid FROM filesys
252 WHERE filsys_id = :iid;
255 if (pid && (s = hash_lookup(machines, pid)))
257 paddr = malloc(strlen(u->login) + strlen(s) + 2);
258 sprintf(paddr, "%s@%s", u->login, s);
263 u->pobox = malloc(strlen(paddr) + strlen(saddr) + 3);
264 sprintf(u->pobox, "%s, %s", paddr, saddr);
273 check_string(u->login);
274 if (hash_store(users, id, u) < 0)
276 fprintf(stderr, "Out of memory!\n");
281 EXEC SQL CLOSE u_cursor;
282 fprintf(stderr, "Loaded %d users\n", cnt);
285 lists = create_hash(15000);
287 EXEC SQL DECLARE l_cursor CURSOR FOR
288 SELECT list_id, name, maillist, description, acl_type, acl_id
292 EXEC SQL OPEN l_cursor;
295 EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :desc, :type, :acl;
298 l = malloc(sizeof(struct list));
299 l->name = strdup(strtrim(lname));
300 l->maillist = maillistp;
301 l->description = strdup(strtrim(desc));
305 if (hash_store(lists, id, l) < 0)
307 fprintf(stderr, "Out of memory!\n");
312 EXEC SQL CLOSE l_cursor;
313 fprintf(stderr, "Loaded %d lists\n", cnt);
317 EXEC SQL DECLARE m_cursor2 CURSOR FOR
318 SELECT list_id, member_type, member_id
322 EXEC SQL OPEN m_cursor2;
325 EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
329 if ((l = hash_lookup(lists, id)))
331 m = malloc(sizeof(struct member));
332 if (type[0] == 'U' && (u = hash_lookup(users, mid)))
339 else if (type[0] == 'L' && (memberlist = hash_lookup(lists, mid)))
342 m->name = memberlist->name;
346 else if (type[0] == 'S' && (s = hash_lookup(strings, mid)))
355 EXEC SQL CLOSE m_cursor2;
356 fprintf(stderr, "Loaded %d members\n", cnt);
361 db_error(sqlca.sqlcode);
366 void output_login(int dummy, void *user, void *out)
368 struct user *u = user;
371 if (u->pobox && check_string(u->login) && u->login[0] != '#')
372 fprintf(out, "%s: %s\n", u->login, u->pobox);
375 int line_width, alias_width;
377 void output_mlist(int id, void *list, void *out)
379 struct list *l = list, *l1;
381 int len = strlen(l->name);
383 if (!l->maillist || !check_string(l->name))
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))
390 put_fill(out, l->description);
392 if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
394 fprintf(out, "owner-%s: ", l->name);
395 if ((l1->maillist) && (strcmp(l->name, l1->name)))
396 fprintf(out, "%s\n", l1->name);
399 alias_width = line_width = len + 8;
400 output_membership(l1, out);
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);
407 fprintf(out, "%s: ", l->name);
408 alias_width = line_width = len + 2;
409 output_membership(l, out);
410 fprintf(out, "\n\n");
414 void output_membership(struct list *l, FILE *out)
418 int word_width, linestart = 1;
422 for (m = l->m; m; m = m->next)
424 word_width = strlen(m->name);
426 if (!linestart && alias_width + word_width + 2 > MAX_ALIAS_WIDTH)
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);
434 alias_width = line_width = 17 + word_width;
438 /* First word on line, so we can't wrap. */
439 line_width += word_width;
440 alias_width = line_width;
443 else if (line_width + word_width + 2 > MAX_LINE_WIDTH)
447 alias_width += line_width + word_width + 2;
448 line_width = word_width + 8;
453 line_width += word_width + 2;
457 if (m->list_id && (l1 = hash_lookup(lists, m->list_id)) && !l1->maillist)
458 output_membership(l1, out);
463 fprintf(out, "/dev/null");
466 /* Write a word-wrapped list description to the aliases file as a
468 void put_fill(FILE *aliases, char *string)
474 if (!string || !*string)
476 fputs("# ", aliases);
481 while (*string == ' ')
483 c = strchr(string, ' ');
485 word_width = strlen(string);
488 word_width = c - string;
492 if (line_width + word_width > MAX_LINE_WIDTH)
494 fputs("\n# ", aliases);
496 fputs(string, aliases);
499 fputs(string, aliases);
503 /* add a space after the word */
506 line_width += word_width;
507 string += word_width;
508 /* add another if after a period */
516 fputc('\n', aliases);
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.
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,
544 int check_string(char *s)
551 if (illegalchars[(unsigned) *s])