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