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