Annotation of src/usr.bin/mail/aux.c, Revision 1.11
1.11 ! millert 1: /* $OpenBSD: aux.c,v 1.10 1997/07/30 07:19:29 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.11 ! millert 41: static char rcsid[] = "$OpenBSD: aux.c,v 1.10 1997/07/30 07:19:29 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: char *bufend;
469: int gotlt, lastsp;
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.9 millert 478: if ((bufend = (char *)malloc(strlen(name) + 1)) == NULL)
479: panic("Out of memory");
1.1 deraadt 480: gotlt = 0;
481: lastsp = 0;
1.2 deraadt 482: for (cp = name, cp2 = bufend; (c = *cp++) != '\0'; ) {
1.1 deraadt 483: switch (c) {
484: case '(':
485: cp = skip_comment(cp);
486: lastsp = 0;
487: break;
488:
489: case '"':
490: /*
491: * Start of a "quoted-string".
492: * Copy it in its entirety.
493: */
1.2 deraadt 494: while ((c = *cp) != '\0') {
1.1 deraadt 495: cp++;
496: if (c == '"')
497: break;
498: if (c != '\\')
499: *cp2++ = c;
1.2 deraadt 500: else if ((c = *cp) != '\0') {
1.1 deraadt 501: *cp2++ = c;
502: cp++;
503: }
504: }
505: lastsp = 0;
506: break;
507:
508: case ' ':
509: if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
510: cp += 3, *cp2++ = '@';
511: else
512: if (cp[0] == '@' && cp[1] == ' ')
513: cp += 2, *cp2++ = '@';
514: else
515: lastsp = 1;
516: break;
517:
518: case '<':
519: cp2 = bufend;
520: gotlt++;
521: lastsp = 0;
522: break;
523:
524: case '>':
525: if (gotlt) {
526: gotlt = 0;
527: while ((c = *cp) && c != ',') {
528: cp++;
529: if (c == '(')
530: cp = skip_comment(cp);
531: else if (c == '"')
1.2 deraadt 532: while ((c = *cp) != '\0') {
1.1 deraadt 533: cp++;
534: if (c == '"')
535: break;
536: if (c == '\\' && *cp)
537: cp++;
538: }
539: }
540: lastsp = 0;
541: break;
542: }
543: /* Fall into . . . */
544:
545: default:
546: if (lastsp) {
547: lastsp = 0;
548: *cp2++ = ' ';
549: }
550: *cp2++ = c;
551: if (c == ',' && !gotlt) {
552: *cp2++ = ' ';
553: for (; *cp == ' '; cp++)
554: ;
555: lastsp = 0;
556: bufend = cp2;
557: }
558: }
559: }
560: *cp2 = 0;
561:
1.9 millert 562: if ((bufend = realloc(bufend, strlen(bufend) + 1)) == NULL)
563: panic("Out of memory");
564: return(bufend);
1.1 deraadt 565: }
566:
567: /*
568: * Fetch the sender's name from the passed message.
569: * Reptype can be
570: * 0 -- get sender's name for display purposes
571: * 1 -- get sender's name for reply
572: * 2 -- get sender's name for Reply
573: */
574: char *
575: name1(mp, reptype)
576: register struct message *mp;
577: int reptype;
578: {
579: char namebuf[LINESIZE];
580: char linebuf[LINESIZE];
581: register char *cp, *cp2;
582: register FILE *ibuf;
583: int first = 1;
584:
1.7 millert 585: if ((cp = hfield("from", mp)) != NULL)
1.5 millert 586: return(cp);
1.7 millert 587: if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
1.5 millert 588: return(cp);
1.1 deraadt 589: ibuf = setinput(mp);
1.4 deraadt 590: namebuf[0] = '\0';
1.1 deraadt 591: if (readline(ibuf, linebuf, LINESIZE) < 0)
592: return(savestr(namebuf));
593: newname:
594: for (cp = linebuf; *cp && *cp != ' '; cp++)
595: ;
596: for (; *cp == ' ' || *cp == '\t'; cp++)
597: ;
598: for (cp2 = &namebuf[strlen(namebuf)];
599: *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
600: *cp2++ = *cp++;
601: *cp2 = '\0';
602: if (readline(ibuf, linebuf, LINESIZE) < 0)
603: return(savestr(namebuf));
1.3 millert 604: if ((cp = strchr(linebuf, 'F')) == NULL)
1.1 deraadt 605: return(savestr(namebuf));
606: if (strncmp(cp, "From", 4) != 0)
607: return(savestr(namebuf));
1.3 millert 608: while ((cp = strchr(cp, 'r')) != NULL) {
1.1 deraadt 609: if (strncmp(cp, "remote", 6) == 0) {
1.3 millert 610: if ((cp = strchr(cp, 'f')) == NULL)
1.1 deraadt 611: break;
612: if (strncmp(cp, "from", 4) != 0)
613: break;
1.3 millert 614: if ((cp = strchr(cp, ' ')) == NULL)
1.1 deraadt 615: break;
616: cp++;
617: if (first) {
1.5 millert 618: cp2 = namebuf;
1.1 deraadt 619: first = 0;
620: } else
1.5 millert 621: cp2 = strrchr(namebuf, '!') + 1;
622: strncpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 2);
623: namebuf[sizeof(namebuf) - 2] = '\0';
1.1 deraadt 624: strcat(namebuf, "!");
625: goto newname;
626: }
627: cp++;
628: }
629: return(savestr(namebuf));
630: }
631:
632: /*
633: * Count the occurances of c in str
634: */
635: int
636: charcount(str, c)
637: char *str;
638: int c;
639: {
640: register char *cp;
641: register int i;
642:
643: for (i = 0, cp = str; *cp; cp++)
644: if (*cp == c)
645: i++;
646: return(i);
647: }
648:
649: /*
650: * Are any of the characters in the two strings the same?
651: */
652: int
653: anyof(s1, s2)
654: register char *s1, *s2;
655: {
656:
657: while (*s1)
1.3 millert 658: if (strchr(s2, *s1++))
1.5 millert 659: return(1);
660: return(0);
1.1 deraadt 661: }
662:
663: /*
664: * Convert c to upper case
665: */
666: int
667: raise(c)
668: register int c;
669: {
670:
671: if (islower(c))
1.5 millert 672: return(toupper(c));
673: return(c);
1.1 deraadt 674: }
675:
676: /*
677: * Copy s1 to s2, return pointer to null in s2.
678: */
679: char *
680: copy(s1, s2)
681: register char *s1, *s2;
682: {
683:
1.2 deraadt 684: while ((*s2++ = *s1++) != '\0')
1.1 deraadt 685: ;
1.5 millert 686: return(s2 - 1);
1.1 deraadt 687: }
688:
689: /*
690: * See if the given header field is supposed to be ignored.
691: */
692: int
693: isign(field, ignore)
694: char *field;
695: struct ignoretab ignore[2];
696: {
1.5 millert 697: char realfld[LINESIZE];
1.1 deraadt 698:
699: if (ignore == ignoreall)
1.5 millert 700: return(1);
1.1 deraadt 701: /*
702: * Lower-case the string, so that "Status" and "status"
703: * will hash to the same place.
704: */
1.10 millert 705: istrncpy(realfld, field, sizeof(realfld));
1.1 deraadt 706: if (ignore[1].i_count > 0)
1.5 millert 707: return(!member(realfld, ignore + 1));
1.1 deraadt 708: else
1.5 millert 709: return(member(realfld, ignore));
1.1 deraadt 710: }
711:
712: int
713: member(realfield, table)
714: register char *realfield;
715: struct ignoretab *table;
716: {
717: register struct ignore *igp;
718:
719: for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
720: if (*igp->i_field == *realfield &&
721: equal(igp->i_field, realfield))
1.5 millert 722: return(1);
723: return(0);
1.1 deraadt 724: }