1 # include <pwd.h>
2 # include <sys/types.h>
3 # include <sys/stat.h>
4 # include "sendmail.h"
5 
6 # ifdef DBM
7 static char SccsId[] = "@(#)alias.c	3.19	09/06/81	(with DBM)";
8 # else DBM
9 static char SccsId[] = "@(#)alias.c	3.19	09/06/81	(without DBM)";
10 # endif DBM
11 
12 /*
13 **  ALIAS -- Compute aliases.
14 **
15 **	Scans the file /usr/lib/aliases for a set of aliases.
16 **	If found, it arranges to deliver to them.  Uses libdbm
17 **	database if -DDBM.
18 **
19 **	Parameters:
20 **		a -- address to alias.
21 **
22 **	Returns:
23 **		none
24 **
25 **	Side Effects:
26 **		Aliases found are expanded.
27 **
28 **	Files:
29 **		/usr/lib/aliases -- the mail aliases.  The format is
30 **			a series of lines of the form:
31 **				alias:name1,name2,name3,...
32 **			where 'alias' expands to all of
33 **			'name[i]'.  Continuations begin with
34 **			space or tab.
35 **		/usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
36 **			of alias file.  Keys are aliases, datums
37 **			(data?) are name1,name2, ...
38 **
39 **	Notes:
40 **		If NoAlias (the "-n" flag) is set, no aliasing is
41 **			done.
42 **
43 **	Deficiencies:
44 **		It should complain about names that are aliased to
45 **			nothing.
46 **		It is unsophisticated about line overflows.
47 */
48 
49 
50 #ifdef DBM
51 typedef struct
52 {
53 	char	*dptr;
54 	int	dsize;
55 } DATUM;
56 DATUM lhs, rhs;
57 extern DATUM fetch();
58 #endif DBM
59 
60 alias(a)
61 	register ADDRESS *a;
62 {
63 	register char *p;
64 # ifndef DBM
65 	register STAB *s;
66 # endif DBM
67 
68 	if (NoAlias)
69 		return;
70 # ifdef DEBUG
71 	if (Debug)
72 		printf("alias(%s)\n", a->q_paddr);
73 # endif
74 
75 	/* don't realias already aliased names */
76 	if (bitset(QDONTSEND, a->q_flags))
77 		return;
78 
79 	To = a->q_paddr;
80 
81 	/*
82 	**  Look up this name
83 	*/
84 
85 # ifdef DBM
86 	/* create a key for fetch */
87 	lhs.dptr = a->q_user;
88 	lhs.dsize = strlen(a->q_user) + 1;
89 	rhs = fetch(lhs);
90 
91 	/* find this alias? */
92 	p = rhs.dptr;
93 	if (p == NULL)
94 		return;
95 # else DBM
96 	s = stab(a->q_user, ST_ALIAS, ST_FIND);
97 	if (s == NULL)
98 		return;
99 	p = s->s_alias;
100 # endif DBM
101 
102 	/*
103 	**  Match on Alias.
104 	**	Deliver to the target list.
105 	*/
106 
107 # ifdef DEBUG
108 	if (Debug)
109 		printf("%s (%s, %s) aliased to %s\n",
110 		    a->q_paddr, a->q_host, a->q_user, p);
111 # endif
112 	if (Verbose)
113 		message(Arpa_Info, "aliased to %s", p);
114 	a->q_flags |= QDONTSEND;
115 	AliasLevel++;
116 	sendto(p, 1);
117 	AliasLevel--;
118 }
119 /*
120 **  INITALIASES -- initialize for aliasing
121 **
122 **	Very different depending on whether we are running DBM or not.
123 **
124 **	Parameters:
125 **		aliasfile -- location of aliases.
126 **		init -- if set and if DBM, initialize the DBM files.
127 **
128 **	Returns:
129 **		none.
130 **
131 **	Side Effects:
132 **		initializes aliases:
133 **		if DBM:  opens the database.
134 **		if ~DBM: reads the aliases into the symbol table.
135 */
136 
137 # define DBMMODE	0666
138 
139 initaliases(aliasfile, init)
140 	char *aliasfile;
141 	bool init;
142 {
143 # ifdef DBM
144 	if (init)
145 	{
146 		char buf[MAXNAME];
147 
148 		(void) strcpy(buf, aliasfile);
149 		(void) strcat(buf, ".dir");
150 		if (close(creat(buf, DBMMODE)) < 0)
151 		{
152 			syserr("cannot make %s", buf);
153 			return;
154 		}
155 		(void) strcpy(buf, aliasfile);
156 		(void) strcat(buf, ".pag");
157 		if (close(creat(buf, DBMMODE)) < 0)
158 		{
159 			syserr("cannot make %s", buf);
160 			return;
161 		}
162 	}
163 	dbminit(aliasfile);
164 	if (init)
165 		readaliases(aliasfile, TRUE);
166 # else DBM
167 	readaliases(aliasfile, init);
168 # endif DBM
169 }
170 /*
171 **  READALIASES -- read and process the alias file.
172 **
173 **	This routine implements the part of initaliases that occurs
174 **	when we are not going to use the DBM stuff.
175 **
176 **	Parameters:
177 **		aliasfile -- the pathname of the alias file master.
178 **		init -- if set, initialize the DBM stuff.
179 **
180 **	Returns:
181 **		none.
182 **
183 **	Side Effects:
184 **		Reads aliasfile into the symbol table.
185 **		Optionally, builds the .dir & .pag files.
186 */
187 
188 static
189 readaliases(aliasfile, init)
190 	char *aliasfile;
191 	bool init;
192 {
193 	char line[BUFSIZ];
194 	register char *p;
195 	char *p2;
196 	char *rhs;
197 	bool skipping;
198 	ADDRESS al, bl;
199 	FILE *af;
200 	int lineno;
201 	register STAB *s;
202 
203 	if ((af = fopen(aliasfile, "r")) == NULL)
204 	{
205 # ifdef DEBUG
206 		if (Debug)
207 			printf("Can't open %s\n", aliasfile);
208 # endif
209 		errno = 0;
210 		NoAlias++;
211 		return;
212 	}
213 
214 	/*
215 	**  Read and interpret lines
216 	*/
217 
218 	lineno = 0;
219 	skipping = FALSE;
220 	while (fgets(line, sizeof (line), af) != NULL)
221 	{
222 		lineno++;
223 		switch (line[0])
224 		{
225 		  case '#':
226 		  case '\n':
227 		  case '\0':
228 			skipping = FALSE;
229 			continue;
230 
231 		  case ' ':
232 		  case '\t':
233 			if (!skipping)
234 				syserr("aliases: %d: Non-continuation line starts with space", lineno);
235 			skipping = TRUE;
236 			continue;
237 		}
238 		skipping = FALSE;
239 
240 		/*
241 		**  Process the LHS
242 		**	Find the final colon, and parse the address.
243 		**	It should resolve to a local name -- this will
244 		**	be checked later (we want to optionally do
245 		**	parsing of the RHS first to maximize error
246 		**	detection).
247 		*/
248 
249 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
250 			continue;
251 		if (*p == '\0' || *p == '\n')
252 		{
253 		 syntaxerr:
254 			syserr("aliases: %d: missing colon", lineno);
255 			continue;
256 		}
257 		*p++ = '\0';
258 		if (parse(line, &al, 1) == NULL)
259 		{
260 			*--p = ':';
261 			goto syntaxerr;
262 		}
263 
264 		/*
265 		**  Process the RHS.
266 		**	'al' is the internal form of the LHS address.
267 		**	'p' points to the text of the RHS.
268 		*/
269 
270 		rhs = p;
271 		for (;;)
272 		{
273 			register char c;
274 
275 			if (init)
276 			{
277 				/* do parsing & compression of addresses */
278 				c = *p;
279 				while (c != '\0')
280 				{
281 					p2 = p;
282 					while (*p != '\n' && *p != ',' && *p != '\0')
283 						p++;
284 					c = *p;
285 					*p++ = '\0';
286 					if (*p2 == '\0')
287 					{
288 						p[-1] = c;
289 						continue;
290 					}
291 					(void) parse(p2, &bl, -1);
292 					p[-1] = c;
293 					while (isspace(*p))
294 						p++;
295 				}
296 			}
297 			else
298 				p = &p[strlen(p)];
299 
300 			/* see if there should be a continuation line */
301 			c = fgetc(af);
302 			if (!feof(af))
303 				(void) ungetc(c, af);
304 			if (c != ' ' && c != '\t')
305 				break;
306 
307 			/* read continuation line */
308 			p--;
309 			if (fgets(p, sizeof line - (p - line), af) == NULL)
310 				break;
311 			lineno++;
312 		}
313 		if (al.q_mailer != MN_LOCAL)
314 		{
315 			syserr("aliases: %d: cannot alias non-local names", lineno);
316 			continue;
317 		}
318 
319 		/*
320 		**  Insert alias into symbol table or DBM file
321 		*/
322 
323 # ifdef DBM
324 		if (init)
325 		{
326 			DATUM key, content;
327 
328 			key.dsize = strlen(al.q_user) + 1;
329 			key.dptr = al.q_user;
330 			content.dsize = strlen(rhs) + 1;
331 			content.dptr = rhs;
332 			store(key, content);
333 		}
334 		else
335 # endif DBM
336 		{
337 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
338 			s->s_alias = newstr(rhs);
339 		}
340 	}
341 	(void) fclose(af);
342 }
343 /*
344 **  FORWARD -- Try to forward mail
345 **
346 **	This is similar but not identical to aliasing.
347 **
348 **	Parameters:
349 **		user -- the name of the user who's mail we would like
350 **			to forward to.  It must have been verified --
351 **			i.e., the q_home field must have been filled
352 **			in.
353 **
354 **	Returns:
355 **		none.
356 **
357 **	Side Effects:
358 **		New names are added to send queues.
359 **		Sets the QDONTSEND bit in addresses that are forwarded.
360 */
361 
362 forward(user)
363 	ADDRESS *user;
364 {
365 	char buf[60];
366 	struct stat stbuf;
367 
368 # ifdef DEBUG
369 	if (Debug)
370 		printf("forward(%s)\n", user->q_paddr);
371 # endif DEBUG
372 
373 	if (user->q_mailer != MN_LOCAL || bitset(QBADADDR, user->q_flags))
374 		return;
375 # ifdef DEBUG
376 	if (user->q_home == NULL)
377 		syserr("forward: no home");
378 # endif DEBUG
379 
380 	/* good address -- look for .forward file in home */
381 	define('z', user->q_home);
382 	(void) expand("$z/.forward", buf, &buf[sizeof buf - 1]);
383 	if (stat(buf, &stbuf) < 0 || stbuf.st_uid != user->q_uid ||
384 	    !bitset(S_IREAD, stbuf.st_mode))
385 		return;
386 
387 	/* we do have an address to forward to -- do it */
388 	user->q_flags |= QDONTSEND;
389 	include(buf, "forwarding");
390 }
391