1 # include <pwd.h>
2 # include <sys/types.h>
3 # include <sys/stat.h>
4 # include "sendmail.h"
5 
6 static char SccsId[] = "@(#)recipient.c	3.16	09/12/81";
7 
8 /*
9 **  SENDTO -- Designate a send list.
10 **
11 **	The parameter is a comma-separated list of people to send to.
12 **	This routine arranges to send to all of them.
13 **
14 **	Parameters:
15 **		list -- the send list.
16 **		copyf -- the copy flag; passed to parse.
17 **
18 **	Returns:
19 **		none
20 **
21 **	Side Effects:
22 **		none.
23 */
24 
25 # define MAXRCRSN	10
26 
27 sendto(list, copyf)
28 	char *list;
29 	int copyf;
30 {
31 	register char *p;
32 	bool more;		/* set if more addresses to send to */
33 	ADDRESS *al;		/* list of addresses to send to */
34 
35 # ifdef DEBUG
36 	if (Debug > 1)
37 		printf("sendto: %s\n", list);
38 # endif DEBUG
39 
40 	more = TRUE;
41 	al = NULL;
42 	for (p = list; more; )
43 	{
44 		register char *q;
45 		register char c;
46 		ADDRESS *a;
47 
48 		/* find the end of this address */
49 		while (*p == ' ' || *p == '\t')
50 			p++;
51 		q = p;
52 		while ((c = *p++) != '\0' && c != ',' && c != '\n')
53 			continue;
54 		more = c != '\0';
55 		*--p = '\0';
56 		if (more)
57 			p++;
58 		if (*q == '\0')
59 			continue;
60 
61 		/* parse the address */
62 		if ((a = parse(q, (ADDRESS *) NULL, copyf)) == NULL)
63 			continue;
64 
65 		/* put it on the local send list */
66 		a->q_next = al;
67 		al = a;
68 	}
69 
70 	/* arrange to send to everyone on the local send list */
71 	while (al != NULL)
72 	{
73 		register ADDRESS *a = al;
74 
75 		al = a->q_next;
76 		recipient(a);
77 	}
78 
79 	To = NULL;
80 }
81 /*
82 **  RECIPIENT -- Designate a message recipient
83 **
84 **	Saves the named person for future mailing.
85 **
86 **	Parameters:
87 **		a -- the (preparsed) address header for the recipient.
88 **
89 **	Returns:
90 **		none.
91 **
92 **	Side Effects:
93 **		none.
94 */
95 
96 recipient(a)
97 	register ADDRESS *a;
98 {
99 	register ADDRESS *q;
100 	ADDRESS **pq;
101 	register struct mailer *m;
102 
103 	To = a->q_paddr;
104 	m = Mailer[a->q_mailer];
105 	errno = 0;
106 # ifdef DEBUG
107 	if (Debug)
108 		printf("recipient(%s)\n", To);
109 # endif DEBUG
110 
111 	/* break aliasing loops */
112 	if (AliasLevel > MAXRCRSN)
113 	{
114 		usrerr("aliasing/forwarding loop broken");
115 		return;
116 	}
117 
118 	/*
119 	**  Do sickly crude mapping for program mailing, etc.
120 	*/
121 
122 	if (a->q_mailer == MN_LOCAL)
123 	{
124 		if (a->q_user[0] == '|')
125 		{
126 			a->q_mailer = MN_PROG;
127 			m = Mailer[MN_PROG];
128 			a->q_user++;
129 # ifdef PARANOID
130 			if (AliasLevel <= 0)
131 			{
132 				usrerr("Cannot mail directly to programs");
133 				a->q_flags |= QDONTSEND;
134 			}
135 # endif PARANOID
136 		}
137 	}
138 
139 	/*
140 	**  Look up this person in the recipient list.  If they
141 	**  are there already, return, otherwise continue.
142 	**  If the list is empty, just add it.
143 	*/
144 
145 	for (pq = &m->m_sendq; (q = *pq) != NULL; pq = &q->q_next)
146 	{
147 		if (!ForceMail && sameaddr(q, a, FALSE))
148 		{
149 # ifdef DEBUG
150 			if (Debug)
151 				printf("(%s in sendq)\n", a->q_paddr);
152 # endif DEBUG
153 			if (Verbose && !bitset(QDONTSEND, a->q_flags))
154 				message(Arpa_Info, "duplicate suppressed");
155 			return;
156 		}
157 	}
158 
159 	/* add address on list */
160 	*pq = a;
161 	a->q_next = NULL;
162 	if (DontSend)
163 		a->q_flags |= QDONTSEND;
164 
165 	/*
166 	**  Alias the name and handle :include: specs.
167 	*/
168 
169 	if (a->q_mailer == MN_LOCAL)
170 	{
171 		if (strncmp(a->q_user, ":include:", 9) == 0)
172 		{
173 			a->q_flags |= QDONTSEND;
174 			if (Verbose)
175 				message(Arpa_Info, "including file %s", &a->q_user[9]);
176 			include(&a->q_user[9], " sending");
177 		}
178 		else
179 			alias(a);
180 	}
181 
182 	/*
183 	**  If the user is local and still being sent, verify that
184 	**  the address is good.  If it is, try to forward.
185 	**  If the address is already good, we have a forwarding
186 	**  loop.  This can be broken by just sending directly to
187 	**  the user (which is probably correct anyway).
188 	*/
189 
190 	if (!bitset(QDONTSEND, a->q_flags) && a->q_mailer == MN_LOCAL)
191 	{
192 		char buf[MAXNAME];
193 		register char *p;
194 		struct stat stb;
195 		extern bool writable();
196 
197 		strcpy(buf, a->q_user);
198 		stripquotes(buf, TRUE);
199 
200 		/* see if this is to a file */
201 		if ((p = rindex(buf, '/')) != NULL)
202 		{
203 			/* check if writable or creatable */
204 			if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
205 			    (*p = '\0', access(buf, 3) < 0))
206 			{
207 				a->q_flags |= QBADADDR;
208 				giveresponse(EX_CANTCREAT, TRUE, m);
209 			}
210 		}
211 		else
212 		{
213 			register struct passwd *pw;
214 			extern struct passwd *finduser();
215 
216 			pw = finduser(buf);
217 			if (pw == NULL)
218 			{
219 				a->q_flags |= QBADADDR;
220 				giveresponse(EX_NOUSER, TRUE, m);
221 			}
222 			else
223 			{
224 				if (strcmp(a->q_user, pw->pw_name) != 0)
225 				{
226 					a->q_user = newstr(pw->pw_name);
227 					strcpy(buf, pw->pw_name);
228 				}
229 				a->q_home = newstr(pw->pw_dir);
230 				a->q_uid = pw->pw_uid;
231 				if (strcmp(buf, a->q_user) == 0)
232 					forward(a);
233 			}
234 		}
235 	}
236 }
237 /*
238 **  FINDUSER -- find the password entry for a user.
239 **
240 **	This looks a lot like getpwnam, except that it may want to
241 **	do some fancier pattern matching in /etc/passwd.
242 **
243 **	Parameters:
244 **		name -- the name to match against.
245 **
246 **	Returns:
247 **		A pointer to a pw struct.
248 **		NULL if name is unknown or ambiguous.
249 **
250 **	Side Effects:
251 **		none.
252 */
253 
254 struct passwd *
255 finduser(name)
256 	char *name;
257 {
258 	extern struct passwd *getpwent();
259 	register struct passwd *pw;
260 
261 	setpwent();
262 	while ((pw = getpwent()) != NULL)
263 	{
264 		char buf[MAXNAME];
265 		register char *p;
266 		extern bool sameword();
267 		bool gotaspace;
268 
269 		if (strcmp(pw->pw_name, name) == 0)
270 			return (pw);
271 		buildfname(pw->pw_gecos, pw->pw_name, buf);
272 		gotaspace = FALSE;
273 		for (p = buf; (p = index(p, ' ')) != NULL; )
274 		{
275 			*p++ = SPACESUB & 0177;
276 			gotaspace = TRUE;
277 		}
278 		if (gotaspace && sameword(buf, name))
279 		{
280 			if (Verbose)
281 				message(Arpa_Info, "sending to login name %s",
282 				    pw->pw_name);
283 			return (pw);
284 		}
285 	}
286 	return (NULL);
287 }
288 /*
289 **  WRITABLE -- predicate returning if the file is writable.
290 **
291 **	This routine must duplicate the algorithm in sys/fio.c.
292 **	Unfortunately, we cannot use the access call since we
293 **	won't necessarily be the real uid when we try to
294 **	actually open the file.
295 **
296 **	Notice that ANY file with ANY execute bit is automatically
297 **	not writable.  This is also enforced by mailfile.
298 **
299 **	Parameters:
300 **		s -- pointer to a stat struct for the file.
301 **
302 **	Returns:
303 **		TRUE -- if we will be able to write this file.
304 **		FALSE -- if we cannot write this file.
305 **
306 **	Side Effects:
307 **		none.
308 */
309 
310 bool
311 writable(s)
312 	register struct stat *s;
313 {
314 	int euid, egid;
315 	int bits;
316 
317 	if (bitset(0111, s->st_mode))
318 		return (FALSE);
319 	euid = getruid();
320 	egid = getrgid();
321 	if (geteuid() == 0)
322 	{
323 		if (bitset(S_ISUID, s->st_mode))
324 			euid = s->st_uid;
325 		if (bitset(S_ISGID, s->st_mode))
326 			egid = s->st_gid;
327 	}
328 
329 	if (euid == 0)
330 		return (TRUE);
331 	bits = S_IWRITE;
332 	if (euid != s->st_uid)
333 	{
334 		bits >>= 3;
335 		if (egid != s->st_gid)
336 			bits >>= 3;
337 	}
338 	return ((s->st_mode & bits) != 0);
339 }
340 /*
341 **  INCLUDE -- handle :include: specification.
342 **
343 **	Parameters:
344 **		fname -- filename to include.
345 **		msg -- message to print in verbose mode.
346 **
347 **	Returns:
348 **		none.
349 **
350 **	Side Effects:
351 **		reads the :include: file and sends to everyone
352 **		listed in that file.
353 */
354 
355 include(fname, msg)
356 	char *fname;
357 	char *msg;
358 {
359 	char buf[MAXLINE];
360 	register FILE *fp;
361 	char *oldto = To;
362 
363 	fp = fopen(fname, "r");
364 	if (fp == NULL)
365 	{
366 		usrerr("Cannot open %s", fname);
367 		return;
368 	}
369 
370 	/* read the file -- each line is a comma-separated list. */
371 	while (fgets(buf, sizeof buf, fp) != NULL)
372 	{
373 		register char *p = index(buf, '\n');
374 
375 		if (p != NULL)
376 			*p = '\0';
377 		if (buf[0] == '\0')
378 			continue;
379 		To = oldto;
380 		if (Verbose)
381 			message(Arpa_Info, "%s to %s", msg, buf);
382 		AliasLevel++;
383 		sendto(buf, 1);
384 		AliasLevel--;
385 	}
386 
387 	(void) fclose(fp);
388 }
389 /*
390 **  SENDTOARGV -- send to an argument vector.
391 **
392 **	Parameters:
393 **		argv -- argument vector to send to.
394 **
395 **	Returns:
396 **		none.
397 **
398 **	Side Effects:
399 **		puts all addresses on the argument vector onto the
400 **			send queue.
401 */
402 
403 sendtoargv(argv)
404 	register char **argv;
405 {
406 	register char *p;
407 	extern bool sameword();
408 
409 	while ((p = *argv++) != NULL)
410 	{
411 		if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at"))
412 		{
413 			char nbuf[MAXNAME];
414 
415 			if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
416 				usrerr("address overflow");
417 			else
418 			{
419 				(void) strcpy(nbuf, p);
420 				(void) strcat(nbuf, "@");
421 				(void) strcat(nbuf, argv[1]);
422 				p = newstr(nbuf);
423 				argv += 2;
424 			}
425 		}
426 		sendto(p, 0);
427 	}
428 }
429