/* $Id: mailhub.pc 3976 2010-02-09 19:30:24Z zacheiss $ * * This generates the /usr/lib/aliases file for the mailhub. * * (c) Copyright 1988-1998 by the Massachusetts Institute of Technology. * For copying and distribution information, please see the file * . */ #include #include #include #include #include #include #include #include #include #include "util.h" EXEC SQL INCLUDE sqlca; RCSID("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/gen/mailhub.pc $ $Id: mailhub.pc 3976 2010-02-09 19:30:24Z zacheiss $"); char *whoami = "mailhub.gen"; char *db = "moira/moira"; char *divide = "##############################################################"; #define MAX_LINE_WIDTH 72 #define MAX_ALIAS_WIDTH 592 #define FALSE 0 #define TRUE (!FALSE) struct hash *users, *machines, *strings, *lists; struct user { char *login; char *pobox; }; struct member { struct member *next; char *name; int list_id; }; struct list { char *name; char maillist; char acl_t; int acl_id; char mailman; char *mailman_server; struct member *m; }; void get_info(void); int check_string(char *s); void output_login(int dummy, void *names, void *out); void output_mlist(int id, void *list, void *out); void output_membership(struct list *l, FILE *out); void do_people(void); int incount = 0; int main(int argc, char **argv) { time_t tm = time(NULL); char filename[MAXPATHLEN], *targetfile; FILE *out; out = stdout; srand(tm); EXEC SQL CONNECT :db; if (argc == 2) { targetfile = argv[1]; sprintf(filename, "%s~", targetfile); if (!(out = fopen(filename, "w"))) { fprintf(stderr, "unable to open %s for output\n", filename); exit(MR_OCONFIG); } } else if (argc != 1) { fprintf(stderr, "usage: %s [outfile]\n", argv[0]); exit(MR_ARGS); } fprintf(out, "%s\n# Aliases File Extract of %s", divide, ctime(&tm)); fprintf(out, "# This file is automatically generated, " "do not edit it directly.\n%s\n\n", divide); get_info(); EXEC SQL COMMIT; incount = 0; fprintf(out, "\n%s\n# Mailing lists\n%s\n\n", divide, divide); hash_step(lists, output_mlist, out); fprintf(stderr, "Output %d lists\n", incount); incount = 0; fprintf(out, "\n%s\n# People\n%s\n\n", divide, divide); hash_step(users, output_login, out); fprintf(stderr, "Output %d users\n", incount); fprintf(out, "\n%s\n# End of aliases file\n", divide); if (fclose(out)) { perror("close failed"); exit(MR_CCONFIG); } if (argc == 2) fix_file(targetfile); exit(MR_SUCCESS); } void get_info(void) { EXEC SQL BEGIN DECLARE SECTION; int id, pid, iid, bid, eid, cnt, maillistp, acl, mid, mailman; char mname[MACHINE_NAME_SIZE], str[STRINGS_STRING_SIZE]; char login[USERS_LOGIN_SIZE], potype[USERS_POTYPE_SIZE]; char lname[LIST_NAME_SIZE], type[LIST_ACL_TYPE_SIZE]; char mailman_server[MACHINE_NAME_SIZE]; EXEC SQL END DECLARE SECTION; char *s; struct user *u; struct list *l, *memberlist; struct member *m; /* The following is declarative, not executed, * and so is dependent on where it is in the file, * not in the order of execution of statements. */ EXEC SQL WHENEVER SQLERROR GOTO sqlerr; cnt = 0; machines = create_hash(100); EXEC SQL DECLARE m_cursor CURSOR FOR SELECT mach_id, name FROM machine WHERE (status = 1 OR status = 2) AND ( mach_id IN ( SELECT UNIQUE pop_id FROM users ) OR mach_id IN ( SELECT UNIQUE mach_id FROM filesys WHERE type = 'IMAP' ) ) ORDER BY mach_id; EXEC SQL OPEN m_cursor; while (1) { EXEC SQL FETCH m_cursor INTO :id, :mname; if (sqlca.sqlcode) break; if ((s = strchr(mname, '.'))) *s = '\0'; else strtrim(mname); #ifdef ATHENA strcat(mname, ".LOCAL"); #endif if (hash_store(machines, id, strdup(mname)) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; } EXEC SQL CLOSE m_cursor; EXEC SQL DECLARE e_cursor CURSOR FOR SELECT mach_id, name FROM machine WHERE (status = 1 OR status = 2) AND mach_id in (SELECT UNIQUE exchange_id FROM users) ORDER BY mach_id; EXEC SQL OPEN e_cursor; while (1) { EXEC SQL FETCH e_cursor INTO :id, :mname; if (sqlca.sqlcode) break; strtrim(mname); if (hash_store(machines, id, strdup(mname)) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; } EXEC SQL CLOSE e_cursor; fprintf(stderr, "Loaded %d machines\n", cnt); cnt = 0; strings = create_hash(11001); EXEC SQL DECLARE s_cursor CURSOR FOR SELECT string_id, string FROM strings ORDER BY string_id; EXEC SQL OPEN s_cursor; while (1) { EXEC SQL FETCH s_cursor INTO :id, :str; if (sqlca.sqlcode) break; if (hash_store(strings, id, strdup(strtrim(str))) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; } EXEC SQL CLOSE s_cursor; fprintf(stderr, "Loaded %d strings\n", cnt); cnt = 0; users = create_hash(13001); EXEC SQL DECLARE u_cursor CURSOR FOR SELECT users_id, login, potype, pop_id, imap_id, box_id, exchange_id FROM users WHERE status != 3 ORDER BY users_id; EXEC SQL OPEN u_cursor; while (1) { char *saddr = NULL, *paddr = NULL; EXEC SQL FETCH u_cursor INTO :id, :login, :potype, :pid, :iid, :bid, :eid; if (sqlca.sqlcode) break; u = malloc(sizeof(struct user)); u->login = strdup(strtrim(login)); if (!strcmp(strtrim(potype), "NONE")) u->pobox = NULL; else { /* If SMTP or SPLIT, get SMTP address. */ if (potype[0] == 'S') { saddr = hash_lookup(strings, bid); /* If SMTP, clear pid and iid. */ if (potype[1] == 'M') pid = iid = eid = 0; } /* If IMAP, or SPLIT with IMAP, set pid to mach_id. */ if (potype[0] == 'I' || (potype[0] == 'S' && iid)) { EXEC SQL SELECT mach_id INTO :pid FROM filesys WHERE filsys_id = :iid; } /* If EXCHANGE or SPLIT with EXCHANGE, set pid to eid. */ if (potype[0] == 'E' || (potype[0] == 'S' && eid)) pid = eid; if (pid && (s = hash_lookup(machines, pid))) { paddr = malloc(strlen(u->login) + strlen(s) + 2); sprintf(paddr, "%s@%s", u->login, s); } if (paddr && saddr) { u->pobox = malloc(strlen(paddr) + strlen(saddr) + 3); sprintf(u->pobox, "%s, %s", paddr, saddr); free(paddr); } else if (paddr) u->pobox = paddr; else u->pobox = saddr; } check_string(u->login); if (hash_store(users, id, u) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; } EXEC SQL CLOSE u_cursor; fprintf(stderr, "Loaded %d users\n", cnt); cnt = 0; lists = create_hash(15000); EXEC SQL DECLARE l_cursor CURSOR FOR SELECT l.list_id, l.name, l.maillist, l.acl_type, l.acl_id, l.mailman, m.name FROM list l, machine m WHERE active != 0 AND l.mailman_id = m.mach_id ORDER BY list_id; EXEC SQL OPEN l_cursor; while (1) { EXEC SQL FETCH l_cursor INTO :id, :lname, :maillistp, :type, :acl, :mailman, :mailman_server; if (sqlca.sqlcode) break; l = malloc(sizeof(struct list)); l->name = strdup(strtrim(lname)); l->maillist = maillistp; l->acl_t = type[0]; l->acl_id = acl; l->mailman = mailman; l->mailman_server = strdup(strtrim(mailman_server)); l->m = NULL; if (hash_store(lists, id, l) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; } EXEC SQL CLOSE l_cursor; fprintf(stderr, "Loaded %d lists\n", cnt); cnt = 0; EXEC SQL DECLARE m_cursor2 CURSOR FOR SELECT list_id, member_type, member_id FROM imembers ORDER BY list_id; EXEC SQL OPEN m_cursor2; while (1) { EXEC SQL FETCH m_cursor2 INTO :id, :type, :mid; if (sqlca.sqlcode) break; cnt++; if ((l = hash_lookup(lists, id))) { m = malloc(sizeof(struct member)); if (type[0] == 'U' && (u = hash_lookup(users, mid))) { m->list_id = 0; m->name = u->login; m->next = l->m; l->m = m; } else if (type[0] == 'S' && (s = hash_lookup(strings, mid))) { m->list_id = 0; m->next = l->m; l->m = m; m->name = s; } } } EXEC SQL CLOSE m_cursor2; fprintf(stderr, "Loaded %d members\n", cnt); EXEC SQL COMMIT; return; sqlerr: db_error(sqlca.sqlcode); exit(MR_DBMS_ERR); } void output_login(int dummy, void *user, void *out) { struct user *u = user; incount++; if (u->pobox && check_string(u->login) && u->login[0] != '#') fprintf(out, "%s: %s\n", u->login, u->pobox); } int line_width, alias_width; static const char *mailman_suffixes[] = { "-admin", "-owner", "-request", "-bounces", "-confirm", "-join", "-leave", "-subscribe", "-unsubscribe", NULL }; void output_mlist(int id, void *list, void *out) { struct list *l = list, *l1; struct user *u; int len = strlen(l->name), i; if (!l->maillist || !check_string(l->name)) return; /* If standard user group appears on a list, substitute in the user. */ if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name)) return; if (l->mailman && strcmp(l->mailman_server, "[NONE]")) { for (i = 0; mailman_suffixes[i]; i++) fprintf(out, "%s%s: %s%s@%s\n", l->name, mailman_suffixes[i], l->name, mailman_suffixes[i], l->mailman_server); fprintf(out, "owner-%s: %s-owner@%s\n", l->name, l->name, l->mailman_server); } fprintf(out, "%s: ", l->name); alias_width = line_width = len + 2; output_membership(l, out); fprintf(out, "\n\n"); incount++; } void output_membership(struct list *l, FILE *out) { struct member *m; struct list *l1; int word_width, linestart = 1; static int cont = 1; char str[8]; for (m = l->m; m; m = m->next) { word_width = strlen(m->name); if (!linestart && alias_width + word_width + 2 > MAX_ALIAS_WIDTH) { /* Make a continuation. */ sprintf(str, "%c%c%c%c%c%c", rand() % 26 + 97, rand() % 26 + 97, rand() % 26 + 97, rand() % 26 + 97, rand() % 26 + 97, rand() % 26 + 97); fprintf(out, ",\n\tcont%d-%s\ncont%d-%s: ", cont, str, cont, str); cont++; alias_width = line_width = 17 + word_width; } else if (linestart) { /* First word on line, so we can't wrap. */ line_width += word_width; alias_width = line_width; linestart = 0; } else if (line_width + word_width + 2 > MAX_LINE_WIDTH) { /* Wrap. */ fputs(",\n\t", out); alias_width += line_width + word_width + 2; line_width = word_width + 8; } else { /* Continue line. */ line_width += word_width + 2; fputs(", ", out); } if (m->list_id && (l1 = hash_lookup(lists, m->list_id)) && !l1->maillist) output_membership(l1, out); else fputs(m->name, out); } if (!l->m) fprintf(out, "/dev/null"); } /* Illegal chars: this no longer corresponds to the array * in setup_alis. '+' is a valid character in a string on * a list, but is not a valid character in a listname. */ static int illegalchars[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^@ - ^O */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P - ^_ */ 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, /* SPACE - / */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, /* 0 - ? */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* @ - O */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* P - _ */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, /* p - ^? */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }; int check_string(char *s) { for (; *s; s++) { if (isupper(*s)) *s = tolower(*s); if (illegalchars[(unsigned) *s]) return 0; } return 1; }