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;
59 void save_mlist(int id, void *list, void *force);
60 int check_string(char *s);
61 void output_login(int dummy, void *names, void *out);
62 void output_mlist(int id, struct list *l);
63 void put_fill(FILE *aliases, char *string);
68 int main(int argc, char **argv)
70 time_t tm = time(NULL);
71 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, save_mlist, FALSE);
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, 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 )
165 EXEC SQL OPEN m_cursor;
168 EXEC SQL FETCH m_cursor INTO :id, :mname;
171 if ((s = strchr(mname, '.')))
176 strcat(mname, ".LOCAL");
178 if (hash_store(machines, id, strdup(mname)) < 0)
180 fprintf(stderr, "Out of memory!\n");
185 EXEC SQL CLOSE m_cursor;
187 fprintf(stderr, "Loaded %d machines\n", cnt);
190 strings = create_hash(11001);
192 EXEC SQL DECLARE s_cursor CURSOR FOR
193 SELECT string_id, string
196 EXEC SQL OPEN s_cursor;
199 EXEC SQL FETCH s_cursor INTO :id, :str;
202 if (hash_store(strings, id, strdup(strtrim(str))) < 0)
204 fprintf(stderr, "Out of memory!\n");
209 EXEC SQL CLOSE s_cursor;
211 fprintf(stderr, "Loaded %d strings\n", cnt);
214 users = create_hash(13001);
216 EXEC SQL DECLARE u_cursor CURSOR FOR
217 SELECT users_id, login, potype, pop_id, box_id
221 EXEC SQL OPEN u_cursor;
224 EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :bid;
227 u = malloc(sizeof(struct user));
228 u->login = strdup(strtrim(login));
230 if (potype[0] == 'P' && (s = hash_lookup(machines, pid)))
232 char *buf = malloc(strlen(u->login) + strlen(s) + 2);
233 sprintf(buf, "%s@%s", u->login, s);
236 else if (potype[0] == 'S')
237 u->pobox = hash_lookup(strings, bid);
241 check_string(u->login);
242 if (hash_store(users, id, u) < 0)
244 fprintf(stderr, "Out of memory!\n");
249 EXEC SQL CLOSE u_cursor;
250 fprintf(stderr, "Loaded %d users\n", cnt);
253 lists = create_hash(15000);
255 EXEC SQL DECLARE l_cursor CURSOR FOR
256 SELECT list_id, name, maillist, description, acl_type, acl_id
260 EXEC SQL OPEN l_cursor;
263 EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :desc, :type, :acl;
266 l = malloc(sizeof(struct list));
267 l->name = strdup(strtrim(lname));
268 l->maillist = maillistp;
269 l->description = strdup(strtrim(desc));
273 if (hash_store(lists, id, l) < 0)
275 fprintf(stderr, "Out of memory!\n");
280 EXEC SQL CLOSE l_cursor;
281 fprintf(stderr, "Loaded %d lists\n", cnt);
285 EXEC SQL DECLARE m_cursor2 CURSOR FOR
286 SELECT list_id, member_type, member_id
290 EXEC SQL OPEN m_cursor2;
293 EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid;
297 if ((l = hash_lookup(lists, id)))
299 m = malloc(sizeof(struct member));
300 if (type[0] == 'U' && (u = hash_lookup(users, mid)))
307 else if (type[0] == 'L' && (memberlist = hash_lookup(lists, mid)))
310 m->name = memberlist->name;
314 else if (type[0] == 'S' && (s = hash_lookup(strings, mid)))
323 EXEC SQL CLOSE m_cursor2;
324 fprintf(stderr, "Loaded %d members\n", cnt);
329 db_error(sqlca.sqlcode);
334 void save_mlist(int id, void *list, void *force)
337 struct list *l = list, *l1;
339 if (l->maillist > 1 || (l->maillist == 0 && !force) ||
340 !check_string(l->name))
343 /* If user group appears on list, replace with user. */
344 if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name))
352 if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
353 save_mlist(0, l1, (void *)TRUE);
355 for (m = l->m; m; m = m->next)
357 if (m->list_id && (l1 = hash_lookup(lists, m->list_id)))
358 save_mlist(0, l1, (void *)TRUE);
362 void output_login(int dummy, void *user, void *out)
364 struct user *u = user;
367 if (u->pobox && check_string(u->login) && u->login[0] != '#')
368 fprintf(out, "%s: %s\n", u->login, u->pobox);
371 void output_mlist(int id, struct list *l)
376 int line_width, alias_width, word_width, beginning;
380 put_fill(out, l->description);
381 if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id)))
382 fprintf(out, "owner-%s: %s\n%s: ", l->name, l1->name, l->name);
383 else if (l->acl_t == 'U' && (u = hash_lookup(users, l->acl_id)))
384 fprintf(out, "owner-%s: %s\n%s: ", l->name, u->login, l->name);
386 fprintf(out, "%s: ", l->name);
388 alias_width = line_width = strlen(l->name) + 2;
390 for (m = l->m; m; m = m->next)
392 word_width = strlen(m->name);
394 if (!beginning && alias_width + word_width + 2 > MAX_ALIAS_WIDTH)
396 /* Make a continuation. */
397 sprintf(str, "%c%c%c%c%c%c", rand() % 26 + 97, rand() % 26 + 97,
398 rand() % 26 + 97, rand() % 26 + 97,
399 rand() % 26 + 97, rand() % 26 + 97);
400 fprintf(out, ",\n\tcont%d-%s\ncont%d-%s: ", cont, str, cont, str);
402 alias_width = line_width = 17 + word_width;
407 /* Beginning of alias, so don't wrap. */
408 line_width += word_width;
409 alias_width = line_width;
413 else if (line_width + word_width + 2 > MAX_LINE_WIDTH)
416 fprintf(out, ",\n\t%s", m->name);
417 alias_width += line_width + word_width + 2;
418 line_width = word_width + 8;
423 line_width += word_width + 2;
424 fprintf(out, ", %s", m->name);
428 fprintf(out, "/dev/null");
429 fprintf(out, "\n\n");
433 /* Write a word-wrapped list description to the aliases file as a
435 void put_fill(FILE *aliases, char *string)
441 if (!string || !*string)
443 fputs("# ", aliases);
448 while (*string == ' ')
450 c = strchr(string, ' ');
452 word_width = strlen(string);
455 word_width = c - string;
459 if (line_width + word_width > MAX_LINE_WIDTH)
461 fputs("\n# ", aliases);
463 fputs(string, aliases);
466 fputs(string, aliases);
470 /* add a space after the word */
473 line_width += word_width;
474 string += word_width;
475 /* add another if after a period */
483 fputc('\n', aliases);
487 /* Illegal chars: this should correspond to the array used by
490 static int illegalchars[] = {
491 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */
492 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */
493 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, /* SPACE - / */
494 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */
495 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */
496 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */
497 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */
498 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */
499 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
500 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
501 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
502 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
503 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
504 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
505 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
506 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
509 int check_string(char *s)
516 if (illegalchars[(unsigned) *s])