/* $Header$ * * This generates the /usr/lib/aliases file for the mailhub. * * (c) Copyright 1988, 1990 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 #include EXEC SQL INCLUDE sqlca; char *whoami = "mailhub.gen"; char *db = "moira/moira"; char *perm_malloc(); char *pstrsave(); char *divide = "##############################################################"; #define ML_WID 72 #define AL_MAX_WID 592 #define FALSE 0 #define TRUE (!FALSE) FILE *out = stdout; 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); } 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; }; 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, pstrsave(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, pstrsave(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 = (struct user *) perm_malloc(sizeof(struct user)); u->login = pstrsave(strtrim(name)); u->first = pstrsave(strtrim(fname)); u->last = pstrsave(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 = pstrsave(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 = (struct list *) perm_malloc(sizeof(struct list)); l->name = pstrsave(strtrim(name)); l->maillist = maillistp; l->description = pstrsave(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 = (struct list *) hash_lookup(lists, id))) { m = (struct member *) perm_malloc(sizeof(struct member)); if (type[0] == 'U' && (u = (struct user *) 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 = (struct list *) 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); } save_mlist(int id, struct list *l, int force) { struct member *m; struct 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 = (struct list *)hash_lookup(lists, l->acl_id))) save_mlist(0, l1, TRUE); for (m = l->m; m; m = m->next) { if (m->list_id && (l1 = (struct list *)hash_lookup(lists, m->list_id))) save_mlist(0, l1, TRUE); } } insert_login(int id, struct user *u, int dummy) { if (u->pobox && u->login[0] != '#') insert_name(u->login, id, TRUE, FALSE); } void insert_names(int id, struct user *u, int dummy) { char buffer[256]; 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; insert_name(char *s, int id, int nodups, int copy) { int code; struct names *ns; incount++; code = hashstr(s); ns = (struct names *) hash_lookup(names, code); if (!ns) { if (!(ns = (struct names *) perm_malloc(sizeof(struct names)))) { fprintf(stderr, "ran out of memory inserting name (sorting)\n"); exit(MR_NO_MEM); } if (copy) ns->name = pstrsave(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 = (struct names *) perm_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 = pstrsave(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; } 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); hash_step(users, insert_names, NULL); fprintf(stderr, "Inserted %d names\n", incount); } output_data(int dummy, struct names *nms, FILE *out) { struct names *ns; 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 = (struct user *) hash_lookup(users, ns->id); if (u->pobox) fprintf(out, "%s: %s\n", ns->name, u->pobox); else fprintf(out, "%s: =%s=@nobox\n", ns->name, ns->name); } else if (ns->id == 0) fprintf(out, "%s: =%s=@ambig\n", ns->name, ns->name); } } int lwid, bol, awid; 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 = (struct list *) 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 = (struct user *) 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 */ do_member(FILE *out, char *s) { 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); } 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); } 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); } #define chunk_size 102400 char *perm_malloc(unsigned size) { static char *pool = NULL; static unsigned pool_size = 0; char *ret; if (size > pool_size) { pool = malloc(chunk_size); pool_size = chunk_size; } ret = pool; pool += size; pool = (char *)(((unsigned) (pool + 3)) & ~3); pool_size -= (pool - ret); return ret; } /* * Make a (permenant) copy of a string. */ char *pstrsave(char *s) { int len; char *p; /* Kludge for sloppy string semantics */ if (!s) { printf("NULL != \"\" !!!!\r\n"); p = perm_malloc(1); *p = '\0'; return p; } len = strlen(s) + 1; p = perm_malloc((u_int)len); if (p) memcpy(p, s, len); return p; } #define hash_func(h, key) (key >= 0 ? (key % h->size) : (-key % h->size)) /* Create a hash table. The size is just a hint, not a maximum. */ struct hash *create_hash(int size) { struct hash *h; h = (struct hash *) perm_malloc(sizeof(struct hash)); if (!h) return NULL; h->size = size; h->data = (struct bucket **) perm_malloc(size * sizeof(char *)); if (!h->data) return NULL; memset(h->data, 0, size * sizeof(char *)); return h; } /* Lookup an object in the hash table. Returns the value associated with * the key, or NULL (thus NULL is not a very good value to store...) */ char *hash_lookup(struct hash *h, int key) { struct bucket *b; b = h->data[hash_func(h, key)]; while (b && b->key != key) b = b->next; if (b && b->key == key) return b->data; else return NULL; } /* Update an existing object in the hash table. Returns 1 if the object * existed, or 0 if not. */ int hash_update(struct hash *h, int key, char *value) { struct bucket *b; b = h->data[hash_func(h, key)]; while (b && b->key != key) b = b->next; if (b && b->key == key) { b->data = value; return 1; } else return 0; } /* Store an item in the hash table. Returns 0 if the key was not previously * there, 1 if it was, or -1 if we ran out of memory. */ int hash_store(struct hash *h, int key, char *value) { struct bucket *b, **p; p = &(h->data[hash_func(h, key)]); if (!*p) { b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket)); if (!b) return -1; b->next = NULL; b->key = key; b->data = value; return 0; } for (b = *p; b && b->key != key; b = *p) p = (struct bucket **) *p; if (b && b->key == key) { b->data = value; return 1; } b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket)); if (!b) return -1; b->next = NULL; b->key = key; b->data = value; return 0; } /* Search through the hash table for a given value. For each piece of * data with that value, call the callback proc with the corresponding key. */ hash_search(struct hash *h, char *value, void (*callback)()) { struct bucket *b, **p; for (p = &(h->data[h->size - 1]); p >= h->data; p--) { for (b = *p; b; b = b->next) { if (b->data == value) (*callback)(b->key); } } } /* Step through the hash table, calling the callback proc with each key. */ hash_step(struct hash *h, void (*callback)(), char *hint) { struct bucket *b, **p; for (p = &(h->data[h->size - 1]); p >= h->data; p--) { for (b = *p; b; b = b->next) (*callback)(b->key, b->data, hint); } }