xref: /original-bsd/usr.bin/mail/aux.c (revision 29d43723)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)aux.c	5.16 (Berkeley) 12/23/88";
20 #endif /* not lint */
21 
22 #include "rcv.h"
23 #include <sys/stat.h>
24 
25 /*
26  * Mail -- a mail program
27  *
28  * Auxiliary functions.
29  */
30 
31 /*
32  * Return a pointer to a dynamic copy of the argument.
33  */
34 char *
35 savestr(str)
36 	char *str;
37 {
38 	char *new;
39 	int size = strlen(str) + 1;
40 
41 	if ((new = salloc(size)) != NOSTR)
42 		bcopy(str, new, size);
43 	return new;
44 }
45 
46 /*
47  * Announce a fatal error and die.
48  */
49 
50 /*VARARGS1*/
51 panic(fmt, a, b)
52 	char *fmt;
53 {
54 	fprintf(stderr, "panic: ");
55 	fprintf(stderr, fmt, a, b);
56 	putc('\n', stderr);
57 	exit(1);
58 }
59 
60 /*
61  * Touch the named message by setting its MTOUCH flag.
62  * Touched messages have the effect of not being sent
63  * back to the system mailbox on exit.
64  */
65 touch(mp)
66 	register struct message *mp;
67 {
68 
69 	mp->m_flag |= MTOUCH;
70 	if ((mp->m_flag & MREAD) == 0)
71 		mp->m_flag |= MREAD|MSTATUS;
72 }
73 
74 /*
75  * Test to see if the passed file name is a directory.
76  * Return true if it is.
77  */
78 isdir(name)
79 	char name[];
80 {
81 	struct stat sbuf;
82 
83 	if (stat(name, &sbuf) < 0)
84 		return(0);
85 	return((sbuf.st_mode & S_IFMT) == S_IFDIR);
86 }
87 
88 /*
89  * Count the number of arguments in the given string raw list.
90  */
91 argcount(argv)
92 	char **argv;
93 {
94 	register char **ap;
95 
96 	for (ap = argv; *ap++ != NOSTR;)
97 		;
98 	return ap - argv - 1;
99 }
100 
101 /*
102  * Return the desired header line from the passed message
103  * pointer (or NOSTR if the desired header field is not available).
104  */
105 char *
106 hfield(field, mp)
107 	char field[];
108 	struct message *mp;
109 {
110 	register FILE *ibuf;
111 	char linebuf[LINESIZE];
112 	register int lc;
113 	register char *hfield;
114 	char *colon;
115 
116 	ibuf = setinput(mp);
117 	if ((lc = mp->m_lines - 1) < 0)
118 		return NOSTR;
119 	if (readline(ibuf, linebuf, LINESIZE) < 0)
120 		return NOSTR;
121 	while (lc > 0) {
122 		if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
123 			return NOSTR;
124 		if (hfield = ishfield(linebuf, colon, field))
125 			return savestr(hfield);
126 	}
127 	return NOSTR;
128 }
129 
130 /*
131  * Return the next header field found in the given message.
132  * Return >= 0 if something found, < 0 elsewise.
133  * "colon" is set to point to the colon in the header.
134  * Must deal with \ continuations & other such fraud.
135  */
136 gethfield(f, linebuf, rem, colon)
137 	register FILE *f;
138 	char linebuf[];
139 	register int rem;
140 	char **colon;
141 {
142 	char line2[LINESIZE];
143 	register char *cp, *cp2;
144 	register int c;
145 
146 	for (;;) {
147 		if (--rem < 0)
148 			return -1;
149 		if ((c = readline(f, linebuf, LINESIZE)) <= 0)
150 			return -1;
151 		for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
152 		     cp++)
153 			;
154 		if (*cp != ':' || cp == linebuf)
155 			continue;
156 		/*
157 		 * I guess we got a headline.
158 		 * Handle wraparounding
159 		 */
160 		*colon = cp;
161 		cp = linebuf + c;
162 		for (;;) {
163 			while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
164 				;
165 			cp++;
166 			if (rem <= 0)
167 				break;
168 			ungetc(c = getc(f), f);
169 			if (c != ' ' && c != '\t')
170 				break;
171 			if ((c = readline(f, line2, LINESIZE)) < 0)
172 				break;
173 			rem--;
174 			for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
175 				;
176 			c -= cp2 - line2;
177 			if (cp + c >= linebuf + LINESIZE - 2)
178 				break;
179 			*cp++ = ' ';
180 			bcopy(cp2, cp, c);
181 			cp += c;
182 		}
183 		*cp = 0;
184 		return rem;
185 	}
186 	/* NOTREACHED */
187 }
188 
189 /*
190  * Check whether the passed line is a header line of
191  * the desired breed.  Return the field body, or 0.
192  */
193 
194 char*
195 ishfield(linebuf, colon, field)
196 	char linebuf[], field[];
197 	char *colon;
198 {
199 	register char *cp = colon;
200 
201 	*cp = 0;
202 	if (strcasecmp(linebuf, field) != 0) {
203 		*cp = ':';
204 		return 0;
205 	}
206 	*cp = ':';
207 	for (cp++; *cp == ' ' || *cp == '\t'; cp++)
208 		;
209 	return cp;
210 }
211 
212 /*
213  * Copy a string, lowercasing it as we go.
214  */
215 istrcpy(dest, src)
216 	register char *dest, *src;
217 {
218 
219 	do {
220 		if (isupper(*src))
221 			*dest++ = tolower(*src);
222 		else
223 			*dest++ = *src;
224 	} while (*src++ != 0);
225 }
226 
227 /*
228  * The following code deals with input stacking to do source
229  * commands.  All but the current file pointer are saved on
230  * the stack.
231  */
232 
233 static	int	ssp;			/* Top of file stack */
234 struct sstack {
235 	FILE	*s_file;		/* File we were in. */
236 	int	s_cond;			/* Saved state of conditionals */
237 	int	s_loading;		/* Loading .mailrc, etc. */
238 } sstack[NOFILE];
239 
240 /*
241  * Pushdown current input file and switch to a new one.
242  * Set the global flag "sourcing" so that others will realize
243  * that they are no longer reading from a tty (in all probability).
244  */
245 source(arglist)
246 	char **arglist;
247 {
248 	FILE *fi;
249 	char *cp;
250 
251 	if ((cp = expand(*arglist)) == NOSTR)
252 		return(1);
253 	if ((fi = fopen(cp, "r")) == NULL) {
254 		perror(cp);
255 		return(1);
256 	}
257 	if (ssp >= NOFILE - 1) {
258 		printf("Too much \"sourcing\" going on.\n");
259 		fclose(fi);
260 		return(1);
261 	}
262 	sstack[ssp].s_file = input;
263 	sstack[ssp].s_cond = cond;
264 	sstack[ssp].s_loading = loading;
265 	ssp++;
266 	loading = 0;
267 	cond = CANY;
268 	input = fi;
269 	sourcing++;
270 	return(0);
271 }
272 
273 /*
274  * Pop the current input back to the previous level.
275  * Update the "sourcing" flag as appropriate.
276  */
277 unstack()
278 {
279 	if (ssp <= 0) {
280 		printf("\"Source\" stack over-pop.\n");
281 		sourcing = 0;
282 		return(1);
283 	}
284 	fclose(input);
285 	if (cond != CANY)
286 		printf("Unmatched \"if\"\n");
287 	ssp--;
288 	cond = sstack[ssp].s_cond;
289 	loading = sstack[ssp].s_loading;
290 	input = sstack[ssp].s_file;
291 	if (ssp == 0)
292 		sourcing = loading;
293 	return(0);
294 }
295 
296 /*
297  * Touch the indicated file.
298  * This is nifty for the shell.
299  */
300 alter(name)
301 	char name[];
302 {
303 	struct stat statb;
304 	long time();
305 	time_t time_p[2];
306 
307 	if (stat(name, &statb) < 0)
308 		return;
309 	time_p[0] = time((long *) 0) + 1;
310 	time_p[1] = statb.st_mtime;
311 	utime(name, time_p);
312 }
313 
314 /*
315  * Examine the passed line buffer and
316  * return true if it is all blanks and tabs.
317  */
318 blankline(linebuf)
319 	char linebuf[];
320 {
321 	register char *cp;
322 
323 	for (cp = linebuf; *cp; cp++)
324 		if (*cp != ' ' && *cp != '\t')
325 			return(0);
326 	return(1);
327 }
328 
329 /*
330  * Get sender's name from this message.  If the message has
331  * a bunch of arpanet stuff in it, we may have to skin the name
332  * before returning it.
333  */
334 char *
335 nameof(mp, reptype)
336 	register struct message *mp;
337 {
338 	register char *cp, *cp2;
339 
340 	cp = skin(name1(mp, reptype));
341 	if (reptype != 0 || charcount(cp, '!') < 2)
342 		return(cp);
343 	cp2 = rindex(cp, '!');
344 	cp2--;
345 	while (cp2 > cp && *cp2 != '!')
346 		cp2--;
347 	if (*cp2 == '!')
348 		return(cp2 + 1);
349 	return(cp);
350 }
351 
352 /*
353  * Skin an arpa net address according to the RFC 822 interpretation
354  * of "host-phrase."
355  */
356 char *
357 skin(name)
358 	char *name;
359 {
360 	register int c;
361 	register char *cp, *cp2;
362 	char *bufend;
363 	int gotlt, lastsp;
364 	char nbuf[BUFSIZ];
365 	int nesting;
366 
367 	if (name == NOSTR)
368 		return(NOSTR);
369 	if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
370 	    && index(name, ' ') == NOSTR)
371 		return(name);
372 	gotlt = 0;
373 	lastsp = 0;
374 	bufend = nbuf;
375 	for (cp = name, cp2 = bufend; c = *cp++; ) {
376 		switch (c) {
377 		case '(':
378 			/*
379 			 * Start of a "comment".
380 			 * Ignore it.
381 			 */
382 			nesting = 1;
383 			while ((c = *cp) != 0) {
384 				cp++;
385 				switch (c) {
386 				case '\\':
387 					if (*cp == 0)
388 						goto outcm;
389 					cp++;
390 					break;
391 				case '(':
392 					nesting++;
393 					break;
394 
395 				case ')':
396 					--nesting;
397 					break;
398 				}
399 
400 				if (nesting <= 0)
401 					break;
402 			}
403 		outcm:
404 			lastsp = 0;
405 			break;
406 
407 		case '"':
408 			/*
409 			 * Start of a "quoted-string".
410 			 * Copy it in its entirety.
411 			 */
412 			while ((c = *cp) != 0) {
413 				cp++;
414 				switch (c) {
415 				case '\\':
416 					if ((c = *cp) == 0)
417 						goto outqs;
418 					cp++;
419 					break;
420 				case '"':
421 					goto outqs;
422 				}
423 				*cp2++ = c;
424 			}
425 		outqs:
426 			lastsp = 0;
427 			break;
428 
429 		case ' ':
430 			if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
431 				cp += 3, *cp2++ = '@';
432 			else
433 			if (cp[0] == '@' && cp[1] == ' ')
434 				cp += 2, *cp2++ = '@';
435 			else
436 				lastsp = 1;
437 			break;
438 
439 		case '<':
440 			cp2 = bufend;
441 			gotlt++;
442 			lastsp = 0;
443 			break;
444 
445 		case '>':
446 			if (gotlt) {
447 				gotlt = 0;
448 				while (*cp != ',' && *cp != 0)
449 					cp++;
450 				if (*cp == 0 )
451 					goto done;
452 				*cp2++ = ',';
453 				*cp2++ = ' ';
454 				bufend = cp2;
455 				break;
456 			}
457 
458 			/* Fall into . . . */
459 
460 		default:
461 			if (lastsp) {
462 				lastsp = 0;
463 				*cp2++ = ' ';
464 			}
465 			*cp2++ = c;
466 			break;
467 		}
468 	}
469 done:
470 	*cp2 = 0;
471 
472 	return(savestr(nbuf));
473 }
474 
475 /*
476  * Fetch the sender's name from the passed message.
477  * Reptype can be
478  *	0 -- get sender's name for display purposes
479  *	1 -- get sender's name for reply
480  *	2 -- get sender's name for Reply
481  */
482 char *
483 name1(mp, reptype)
484 	register struct message *mp;
485 {
486 	char namebuf[LINESIZE];
487 	char linebuf[LINESIZE];
488 	register char *cp, *cp2;
489 	register FILE *ibuf;
490 	int first = 1;
491 
492 	if ((cp = hfield("from", mp)) != NOSTR)
493 		return cp;
494 	if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
495 		return cp;
496 	ibuf = setinput(mp);
497 	namebuf[0] = 0;
498 	if (readline(ibuf, linebuf, LINESIZE) < 0)
499 		return(savestr(namebuf));
500 newname:
501 	for (cp = linebuf; *cp && *cp != ' '; cp++)
502 		;
503 	for (; *cp == ' ' || *cp == '\t'; cp++)
504 		;
505 	for (cp2 = &namebuf[strlen(namebuf)];
506 	     *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
507 		*cp2++ = *cp++;
508 	*cp2 = '\0';
509 	if (readline(ibuf, linebuf, LINESIZE) < 0)
510 		return(savestr(namebuf));
511 	if ((cp = index(linebuf, 'F')) == NULL)
512 		return(savestr(namebuf));
513 	if (strncmp(cp, "From", 4) != 0)
514 		return(savestr(namebuf));
515 	while ((cp = index(cp, 'r')) != NULL) {
516 		if (strncmp(cp, "remote", 6) == 0) {
517 			if ((cp = index(cp, 'f')) == NULL)
518 				break;
519 			if (strncmp(cp, "from", 4) != 0)
520 				break;
521 			if ((cp = index(cp, ' ')) == NULL)
522 				break;
523 			cp++;
524 			if (first) {
525 				strcpy(namebuf, cp);
526 				first = 0;
527 			} else
528 				strcpy(rindex(namebuf, '!')+1, cp);
529 			strcat(namebuf, "!");
530 			goto newname;
531 		}
532 		cp++;
533 	}
534 	return(savestr(namebuf));
535 }
536 
537 /*
538  * Count the occurances of c in str
539  */
540 charcount(str, c)
541 	char *str;
542 {
543 	register char *cp;
544 	register int i;
545 
546 	for (i = 0, cp = str; *cp; cp++)
547 		if (*cp == c)
548 			i++;
549 	return(i);
550 }
551 
552 /*
553  * Are any of the characters in the two strings the same?
554  */
555 anyof(s1, s2)
556 	register char *s1, *s2;
557 {
558 
559 	while (*s1)
560 		if (index(s2, *s1++))
561 			return 1;
562 	return 0;
563 }
564 
565 /*
566  * Convert c to upper case
567  */
568 raise(c)
569 	register c;
570 {
571 
572 	if (islower(c))
573 		return toupper(c);
574 	return c;
575 }
576 
577 /*
578  * Copy s1 to s2, return pointer to null in s2.
579  */
580 char *
581 copy(s1, s2)
582 	register char *s1, *s2;
583 {
584 
585 	while (*s2++ = *s1++)
586 		;
587 	return s2 - 1;
588 }
589 
590 /*
591  * See if the given header field is supposed to be ignored.
592  */
593 isign(field, ignore)
594 	char *field;
595 	struct ignoretab ignore[2];
596 {
597 	char realfld[BUFSIZ];
598 
599 	if (ignore == ignoreall)
600 		return 1;
601 	/*
602 	 * Lower-case the string, so that "Status" and "status"
603 	 * will hash to the same place.
604 	 */
605 	istrcpy(realfld, field);
606 	if (ignore[1].i_count > 0)
607 		return (!member(realfld, ignore + 1));
608 	else
609 		return (member(realfld, ignore));
610 }
611 
612 member(realfield, table)
613 	register char *realfield;
614 	struct ignoretab *table;
615 {
616 	register struct ignore *igp;
617 
618 	for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
619 		if (*igp->i_field == *realfield &&
620 		    equal(igp->i_field, realfield))
621 			return (1);
622 	return (0);
623 }
624