/* $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 ML_WID 72 #define AL_MAX_WID 592 #define FALSE 0 #define TRUE (!FALSE) FILE *out = stdout; struct hash *users, *machines, *strings, *lists, *names; struct user { char *login; char *first; char *last; char mi; 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; struct member *m; }; struct names { char *name; struct names *next; int keep; int id; }; void get_info(void); void save_mlist(int id, void *list, void *force); void insert_login(int id, void *user, void *hint); void insert_names(int id, void *user, void *hint); void insert_name(char *s, int id, int nodups, int copy); int hashstr(char *s); void sort_info(void); void output_data(int dummy, void *names, void *out); void output_mlist(int id, struct list *l); void do_member(FILE *out, char *s); void put_fill(FILE *aliases, char *string); void do_people(void); int main(int argc, char **argv) { long tm = time(NULL); char filename[64], *targetfile; struct stat sb; EXEC SQL BEGIN DECLARE SECTION; int flag; EXEC SQL END DECLARE SECTION; EXEC SQL CONNECT :db; if (argc == 2) { if (stat(argv[1], &sb) == 0) { if (ModDiff (&flag, "users", sb.st_mtime)) exit(MR_DATE); if (flag < 0) { fprintf(stderr, "File %s does not need to be rebuilt.\n", argv[1]); exit(MR_NO_CHANGE); } } 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; fprintf(stderr, "Sorting Info\n"); sort_info(); fprintf(stderr, "Dumping information\n"); do_people(); 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, bid, cnt, maillistp, acl, mid; char name[129], type[9], fname[17], mname[17], lname[17], buf[257]; 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(1000); EXEC SQL DECLARE m_cursor CURSOR FOR SELECT mach_id, name FROM machine WHERE status = 1 ORDER BY mach_id; EXEC SQL OPEN m_cursor; while (1) { EXEC SQL FETCH m_cursor INTO :id, :name; if (sqlca.sqlcode) break; if ((s = strchr(name, '.'))) *s = '\0'; else strtrim(name); #ifdef ATHENA strcat(name, ".LOCAL"); #endif if (hash_store(machines, id, strdup(name)) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; } EXEC SQL CLOSE m_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, :name; if (sqlca.sqlcode) break; if (hash_store(strings, id, strdup(strtrim(name))) < 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, first, middle, last, potype, pop_id, box_id FROM users WHERE status != 3 ORDER BY users_id; EXEC SQL OPEN u_cursor; while (1) { EXEC SQL FETCH u_cursor INTO :id, :name, :fname, :mname, :lname, :type, :pid, :bid; if (sqlca.sqlcode) break; u = malloc(sizeof(struct user)); u->login = strdup(strtrim(name)); u->first = strdup(strtrim(fname)); u->last = strdup(strtrim(lname)); if (mname[0] != ' ') u->mi = mname[0]; else u->mi = 0; if (type[0] == 'P' && (s = hash_lookup(machines, pid))) { sprintf(buf, "%s@%s", u->login, s); u->pobox = strdup(buf); } else if (type[0] == 'S') u->pobox = hash_lookup(strings, bid); else u->pobox = NULL; 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 list_id, name, maillist, description, acl_type, acl_id FROM list WHERE active != 0 ORDER BY list_id; EXEC SQL OPEN l_cursor; while (1) { EXEC SQL FETCH l_cursor INTO :id, :name, :maillistp, :buf, :type, :acl; if (sqlca.sqlcode) break; l = malloc(sizeof(struct list)); l->name = strdup(strtrim(name)); l->maillist = maillistp; l->description = strdup(strtrim(buf)); l->acl_t = type[0]; l->acl_id = acl; 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)) return; if (l->m && l->m->next == NULL && !strcasecmp(l->name, l->m->name)) { l->maillist = 3; return; } l->maillist = 2; insert_name(l->name, -1, TRUE, FALSE); 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 insert_login(int id, void *user, void *hint) { struct user *u = user; if (u->pobox && u->login[0] != '#') insert_name(u->login, id, TRUE, FALSE); } void insert_names(int id, void *user, void *hint) { char buffer[256]; struct user *u = user; insert_name(u->last, id, FALSE, FALSE); sprintf(buffer, "%s_%s", u->first, u->last); insert_name(buffer, id, FALSE, TRUE); #if 0 sprintf(buffer, "%c_%s", u->first[0], u->last); insert_name(buffer, id, FALSE, TRUE); #endif if (u->mi) { sprintf(buffer, "%s_%c_%s", u->first, u->mi, u->last); insert_name(buffer, id, FALSE, TRUE); } } int incount = 0; void insert_name(char *s, int id, int nodups, int copy) { int code; struct names *ns; incount++; code = hashstr(s); ns = hash_lookup(names, code); if (!ns) { if (!(ns = malloc(sizeof(struct names)))) { fprintf(stderr, "ran out of memory inserting name (sorting)\n"); exit(MR_NO_MEM); } if (copy) ns->name = strdup(s); else ns->name = s; ns->keep = nodups; ns->id = id; ns->next = NULL; if (hash_store(names, code, ns) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } return; } if (strcasecmp(ns->name, s)) { while (ns->next) { ns = ns->next; if (!strcasecmp(ns->name, s)) goto foundns; } if (!(ns->next = malloc(sizeof(struct names)))) { fprintf(stderr, "ran out of memory insterting name (sorting)\n"); exit(MR_NO_MEM); } ns = ns->next; if (copy) ns->name = strdup(s); else ns->name = s; ns->keep = nodups; ns->id = id; ns->next = NULL; return; } foundns: if (nodups || ns->keep) { if (nodups && ns->keep) fprintf(stderr, "duplicated name: %s\n", s); return; } ns->id = 0; } /* Illegal chars: ! " % ( ) , / : ; < = > @ [ \ ] ^ { | } */ 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 - ^_ */ 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, /* SPACE - / */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, /* 0 - ? */ 1, 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, 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, }; /* While hashing the string, punt any illegal characters */ int hashstr(char *s) { int result; int c; for (result = 0; (c = *s); s++) { if (illegalchars[c]) { char *p; for (p = s; *p; p++) *p = p[1]; continue; } if (isupper(c)) c = *s = tolower(c); result = (result << 5) - result + c - '`'; } return result < 0 ? -result : result; } void sort_info(void) { names = create_hash(20001); hash_step(users, insert_login, NULL); incount = 0; fprintf(out, "\n%s\n# Mailing lists\n%s\n", divide, divide); hash_step(lists, save_mlist, FALSE); fprintf(stderr, "Output %d lists\n", incount); #ifdef NAME_ALIASES hash_step(users, insert_names, NULL); fprintf(stderr, "Inserted %d names\n", incount); #endif } void output_data(int dummy, void *names, void *out) { struct names *ns, *nms = names; struct user *u; incount++; for (ns = nms; ns; ns = ns->next) { if (!ns->name[0] || !ns->name[1]) { fprintf(stderr, "punting %s due to short name\n", ns->name); continue; } if (ns->id > 0) { u = hash_lookup(users, ns->id); if (u->pobox) fprintf(out, "%s: %s\n", ns->name, u->pobox); #ifdef NAME_ALIASES else fprintf(out, "%s: =%s=@nobox\n", ns->name, ns->name); #endif } #ifdef NAME_ALIASES else if (ns->id == 0) fprintf(out, "%s: =%s=@ambig\n", ns->name, ns->name); #endif } } int lwid, bol, awid; void output_mlist(int id, struct list *l) { struct list *l1; struct member *m; struct user *u; put_fill(out, l->description); 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); lwid = strlen(l->name) + 2; bol = 1; for (m = l->m; m; m = m->next) do_member(out, m->name); if (!l->m) fprintf(out, "/dev/null"); fprintf(out, "\n\n"); incount++; } /* print out strings separated by commas, doing line breaks as appropriate */ void do_member(FILE *out, char *s) { int wwid; static int cont = 1; char str[8]; wwid = strlen(s); if (!bol && awid + wwid + 2 > AL_MAX_WID) { 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); str[6] = '\0'; fprintf(out, ",\n\tcont%d-%s\ncont%d-%s: ", cont, str, cont, str); cont++; awid = lwid = 17 + wwid; fputs(s, out); return; } if (bol) { lwid += wwid; awid = lwid; fputs(s, out); bol = 0; return; } if (lwid + wwid + 2 > ML_WID) { fprintf(out, ",\n\t%s", s); awid += lwid + wwid + 2; lwid = wwid + 8; return; } lwid += wwid + 2; fprintf(out, ", %s", s); } void put_fill(FILE *aliases, char *string) { char *c; int lwid; int wwid; if (!string || !*string) return; fputs("# ", aliases); lwid = 3; while (1) { while (*string == ' ') string++; c = strchr(string, ' '); if (!c) wwid = strlen(string); else { wwid = c - string; *c = '\0'; } if ((lwid + wwid) > ML_WID) { fputs("\n# ", aliases); lwid = 3; fputs(string, aliases); } else fputs(string, aliases); if (!c) break; /* add a space after the word */ fputc(' ', aliases); wwid++; lwid += wwid; string += wwid; /* add another if after a period */ if (*--c == '.') { fputc(' ', aliases); lwid++; } } fputc('\n', aliases); } void do_people(void) { incount = 0; fprintf(out, "\n%s\n# People\n%s\n", divide, divide); hash_step(names, output_data, out); fprintf(stderr, "Output %d entries\n", incount); }