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