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