/* $Header$ * * This generates the /usr/lib/aliases file for the mailhub. * * (c) Copyright 1988 by the Massachusetts Institute of Technology. * For copying and distribution information, please see the file * . */ #include #include #include #include #include #include #include #include #include extern int errno; char *whoami = "mailhub.gen"; char *ingres_date_and_time(); char *perm_malloc(); char *pstrsave(); char *divide = "##############################################################"; #define ML_WID 72 #define AL_MAX_WID 896 #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; ## int flag; ## char *filetime; int ingerr(); IIseterr(ingerr); ## ingres sms ## set lockmode session where level = table if (argc == 2) { if (stat(argv[1], &sb) == 0) { filetime = ingres_date_and_time(sb.st_mtime); ## retrieve (flag = int4(interval("min",tblstats.modtime - filetime))) ## where tblstats.table = "users" 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); ## begin transaction get_info(); ## end transaction ## exit 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); } /* * ingerr: (supposedly) called when Ingres indicates an error. * I have not yet been able to get this to work to intercept a * database open error. */ #define INGRES_DEADLOCK 4700 static int ingerr(num) int *num; { char buf[256]; int ingres_errno; switch (*num) { case INGRES_DEADLOCK: ingres_errno = MR_DEADLOCK; break; default: ingres_errno = MR_INGRES_ERR; } com_err(whoami, MR_INGRES_ERR, " code %d\n", *num); critical_alert("DCM", "Alias build encountered INGRES ERROR %d", *num); exit(ingres_errno); } 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 acl_t; int acl_id; struct member *m; }; struct names { char *name; struct names *next; int keep:1; int id:31; }; get_info() ##{ ## int id, pid, bid, stat, cnt, maillistp, acl, mid; ## char name[129], type[9], fname[17], mname[17], lname[17], buf[257]; char *s; register struct user *u; struct list *l, *memberlist; register struct member *m; /* get locks */ ## retrieve (buf = users.modtime) where users.users_id = 0 ## retrieve (buf = list.modtime) where list.list_id = 0 cnt = 0; machines = create_hash(10); ## retrieve (id = machine.mach_id, name = machine.#name) ## where machine.mach_id = users.pop_id { if (s = index(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++; ## } fprintf(stderr, "Loaded %d machines\n", cnt); cnt = 0; strings = create_hash(4000); ## retrieve (id = strings.string_id, name = strings.string) { if (hash_store(strings, id, pstrsave(strtrim(name))) < 0) { fprintf(stderr, "Out of memory!\n"); exit(MR_NO_MEM); } cnt++; ## } fprintf(stderr, "Loaded %d strings\n", cnt); cnt = 0; users = create_hash(12001); ## range of u is users ## retrieve (id = u.users_id, name = u.login, fname = u.first, ## mname = u.middle, lname = u.last, ## type = u.potype, pid = u.pop_id, bid = u.box_id) ## where u.status != 3 { 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++; ## } fprintf(stderr, "Loaded %d users\n", cnt); cnt = 0; lists = create_hash(15000); ## range of l is list ## retrieve (id = l.list_id, name = l.#name, maillistp = l.maillist, ## type = l.acl_type, acl = l.acl_id) ## where l.active != 0 { l = (struct list *) perm_malloc(sizeof(struct list)); l->name = pstrsave(strtrim(name)); l->maillist = maillistp; 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++; ## } fprintf(stderr, "Loaded %d lists\n", cnt); cnt = 0; ## range of m is imembers ## retrieve (id = m.list_id, type = m.member_type, mid = m.member_id) ## where m.direct = 1 { 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; } } ## } fprintf(stderr, "Loaded %d members\n", cnt); ##} 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; register int count; register struct idblock *ra; 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 named: %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, 1, 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; 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; wwid = strlen(s); if (!bol && awid + wwid + 2 > AL_MAX_WID) { fprintf(out, ",\n\tcontinuation-%d\ncontinuation-%d: ", cont, cont); 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); } 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 + 1)) & ~1); 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) bcopy(s, p, 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); } bzero(h->data, 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); } } }