/* $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 EXEC SQL INCLUDE sqlca; extern int errno; 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; main(argc, argv) 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")) == NULL) { 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() { 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; register struct user *u; struct list *l, *memberlist; register 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 != 0) 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 != 0) 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 != 0) 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 = (char *) 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 != 0) 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 = (struct member *) 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 != 0) 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(id, l, force) int id; struct list *l; int force; { register struct member *m; register 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(id, u, dummy) int id; struct user *u; int dummy; { if (u->pobox && u->login[0] != '#') insert_name(u->login, id, TRUE, FALSE); } void insert_names(id, u, dummy) 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); /* sprintf(buffer, "%c_%s", u->first[0], u->last); insert_name(buffer, id, FALSE, TRUE); */ 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(s, id, nodups, copy) char *s; int id; int nodups; int copy; { int code; register struct names *ns; incount++; code = hashstr(s); ns = (struct names *) hash_lookup(names, code); if (ns == NULL) { if ((ns = (struct names *) perm_malloc(sizeof(struct names))) == NULL) { 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))) == NULL) { 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(s) register char *s; { register int result; register int c; for (result = 0; c = *s; s++) { if (illegalchars[c]) { register char *p; for (p = s; *p; p++) *p = p[1]; continue; } if (isupper(c)) c = *s = tolower(c); /* result = result * 31 + *s; */ result = (result << 5) - result + c - '`'; } return(result < 0 ? -result : result); } sort_info() { 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(dummy, nms, out) int dummy; struct names *nms; FILE *out; { register struct names *ns; register struct user *u; incount++; for (ns = nms; ns; ns = ns->next) { if (ns->name[0] == 0 || ns->name[1] == 0) { 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(id, l) int id; register struct list *l; { struct list *l1; register struct member *m; register 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 == (struct member *)NULL) fprintf(out, "/dev/null"); fprintf(out, "\n\n"); incount++; } /* print out strings separated by commas, doing line breaks as appropriate */ do_member(out, s) FILE *out; register char *s; { register 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(aliases, string) FILE *aliases; register char *string; { register char *c; register int lwid; register int wwid; if (string == 0 || *string == 0) return; fputs("# ", aliases); lwid = 3; while (1) { while (*string && *string == ' ') string++; c = (char *)strchr(string, ' '); if (c == 0) { 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 == (char *)0) break; /* add a space after the word */ (void) fputc(' ', aliases); wwid++; lwid += wwid; string += wwid; /* add another if after a period */ if (*--c == '.') { (void) fputc(' ', aliases); lwid++; } } (void) fputc('\n', aliases); } do_people() { 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(size) unsigned size; { static char *pool = NULL; static unsigned pool_size = 0; register char *ret; if (size > pool_size) { pool = (char *) 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(s) char *s; { register int len; register 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(size) int size; { struct hash *h; h = (struct hash *) perm_malloc(sizeof(struct hash)); if (h == (struct hash *) NULL) return((struct hash *) NULL); h->size = size; h->data = (struct bucket **) perm_malloc(size * sizeof(char *)); if (h->data == (struct bucket **) NULL) { free(h); return((struct hash *) 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(h, key) struct hash *h; register int key; { register 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(h, key, value) struct hash *h; register int key; char *value; { register 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(h, key, value) struct hash *h; register int key; char *value; { register struct bucket *b, **p; p = &(h->data[hash_func(h, key)]); if (*p == NULL) { b = *p = (struct bucket *) perm_malloc(sizeof(struct bucket)); if (b == (struct bucket *) NULL) 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 == (struct bucket *) NULL) 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(h, value, callback) struct hash *h; register char *value; void (*callback)(); { register 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(h, callback, hint) struct hash *h; void (*callback)(); char *hint; { register 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); } } } /* Deallocate all of the memory associated with a table */ hash_destroy(h) struct hash *h; { register struct bucket *b, **p, *b1; for (p = &(h->data[h->size - 1]); p >= h->data; p--) { for (b = *p; b; b = b1) { b1 = b->next; free(b); } } }