|
|
1.1 root 1: #ifndef lint
2: static char *sccsid = "@(#)alias.c 2.5 (smail) 9/15/87";
3: #endif
4:
5: #include <stdio.h>
6: #include <sys/types.h>
7: #include <sys/stat.h>
8: #include <pwd.h>
9: #include "defs.h"
10: #include <ctype.h>
11:
12: extern enum edebug debug; /* verbose and debug modes */
13: extern char hostdomain[];
14: extern char hostname[];
15: extern char *aliasfile;
16: #ifdef HOMEALIASES
17: extern char *homealias;
18: #endif /* HOMEALIASES */
19:
20: /*
21: **
22: ** Picture of the alias graph structure
23: **
24: ** head
25: ** |
26: ** v
27: ** maps -> mark -> gjm -> mel -> NNULL
28: ** |
29: ** v
30: ** sys -> root -> ron -> NNULL
31: ** |
32: ** v
33: ** root -> mark -> chris -> lda -> NNULL
34: ** |
35: ** v
36: ** NNULL
37: */
38:
39: typedef struct alias_node node;
40:
41: static struct alias_node {
42: char *string;
43: node *horz;
44: node *vert;
45: };
46:
47: #ifndef SENDMAIL
48: static node aliases = {"", 0, 0}; /* this is the 'dummy header' */
49: #endif /* not SENDMAIL */
50:
51: /*
52: ** lint free forms of NULL
53: */
54:
55: #define NNULL ((node *) 0)
56: #define CNULL ('\0')
57:
58: #define TRUE ((int) (1==1))
59: #define FALSE ((int) (1==2))
60: /*
61: ** string parsing macros
62: */
63: #define SKIPWORD(Z) while(*Z!=' ' && *Z!='\t' && *Z!='\n' && *Z!=',') Z++;
64: #define SKIPSPACE(Z) while(*Z==' ' || *Z=='\t' || *Z=='\n' || *Z==',') Z++;
65:
66: static int nargc = 0;
67: static char *nargv[MAXARGS];
68: static int no_backslash = TRUE;
69: static int last_no_backslash = TRUE;
70:
71: void add_horz();
72: void load_alias(), strip_comments();
73: int recipients();
74: node *pop();
75: #ifndef SENDMAIL
76: node *v_search(), *h_search();
77: char *tilde();
78: #endif /* not SENDMAIL */
79:
80: /* our horizontal linked list looks like a stack */
81: #define push add_horz
82:
83: #define escape(s) ((*s != '\\') ? (s) : (s+1))
84:
85: char **
86: alias(pargc, argv)
87: int *pargc;
88: char **argv;
89: {
90: /*
91: ** alias the addresses
92: */
93: int i;
94: char domain[SMLBUF], ubuf[SMLBUF], *user;
95: node *addr, addrstk;
96: node *flist, fliststk, *u;
97:
98: #ifndef SENDMAIL
99: FILE *fp;
100: node *a;
101: char *home, buf[SMLBUF];
102: int aliased;
103: struct stat st;
104: #endif /* not SENDMAIL */
105:
106: #ifdef FULLNAME
107: char *res_fname(); /* Does fullname processing */
108: #endif
109:
110: addr = &addrstk;
111: flist = &fliststk;
112: user = ubuf;
113:
114: addr->horz = NNULL;
115: flist->horz = NNULL;
116:
117: /*
118: ** push all of the addresses onto a stack
119: */
120: for(i=0; i < *pargc; i++) {
121: push(addr, argv[i]);
122: }
123:
124: /*
125: ** for each adress, check for included files, aliases,
126: ** full name mapping, and .forward files
127: */
128:
129: while((nargc < MAXARGS) && ((u = pop(addr)) != NNULL)) {
130: #ifndef SENDMAIL
131: if(strncmpic(u->string, ":include:", 9) == 0) {
132: /*
133: ** make sure it's a full path name
134: ** don't allow multiple sourcing
135: ** of a given include file
136: */
137: char *p = u->string + 9;
138:
139: if((*p == '/')
140: && (h_search(flist, p) == NULL)) {
141: push(flist, p);
142: if((stat(p, &st) >= 0)
143: &&((st.st_mode & S_IFMT) == S_IFREG)
144: &&((fp = fopen(p, "r")) != NULL)) {
145: while(fgets(buf, sizeof buf, fp)) {
146: (void) recipients(addr, buf);
147: }
148: (void) fclose(fp);
149: }
150: }
151: continue;
152: }
153: #endif /* not SENDMAIL */
154: /*
155: ** parse the arg to see if it's to be aliased
156: */
157:
158: if(islocal(u->string, domain, ubuf) == 0) {
159: goto aliasing_complete;
160: }
161:
162: /*
163: ** local form - try to alias user
164: ** aliases file takes precedence over ~user/.forward
165: ** since that's the way that sendmail does it.
166: */
167:
168: #ifdef LOWERLOGNAME
169: /* squish 'user' into lower case */
170: for(user = ubuf; *user ; user++) {
171: *user = lower(*user);
172: }
173: #endif
174: user = escape(ubuf);
175:
176: (void) strcpy(u->string, user); /* local => elide domain */
177: #ifndef SENDMAIL
178: /*
179: ** check for alias - all this complication is necessary
180: ** to handle perverted aliases like these:
181: ** # mail to 's' resolves to 't' 'm' and 'rmt!j'
182: ** s t,g,j,m
183: ** g j,m
184: ** j rmt!j
185: ** # mail to 'a' resolves to 'rmt!d'
186: ** a b c
187: ** b c
188: ** c rmt!d
189: ** # mail to x resolves to 'x'
190: ** x local!x
191: ** # mail to 'y' resolves to 'y' and 'z'
192: ** y \y z
193: */
194: if(((a = v_search(user)) != NNULL)) {
195: char dtmpb[SMLBUF], utmpb[SMLBUF], *ut;
196: int user_inalias = 0;
197: node *t = a;
198:
199: for(a = a->horz; a != NNULL; a=a->horz) {
200: if(islocal(a->string, dtmpb, utmpb)) {
201: #ifdef LOWERLOGNAME
202: /* squish 'utmpb' into lower case */
203: for(ut = utmpb; *ut ; ut++) {
204: *ut = lower(*ut);
205: }
206: #endif
207:
208: ut = escape(utmpb);
209: #ifdef CASEALIAS
210: if(strcmp(ut, user) == 0)
211: #else
212: if(strcmpic(ut, user) == 0)
213: #endif
214: {
215: user_inalias = 1;
216: } else {
217: push(addr, a->string);
218: }
219: } else {
220: push(addr, a->string);
221: }
222: }
223: t->horz = NNULL; /* truncate horz list of aliases */
224: if(user_inalias == 0) {
225: continue;
226: }
227: }
228:
229: if((home = tilde(user)) != NULL) {
230: /* don't allow multiple sourcing
231: ** of a given .forward file
232: */
233:
234: if((h_search(flist, home) != NULL)) {
235: continue;
236: }
237: push(flist, home);
238:
239: /*
240: ** check for ~user/.forward file
241: ** must be a regular, readable file
242: */
243:
244: (void) sprintf(buf, "%s/%s", home, ".forward");
245: if((stat(buf, &st) >= 0)
246: &&((st.st_mode & S_IFMT) == S_IFREG)
247: &&((st.st_mode & 0444) == 0444)
248: &&((fp = fopen(buf, "r")) != NULL)) {
249: aliased = 0;
250: while(fgets(buf, sizeof buf, fp)) {
251: aliased |= recipients(addr, buf);
252: }
253: (void) fclose(fp);
254: if(aliased) {
255: continue;
256: }
257: }
258: }
259: #endif /* not SENDMAIL */
260:
261: #ifdef FULLNAME
262: /*
263: ** Do possible fullname substitution.
264: */
265: #ifdef DOT_REQD
266: if (index(user, '.') != NULL)
267: #endif
268: {
269: static char t_dom[SMLBUF], t_unam[SMLBUF];
270: char *t_user = res_fname(user);
271: if (t_user != NULL) {
272: if(islocal(t_user, t_dom, t_unam) == 0) {
273: /* aliased to non-local address */
274: push(addr, t_user);
275: continue;
276: }
277: if(strcmp(t_unam, user) != 0) {
278: /* aliased to different local address */
279: push(addr, t_unam);
280: continue;
281: }
282: }
283: }
284: #endif
285:
286: aliasing_complete:
287: user = escape(u->string);
288: for(i=0; i < nargc; i++) {
289: #ifdef CASEALIAS
290: if(strcmp(nargv[i], user) == 0) {
291: #else
292: if(strcmpic(nargv[i], user) == 0) {
293: #endif
294: break;
295: }
296: }
297:
298: if(i == nargc) {
299: nargv[nargc++] = user;
300: }
301: }
302: *pargc = nargc;
303: return(nargv);
304: }
305:
306: #ifndef SENDMAIL
307: /*
308: ** v_search
309: ** given an string, look for its alias in
310: ** the 'vertical' linked list of aliases.
311: */
312: node *
313: v_search(user)
314: char *user;
315: {
316: node *head;
317: node *a;
318: static int loaded = 0;
319:
320: head = &aliases;
321: if(loaded == 0) {
322: load_alias(head, aliasfile);
323: #ifdef HOMEALIASES
324: /* Load $HOME/.aliases if there is one. */
325: /* NB: Security problem if aliases can pipe to programs. */
326: if (( homealias = getenv("HOME") ) != NULL ) {
327: FILE *fp;
328:
329: strcat(homealias, HOMEALIASES);
330: /* This fopen is just an existence and perm check. */
331: if((fp = fopen(homealias,"r")) != NULL) {
332: fclose(fp);
333: load_alias(head, homealias);
334: } else {
335: fclose(fp);
336: }
337: }
338: #endif
339: loaded = 1;
340: }
341:
342: for(a = head->vert; a != NNULL; a = a->vert) {
343: #ifdef CASEALIAS
344: if(strcmp(a->string, user) == 0)
345: #else
346: if(strcmpic(a->string, user) == 0)
347: #endif
348: {
349: break;
350: }
351: }
352: if(a == NNULL) { /* not in graph */
353: return(NNULL);
354: }
355: return(a);
356: }
357:
358: /*
359: ** h_search
360: ** given an string, look for it in
361: ** a 'horizontal' linked list of strings.
362: */
363: node *
364: h_search(head, str)
365: node *head;
366: char *str;
367: {
368: node *a;
369: for(a = head->horz; a != NNULL; a = a->horz) {
370: #ifdef CASEALIAS
371: if(strcmp(a->string, str) == 0)
372: #else
373: if(strcmpic(a->string, str) == 0)
374: #endif
375: {
376: break;
377: }
378: }
379: return(a);
380: }
381: #endif /* not SENDMAIL */
382:
383: /*
384: ** load_alias
385: ** parse an 'aliases' file and add the aliases to the alias graph.
386: ** Handle inclusion of other 'aliases' files.
387: */
388:
389: void
390: load_alias(head, filename)
391: node *head;
392: char *filename;
393: {
394: FILE *fp;
395: node *v, *h, *add_vert();
396: char domain[SMLBUF], user[SMLBUF];
397: char *p, *b, buf[SMLBUF];
398:
399: if((fp = fopen(filename,"r")) == NULL) {
400: DEBUG("load_alias open('%s') failed\n", filename);
401: return;
402: }
403:
404: while(fgets(buf, sizeof buf, fp) != NULL) {
405: p = buf;
406:
407: /* Check for trailing \, mark for later. */
408: no_backslash = last_no_backslash;
409: if(strlen(p) > 0) {
410: if(p[strlen(p) - 2 ] == '\\') {
411: last_no_backslash = FALSE;
412: } else {
413: last_no_backslash = TRUE;
414: }
415: } else {
416: last_no_backslash = TRUE;
417: } /* check for trailing \ */
418:
419: if((*p == '#') || (*p == '\n')) {
420: continue;
421: }
422:
423: /*
424: ** include another file of aliases
425: */
426:
427: if(strncmp(p, ":include:", 9) == 0) {
428: char *nl;
429: p += 9;
430: if((nl = index(p, '\n')) != NULL) {
431: *nl = CNULL;
432: }
433: DEBUG("load_alias '%s' includes file '%s'\n", filename, p);
434: load_alias(head, p);
435: continue; /* bug? line truncated after :include: */
436: }
437:
438: /*
439: ** if the first char on the line is a space or tab
440: ** or the last character of the last line was a '\'
441: ** then it's a continuation line. Otherwise,
442: ** we start a new alias.
443: */
444: if(*p != ' ' && *p != '\t' && no_backslash) {
445: b = p;
446: SKIPWORD(p);
447: *p++ = CNULL;
448: /*
449: ** be sure that the alias is in local form
450: */
451: if(islocal(b, domain, user) == 0) {
452: /*
453: ** non-local alias format - skip it
454: */
455: continue;
456: }
457: /*
458: ** add the alias to the (vertical) list of aliases
459: */
460: if((h = add_vert(head, user)) == NNULL) {
461: DEBUG("load_alias for '%s' failed\n", b);
462: fclose(fp);
463: return;
464: }
465: }
466: /*
467: ** Next on the line is the list of recipents.
468: ** Strip out each word and add it to the
469: ** horizontal linked list.
470: */
471: (void) recipients(h, p);
472:
473: } /* while (fgets(buf, sizeof buf, fp) != NULL) */
474: (void) fclose(fp);
475: /*
476: ** strip out aliases which have no members
477: */
478: for(v = head; v->vert != NNULL; ) {
479: if(v->vert->horz == NNULL) {
480: v->vert = v->vert->vert;
481: } else {
482: v = v->vert;
483: }
484: }
485: }
486:
487: /*
488: ** add each word in a string (*p) of recipients
489: ** to the (horizontal) linked list associated with 'h'
490: */
491:
492: recipients(h, p)
493: node *h;
494: char *p;
495: {
496:
497: char *b, d[SMLBUF], u[SMLBUF];
498: int ret = 0;
499:
500: strip_comments(p); /* strip out stuff in ()'s */
501:
502: SKIPSPACE(p); /* skip leading whitespace on line */
503:
504: while((*p != NULL) && (*p != '#')) {
505: b = p;
506: if(*b == '"') {
507: if((p = index(++b, '"')) == NULL) {
508: /* syntax error - no matching quote */
509: /* skip the rest of the line */
510: return(ret);
511: }
512: } else {
513: SKIPWORD(p);
514: }
515:
516: if(*p != CNULL) {
517: *p++ = CNULL;
518: }
519:
520: /* don't allow aliases of the form
521: ** a a
522: */
523: if((islocal(b, d, u) == 0)
524: #ifdef CASEALIAS
525: || (strcmp(h->string, u) != 0)) {
526: #else
527: || (strcmpic(h->string, u) != 0)) {
528: #endif
529: add_horz(h, b);
530: ret = 1;
531: }
532: SKIPSPACE(p);
533: }
534: return(ret);
535: }
536:
537: /*
538: ** some aliases may have comments on the line like:
539: **
540: ** moderators [email protected] (Moderator's Name)
541: ** [email protected] (Another Moderator's Name)
542: **
543: ** strip out the stuff in ()'s
544: **
545: */
546:
547: void
548: strip_comments(p)
549: char *p;
550: {
551: char *b;
552: while((p = index(p, '(')) != NULL) {
553: b = p++; /*
554: ** save pointer to open parenthesis
555: */
556: if((p = index(p, ')')) != NULL) {/* look for close paren */
557: (void) strcpy(b, ++p); /* slide string left */
558: } else {
559: *b = CNULL; /* no paren, skip rest of line */
560: break;
561: }
562: }
563: }
564:
565: /*
566: ** add_vert - add a (vertical) link to the chain of aliases.
567: */
568:
569: node *
570: add_vert(head, str)
571: node *head;
572: char *str;
573: {
574: char *p, *malloc();
575: void free();
576: node *new;
577:
578: /*
579: ** strip colons off the end of alias names
580: */
581: if((p = index(str, ':')) != NULL) {
582: *p = CNULL;
583: }
584: if((new = (node *) malloc(sizeof(node))) != NNULL) {
585: if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
586: free(new);
587: new = NNULL;
588: } else {
589: (void) strcpy(new->string, str);
590: new->vert = head->vert;
591: new->horz = NNULL;
592: head->vert = new;
593: /*DEBUG("add_vert %s->%s\n", head->string, new->string);/* */
594: }
595: }
596: return(new);
597: }
598:
599: /*
600: ** add_horz - add a (horizontal) link to the chain of recipients.
601: */
602:
603: void
604: add_horz(head, str)
605: node *head;
606: char *str;
607: {
608: char *malloc();
609: node *new;
610:
611: if((new = (node *) malloc(sizeof(node))) != NNULL) {
612: if((new->string = malloc((unsigned) strlen(str)+1)) == NULL) {
613: free(new);
614: new = NNULL;
615: } else {
616: (void) strcpy(new->string, str);
617: new->horz = head->horz;
618: new->vert = NNULL;
619: head->horz = new;
620: }
621: /*DEBUG("add_horz %s->%s\n", head->string, new->string);/* */
622: }
623: }
624:
625: node *
626: pop(head)
627: node *head;
628: {
629: node *ret = NNULL;
630:
631:
632: if(head != NNULL) {
633: ret = head->horz;
634: if(ret != NNULL) {
635: head->horz = ret->horz;
636: }
637: }
638: return(ret);
639: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.