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