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