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