/* ** Patched for MS-DOS compatibility by Stephen Trier, March-May, 1990 */ #ifndef lint static char *sccsid = "@(#)alias.c 2.5 (smail) 9/15/87"; #endif #include #include #include #include #include "defs.h" #include extern enum edebug debug; /* verbose and debug modes */ extern char hostdomain[]; extern char hostname[]; extern char *aliasfile; /* ** ** Picture of the alias graph structure ** ** head ** | ** v ** maps -> mark -> gjm -> mel -> NNULL ** | ** v ** sys -> root -> ron -> NNULL ** | ** v ** root -> mark -> chris -> lda -> NNULL ** | ** v ** NNULL */ typedef struct alias_node node; static struct alias_node { char *string; node *horz; node *vert; }; #ifndef SENDMAIL static node aliases = {"", 0, 0}; /* this is the 'dummy header' */ #endif /* not SENDMAIL */ /* ** lint free forms of NULL */ #define NNULL ((node *) 0) #define CNULL ('\0') /* ** string parsing macros */ #define SKIPWORD(Z) while(*Z!=' ' && *Z!='\t' && *Z!='\n' && *Z!=',') Z++; #define SKIPSPACE(Z) while(*Z==' ' || *Z=='\t' || *Z=='\n' || *Z==',') Z++; static int nargc = 0; static char *nargv[MAXARGS]; void add_horz(); void load_alias(), strip_comments(); int recipients(); node *pop(); #ifndef SENDMAIL node *v_search(), *h_search(); char *tilde(); #endif /* not SENDMAIL */ /* our horizontal linked list looks like a stack */ #define push add_horz #define escape(s) ((*s != '\\') ? (s) : (s+1)) char ** alias(pargc, argv) int *pargc; char **argv; { /* ** alias the addresses */ int i; char domain[SMLBUF], ubuf[SMLBUF], *user; node *addr, addrstk; node *flist, fliststk, *u; #ifndef SENDMAIL FILE *fp; node *a; char *home, buf[SMLBUF]; int aliased; struct stat st; #endif /* not SENDMAIL */ #ifdef FULLNAME char *res_fname(); /* Does fullname processing */ #endif addr = &addrstk; flist = &fliststk; user = ubuf; addr->horz = NNULL; flist->horz = NNULL; /* ** push all of the addresses onto a stack */ for(i=0; i < *pargc; i++) { push(addr, argv[i]); } /* ** for each adress, check for included files, aliases, ** full name mapping, and .forward files */ while((nargc < MAXARGS) && ((u = pop(addr)) != NNULL)) { #ifndef SENDMAIL if(strncmpic(u->string, ":include:", 9) == 0) { /* ** make sure it's a full path name ** don't allow multiple sourcing ** of a given include file */ char *p = u->string + 9; if((*p == '/') && (h_search(flist, p) == NULL)) { push(flist, p); if((stat(p, &st) >= 0) &&((st.st_mode & S_IFMT) == S_IFREG) &&((fp = fopen(p, "r")) != NULL)) { while(fgets(buf, sizeof buf, fp)) { (void) recipients(addr, buf); } (void) fclose(fp); } } continue; } #endif /* not SENDMAIL */ /* ** parse the arg to see if it's to be aliased */ if(islocal(u->string, domain, ubuf) == 0) { goto aliasing_complete; } /* ** local form - try to alias user ** aliases file takes precedence over ~user/.forward ** since that's the way that sendmail does it. */ #ifdef LOWERLOGNAME /* squish 'user' into lower case */ for(user = ubuf; *user ; user++) { *user = lower(*user); } #endif user = escape(ubuf); (void) strcpy(u->string, user); /* local => elide domain */ #ifndef SENDMAIL /* ** check for alias - all this complication is necessary ** to handle perverted aliases like these: ** # mail to 's' resolves to 't' 'm' and 'rmt!j' ** s t,g,j,m ** g j,m ** j rmt!j ** # mail to 'a' resolves to 'rmt!d' ** a b c ** b c ** c rmt!d ** # mail to x resolves to 'x' ** x local!x ** # mail to 'y' resolves to 'y' and 'z' ** y \y z */ if(((a = v_search(user)) != NNULL)) { char dtmpb[SMLBUF], utmpb[SMLBUF], *ut; int user_inalias = 0; node *t = a; for(a = a->horz; a != NNULL; a=a->horz) { if(islocal(a->string, dtmpb, utmpb)) { #ifdef LOWERLOGNAME /* squish 'utmpb' into lower case */ for(ut = utmpb; *ut ; ut++) { *ut = lower(*ut); } #endif ut = escape(utmpb); #ifdef CASEALIAS if(strcmp(ut, user) == 0) #else if(strcmpic(ut, user) == 0) #endif { user_inalias = 1; } else { push(addr, a->string); } } else { push(addr, a->string); } } t->horz = NNULL; /* truncate horz list of aliases */ if(user_inalias == 0) { continue; } } if((home = tilde(user)) != NULL) { /* don't allow multiple sourcing ** of a given .forward file */ if((h_search(flist, home) != NULL)) { continue; } push(flist, home); /* ** check for ~user/.forward file ** must be a regular, readable file */ #ifndef MSDOS /* Change by SCT */ (void) sprintf(buf, "%s/%s", home, ".forward"); #else (void) sprintf(buf, "%s/%s", home, "forward"); #endif /* MSDOS */ if((stat(buf, &st) >= 0) &&((st.st_mode & S_IFMT) == S_IFREG) #ifndef MSDOS &&((st.st_mode & 0444) == 0444) #endif &&((fp = fopen(buf, "r")) != NULL)) { aliased = 0; while(fgets(buf, sizeof buf, fp)) { aliased |= recipients(addr, buf); } (void) fclose(fp); if(aliased) { continue; } } } #endif /* not SENDMAIL */ #ifdef FULLNAME /* ** Do possible fullname substitution. */ #ifdef DOT_REQD if (index(user, '.') != NULL) #endif { static char t_dom[SMLBUF], t_unam[SMLBUF]; char *t_user = res_fname(user); if (t_user != NULL) { if(islocal(t_user, t_dom, t_unam) == 0) { /* aliased to non-local address */ push(addr, t_user); continue; } if(strcmp(t_unam, user) != 0) { /* aliased to different local address */ push(addr, t_unam); continue; } } } #endif aliasing_complete: user = escape(u->string); for(i=0; i < nargc; i++) { if(strcmpic(nargv[i], user) == 0) { break; } } if(i == nargc) { nargv[nargc++] = user; } } *pargc = nargc; return(nargv); } #ifndef SENDMAIL /* ** v_search ** given an string, look for its alias in ** the 'vertical' linked list of aliases. */ node * v_search(user) char *user; { node *head; node *a; static int loaded = 0; head = &aliases; if(loaded == 0) { load_alias(head, aliasfile); loaded = 1; } for(a = head->vert; a != NNULL; a = a->vert) { #ifdef CASEALIAS if(strcmp(a->string, user) == 0) #else if(strcmpic(a->string, user) == 0) #endif { break; } } if(a == NNULL) { /* not in graph */ return(NNULL); } return(a); } /* ** h_search ** given an string, look for it in ** a 'horizontal' linked list of strings. */ node * h_search(head, str) node *head; char *str; { node *a; for(a = head->horz; a != NNULL; a = a->horz) { #ifdef CASEALIAS if(strcmp(a->string, str) == 0) #else if(strcmpic(a->string, str) == 0) #endif { break; } } return(a); } #endif /* not SENDMAIL */ /* ** load_alias ** parse an 'aliases' file and add the aliases to the alias graph. ** Handle inclusion of other 'aliases' files. */ void load_alias(head, filename) node *head; char *filename; { FILE *fp; node *v, *h, *add_vert(); char domain[SMLBUF], user[SMLBUF]; char *p, *b, buf[SMLBUF]; if((fp = fopen(filename,"r")) == NULL) { DEBUG("load_alias open('%s') failed\n", filename); return; } while(fgets(buf, sizeof buf, fp) != NULL) { p = buf; if((*p == '#') || (*p == '\n')) { continue; } /* ** include another file of aliases */ if(strncmp(p, ":include:", 9) == 0) { char *nl; p += 9; if((nl = index(p, '\n')) != NULL) { *nl = CNULL; } DEBUG("load_alias '%s' includes file '%s'\n", filename, p); load_alias(head, p); continue; } /* ** if the first char on the line is a space or tab ** then it's a continuation line. Otherwise, ** we start a new alias. */ if(*p != ' ' && *p != '\t') { b = p; SKIPWORD(p); *p++ = CNULL; /* ** be sure that the alias is in local form */ if(islocal(b, domain, user) == 0) { /* ** non-local alias format - skip it */ continue; } /* ** add the alias to the (vertical) list of aliases */ if((h = add_vert(head, user)) == NNULL) { DEBUG("load_alias for '%s' failed\n", b); return; } } /* ** Next on the line is the list of recipents. ** Strip out each word and add it to the ** horizontal linked list. */ (void) recipients(h, p); } (void) fclose(fp); /* ** strip out aliases which have no members */ for(v = head; v->vert != NNULL; ) { if(v->vert->horz == NNULL) { v->vert = v->vert->vert; } else { v = v->vert; } } } /* ** add each word in a string (*p) of recipients ** to the (horizontal) linked list associated with 'h' */ recipients(h, p) node *h; char *p; { char *b, d[SMLBUF], u[SMLBUF]; int ret = 0; strip_comments(p); /* strip out stuff in ()'s */ SKIPSPACE(p); /* skip leading whitespace on line */ while((*p != NULL) && (*p != '#')) { b = p; if(*b == '"') { if((p = index(++b, '"')) == NULL) { /* syntax error - no matching quote */ /* skip the rest of the line */ return(ret); } } else { SKIPWORD(p); } if(*p != CNULL) { *p++ = CNULL; } /* don't allow aliases of the form ** a a */ if((islocal(b, d, u) == 0) || (strcmpic(h->string, u) != 0)) { add_horz(h, b); ret = 1; } SKIPSPACE(p); } return(ret); } /* ** some aliases may have comments on the line like: ** ** moderators moderator@somehost.domain (Moderator's Name) ** moderator@anotherhost.domain (Another Moderator's Name) ** ** strip out the stuff in ()'s ** */ void strip_comments(p) char *p; { char *b; while((p = index(p, '(')) != NULL) { b = p++; /* ** save pointer to open parenthesis */ if((p = index(p, ')')) != NULL) {/* look for close paren */ (void) strcpy(b, ++p); /* slide string left */ } else { *b = CNULL; /* no paren, skip rest of line */ break; } } } /* ** add_vert - add a (vertical) link to the chain of aliases. */ node * add_vert(head, str) node *head; char *str; { char *p, *malloc(); void free(); node *new; /* ** strip colons off the end of alias names */ if((p = index(str, ':')) != NULL) { *p = CNULL; } if((new = (node *) malloc(sizeof(node))) != NNULL) { if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) { free(new); new = NNULL; } else { (void) strcpy(new->string, str); new->vert = head->vert; new->horz = NNULL; head->vert = new; /*DEBUG("add_vert %s->%s\n", head->string, new->string);/* */ } } return(new); } /* ** add_horz - add a (horizontal) link to the chain of recipients. */ void add_horz(head, str) node *head; char *str; { char *malloc(); node *new; if((new = (node *) malloc(sizeof(node))) != NNULL) { if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) { free(new); new = NNULL; } else { (void) strcpy(new->string, str); new->horz = head->horz; new->vert = NNULL; head->horz = new; } /*DEBUG("add_horz %s->%s\n", head->string, new->string);/* */ } } node * pop(head) node *head; { node *ret = NNULL; if(head != NNULL) { ret = head->horz; if(ret != NNULL) { head->horz = ret->horz; } } return(ret); }