/* $Id$ * * 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 "util.h" EXEC SQL INCLUDE sqlca; RCSID("$Header$"); 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) FILE *out = stdout; 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 *description; char acl_t; int acl_id; char mailman; char *mailman_server; struct member *m; }; void get_info(void); void save_mlist(int id, void *list, void *force); int check_string(char *s); void output_login(int dummy, void *names, void *out); void output_mlist(int id, struct list *l); void put_fill(FILE *aliases, char *string); void do_people(void); int incount = 0; int main(int argc, char **argv) { time_t tm = time(NULL); char filename[MAXPATHLEN], *targetfile; 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, save_mlist, FALSE); 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], desc[LIST_DESCRIPTION_SIZE]; char type[LIST_ACL_TYPE_SIZE], 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 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 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.description, 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, :desc, :type, :acl, :mailman, :mailman_server; if (sqlca.sqlcode) break; l = malloc(sizeof(struct list)); l->name = strdup(strtrim(lname)); l->maillist = maillistp; l->description = strdup(strtrim(desc)); 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 WHERE direct = 1 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] == 'L' && (memberlist = hash_lookup(lists, mid))) { m->list_id = mid; m->name = memberlist->name; 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 save_mlist(int id, void *list, void *force) { struct member *m; struct list *l = list, *l1; if (l->maillist > 1 || (l->maillist == 0 && !force) || !check_string(l->name)) return; /* If user group appears on list, replace with user. */ if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name)) { l->maillist = 3; return; } l->maillist = 2; output_mlist(id, l); if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id))) save_mlist(0, l1, (void *)TRUE); for (m = l->m; m; m = m->next) { if (m->list_id && (l1 = hash_lookup(lists, m->list_id))) save_mlist(0, l1, (void *)TRUE); } } 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); } static const char *mailman_suffixes[] = { "-admin", "-owner", "-request", "-bounces", "-confirm", "-join", "-leave", "-subscribe", "-unsubscribe", NULL }; void output_mlist(int id, struct list *l) { struct list *l1; struct member *m; struct user *u; int line_width, alias_width, word_width, beginning; static int cont = 1; char str[8]; int i; put_fill(out, l->description); 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%s: ", l->name, l->name, l->mailman_server, l->name); } else if (l->acl_t == 'L' && (l1 = hash_lookup(lists, l->acl_id))) fprintf(out, "owner-%s: %s\n%s: ", l->name, l1->name, l->name); else if (l->acl_t == 'U' && (u = hash_lookup(users, l->acl_id))) fprintf(out, "owner-%s: %s\n%s: ", l->name, u->login, l->name); else fprintf(out, "%s: ", l->name); alias_width = line_width = strlen(l->name) + 2; beginning = 1; for (m = l->m; m; m = m->next) { word_width = strlen(m->name); if (!beginning && 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; fputs(m->name, out); } else if (beginning) { /* Beginning of alias, so don't wrap. */ line_width += word_width; alias_width = line_width; fputs(m->name, out); beginning = 0; } else if (line_width + word_width + 2 > MAX_LINE_WIDTH) { /* Wrap. */ fprintf(out, ",\n\t%s", m->name); alias_width += line_width + word_width + 2; line_width = word_width + 8; } else { /* Continue line. */ line_width += word_width + 2; fprintf(out, ", %s", m->name); } } if (!l->m) fprintf(out, "/dev/null"); fprintf(out, "\n\n"); incount++; } /* Write a word-wrapped list description to the aliases file as a * comment. */ void put_fill(FILE *aliases, char *string) { char *c; int line_width; int word_width; if (!string || !*string) return; fputs("# ", aliases); line_width = 3; while (1) { while (*string == ' ') string++; c = strchr(string, ' '); if (!c) word_width = strlen(string); else { word_width = c - string; *c = '\0'; } if (line_width + word_width > MAX_LINE_WIDTH) { fputs("\n# ", aliases); line_width = 3; fputs(string, aliases); } else fputs(string, aliases); if (!c) break; /* add a space after the word */ fputc(' ', aliases); word_width++; line_width += word_width; string += word_width; /* add another if after a period */ if (*--c == '.') { fputc(' ', aliases); line_width++; } } fputc('\n', aliases); } /* 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; }