Annotation of src/usr.bin/mail/aux.c, Revision 1.26
1.26 ! okan 1: /* $OpenBSD: aux.c,v 1.25 2009/10/27 23:59:40 deraadt 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.26 ! okan 163: for (cp = linebuf;
! 164: isprint((unsigned char)*cp) && *cp != ' ' && *cp != ':';
! 165: cp++)
1.1 deraadt 166: ;
167: if (*cp != ':' || cp == linebuf)
168: continue;
169: /*
170: * I guess we got a headline.
171: * Handle wraparounding
172: */
173: *colon = cp;
174: cp = linebuf + c;
175: for (;;) {
176: while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
177: ;
178: cp++;
179: if (rem <= 0)
180: break;
181: ungetc(c = getc(f), f);
182: if (c != ' ' && c != '\t')
183: break;
1.19 millert 184: if ((c = readline(f, line2, LINESIZE, NULL)) < 0)
1.1 deraadt 185: break;
186: rem--;
187: for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
188: ;
189: c -= cp2 - line2;
190: if (cp + c >= linebuf + LINESIZE - 2)
191: break;
192: *cp++ = ' ';
1.6 millert 193: (void)memcpy(cp, cp2, c);
1.1 deraadt 194: cp += c;
195: }
196: *cp = 0;
1.5 millert 197: return(rem);
1.1 deraadt 198: }
199: /* NOTREACHED */
200: }
201:
202: /*
203: * Check whether the passed line is a header line of
204: * the desired breed. Return the field body, or 0.
205: */
206:
207: char*
1.20 millert 208: ishfield(char *linebuf, char *colon, char *field)
1.1 deraadt 209: {
1.13 millert 210: char *cp = colon;
1.1 deraadt 211:
212: *cp = 0;
213: if (strcasecmp(linebuf, field) != 0) {
214: *cp = ':';
1.5 millert 215: return(0);
1.1 deraadt 216: }
217: *cp = ':';
218: for (cp++; *cp == ' ' || *cp == '\t'; cp++)
219: ;
1.5 millert 220: return(cp);
1.1 deraadt 221: }
222:
223: /*
1.10 millert 224: * Copy a string, lowercasing it as we go. ``dsize'' should be
1.20 millert 225: * the real size (not len) of the dest string (guarantee NUL term).
1.1 deraadt 226: */
1.20 millert 227: size_t
228: istrlcpy(char *dst, const char *src, size_t dsize)
1.1 deraadt 229: {
1.20 millert 230: char *d = dst;
231: const char *s = src;
232: size_t n = dsize;
233:
234: /* Copy as many bytes as will fit */
235: if (n != 0 && --n != 0) {
236: do {
1.26 ! okan 237: if ((*d++ = tolower((unsigned char)*s++)) == 0)
1.20 millert 238: break;
239: } while (--n != 0);
240: }
1.1 deraadt 241:
1.20 millert 242: /* Not enough room in dst, add NUL and traverse rest of src */
243: if (n == 0) {
244: if (dsize != 0)
245: *d = '\0'; /* NUL-terminate dst */
246: while (*s++)
247: ;
1.10 millert 248: }
1.20 millert 249:
250: return(s - src - 1); /* count does not include NUL */
1.1 deraadt 251: }
252:
253: /*
254: * The following code deals with input stacking to do source
255: * commands. All but the current file pointer are saved on
256: * the stack.
257: */
258: static int ssp; /* Top of file stack */
259: struct sstack {
260: FILE *s_file; /* File we were in. */
261: int s_cond; /* Saved state of conditionals */
262: int s_loading; /* Loading .mailrc, etc. */
263: } sstack[NOFILE];
264:
265: /*
266: * Pushdown current input file and switch to a new one.
267: * Set the global flag "sourcing" so that others will realize
268: * that they are no longer reading from a tty (in all probability).
269: */
270: int
1.20 millert 271: source(void *v)
1.1 deraadt 272: {
1.2 deraadt 273: char **arglist = v;
1.1 deraadt 274: FILE *fi;
275: char *cp;
276:
1.7 millert 277: if ((cp = expand(*arglist)) == NULL)
1.1 deraadt 278: return(1);
279: if ((fi = Fopen(cp, "r")) == NULL) {
1.15 millert 280: warn("%s", cp);
1.1 deraadt 281: return(1);
282: }
283: if (ssp >= NOFILE - 1) {
1.5 millert 284: puts("Too much \"sourcing\" going on.");
285: (void)Fclose(fi);
1.1 deraadt 286: return(1);
287: }
288: sstack[ssp].s_file = input;
289: sstack[ssp].s_cond = cond;
290: sstack[ssp].s_loading = loading;
291: ssp++;
292: loading = 0;
293: cond = CANY;
294: input = fi;
295: sourcing++;
296: return(0);
297: }
298:
299: /*
300: * Pop the current input back to the previous level.
301: * Update the "sourcing" flag as appropriate.
302: */
303: int
1.20 millert 304: unstack(void)
1.1 deraadt 305: {
1.20 millert 306:
1.1 deraadt 307: if (ssp <= 0) {
1.5 millert 308: puts("\"Source\" stack over-pop.");
1.1 deraadt 309: sourcing = 0;
310: return(1);
311: }
1.5 millert 312: (void)Fclose(input);
1.1 deraadt 313: if (cond != CANY)
1.5 millert 314: puts("Unmatched \"if\"");
1.1 deraadt 315: ssp--;
316: cond = sstack[ssp].s_cond;
317: loading = sstack[ssp].s_loading;
318: input = sstack[ssp].s_file;
319: if (ssp == 0)
320: sourcing = loading;
321: return(0);
322: }
323:
324: /*
325: * Touch the indicated file.
326: * This is nifty for the shell.
327: */
328: void
1.20 millert 329: alter(char *name)
1.1 deraadt 330: {
331: struct stat sb;
332: struct timeval tv[2];
333:
334: if (stat(name, &sb))
335: return;
1.13 millert 336: (void) gettimeofday(&tv[0], (struct timezone *)0);
337: tv[0].tv_sec++;
1.16 millert 338: #ifdef TIMESPEC_TO_TIMEVAL
1.13 millert 339: TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
1.16 millert 340: #else
341: tv[1].tv_sec = sb.st_mtime;
342: #endif
1.1 deraadt 343: (void)utimes(name, tv);
344: }
345:
346: /*
347: * Examine the passed line buffer and
348: * return true if it is all blanks and tabs.
349: */
350: int
1.20 millert 351: blankline(char *linebuf)
1.1 deraadt 352: {
1.13 millert 353: char *cp;
1.1 deraadt 354:
355: for (cp = linebuf; *cp; cp++)
356: if (*cp != ' ' && *cp != '\t')
357: return(0);
358: return(1);
359: }
360:
361: /*
362: * Get sender's name from this message. If the message has
363: * a bunch of arpanet stuff in it, we may have to skin the name
364: * before returning it.
365: */
366: char *
1.20 millert 367: nameof(struct message *mp, int reptype)
1.1 deraadt 368: {
1.13 millert 369: char *cp, *cp2;
1.1 deraadt 370:
371: cp = skin(name1(mp, reptype));
372: if (reptype != 0 || charcount(cp, '!') < 2)
373: return(cp);
1.3 millert 374: cp2 = strrchr(cp, '!');
1.1 deraadt 375: cp2--;
376: while (cp2 > cp && *cp2 != '!')
377: cp2--;
378: if (*cp2 == '!')
379: return(cp2 + 1);
380: return(cp);
381: }
382:
383: /*
384: * Start of a "comment".
385: * Ignore it.
386: */
387: char *
1.20 millert 388: skip_comment(char *cp)
1.1 deraadt 389: {
1.13 millert 390: int nesting = 1;
1.1 deraadt 391:
392: for (; nesting > 0 && *cp; cp++) {
393: switch (*cp) {
394: case '\\':
395: if (cp[1])
396: cp++;
397: break;
398: case '(':
399: nesting++;
400: break;
401: case ')':
402: nesting--;
403: break;
404: }
405: }
1.5 millert 406: return(cp);
1.1 deraadt 407: }
408:
409: /*
410: * Skin an arpa net address according to the RFC 822 interpretation
411: * of "host-phrase."
412: */
413: char *
1.20 millert 414: skin(char *name)
1.1 deraadt 415: {
1.13 millert 416: char *nbuf, *bufend, *cp, *cp2;
417: int c, gotlt, lastsp;
1.1 deraadt 418:
1.7 millert 419: if (name == NULL)
420: return(NULL);
421: if (strchr(name, '(') == NULL && strchr(name, '<') == NULL
422: && strchr(name, ' ') == NULL)
1.1 deraadt 423: return(name);
1.11 millert 424:
425: /* We assume that length(input) <= length(output) */
1.12 millert 426: if ((nbuf = (char *)malloc(strlen(name) + 1)) == NULL)
1.13 millert 427: errx(1, "Out of memory");
1.1 deraadt 428: gotlt = 0;
429: lastsp = 0;
1.12 millert 430: bufend = nbuf;
1.26 ! okan 431: for (cp = name, cp2 = bufend; (c = (unsigned char)*cp++) != '\0'; ) {
1.1 deraadt 432: switch (c) {
433: case '(':
434: cp = skip_comment(cp);
435: lastsp = 0;
436: break;
437:
438: case '"':
439: /*
440: * Start of a "quoted-string".
441: * Copy it in its entirety.
442: */
1.26 ! okan 443: while ((c = (unsigned char)*cp) != '\0') {
1.1 deraadt 444: cp++;
445: if (c == '"')
446: break;
447: if (c != '\\')
448: *cp2++ = c;
1.26 ! okan 449: else if ((c = (unsigned char)*cp) != '\0') {
1.1 deraadt 450: *cp2++ = c;
451: cp++;
452: }
453: }
454: lastsp = 0;
455: break;
456:
457: case ' ':
1.24 martynas 458: if (strncmp(cp, "at ", 3) == 0)
1.1 deraadt 459: cp += 3, *cp2++ = '@';
460: else
1.24 martynas 461: if (strncmp(cp, "@ ", 2) == 0)
1.1 deraadt 462: cp += 2, *cp2++ = '@';
463: else
464: lastsp = 1;
465: break;
466:
467: case '<':
468: cp2 = bufend;
469: gotlt++;
470: lastsp = 0;
471: break;
472:
473: case '>':
474: if (gotlt) {
475: gotlt = 0;
1.26 ! okan 476: while ((c = (unsigned char)*cp) && c != ',') {
1.1 deraadt 477: cp++;
478: if (c == '(')
479: cp = skip_comment(cp);
480: else if (c == '"')
1.26 ! okan 481: while ((c = (unsigned char)*cp) != '\0') {
1.1 deraadt 482: cp++;
483: if (c == '"')
484: break;
485: if (c == '\\' && *cp)
486: cp++;
487: }
488: }
489: lastsp = 0;
490: break;
491: }
492: /* Fall into . . . */
493:
494: default:
495: if (lastsp) {
496: lastsp = 0;
497: *cp2++ = ' ';
498: }
499: *cp2++ = c;
1.17 millert 500: if (c == ',' && *cp == ' ' && !gotlt) {
1.1 deraadt 501: *cp2++ = ' ';
1.18 millert 502: while (*++cp == ' ')
1.1 deraadt 503: ;
504: lastsp = 0;
505: bufend = cp2;
506: }
507: }
508: }
509: *cp2 = 0;
510:
1.17 millert 511: if ((cp = (char *)realloc(nbuf, strlen(nbuf) + 1)) != NULL)
512: nbuf = cp;
1.12 millert 513: return(nbuf);
1.1 deraadt 514: }
515:
516: /*
517: * Fetch the sender's name from the passed message.
518: * Reptype can be
519: * 0 -- get sender's name for display purposes
520: * 1 -- get sender's name for reply
521: * 2 -- get sender's name for Reply
522: */
523: char *
1.20 millert 524: name1(struct message *mp, int reptype)
1.1 deraadt 525: {
526: char namebuf[LINESIZE];
527: char linebuf[LINESIZE];
1.13 millert 528: char *cp, *cp2;
529: FILE *ibuf;
1.1 deraadt 530: int first = 1;
531:
1.7 millert 532: if ((cp = hfield("from", mp)) != NULL)
1.5 millert 533: return(cp);
1.7 millert 534: if (reptype == 0 && (cp = hfield("sender", mp)) != NULL)
1.5 millert 535: return(cp);
1.1 deraadt 536: ibuf = setinput(mp);
1.4 deraadt 537: namebuf[0] = '\0';
1.19 millert 538: if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
1.1 deraadt 539: return(savestr(namebuf));
540: newname:
541: for (cp = linebuf; *cp && *cp != ' '; cp++)
542: ;
543: for (; *cp == ' ' || *cp == '\t'; cp++)
544: ;
545: for (cp2 = &namebuf[strlen(namebuf)];
546: *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
547: *cp2++ = *cp++;
548: *cp2 = '\0';
1.19 millert 549: if (readline(ibuf, linebuf, LINESIZE, NULL) < 0)
1.1 deraadt 550: return(savestr(namebuf));
1.3 millert 551: if ((cp = strchr(linebuf, 'F')) == NULL)
1.1 deraadt 552: return(savestr(namebuf));
553: if (strncmp(cp, "From", 4) != 0)
554: return(savestr(namebuf));
1.3 millert 555: while ((cp = strchr(cp, 'r')) != NULL) {
1.1 deraadt 556: if (strncmp(cp, "remote", 6) == 0) {
1.3 millert 557: if ((cp = strchr(cp, 'f')) == NULL)
1.1 deraadt 558: break;
559: if (strncmp(cp, "from", 4) != 0)
560: break;
1.3 millert 561: if ((cp = strchr(cp, ' ')) == NULL)
1.1 deraadt 562: break;
563: cp++;
564: if (first) {
1.5 millert 565: cp2 = namebuf;
1.1 deraadt 566: first = 0;
567: } else
1.5 millert 568: cp2 = strrchr(namebuf, '!') + 1;
1.20 millert 569: strlcpy(cp2, cp, sizeof(namebuf) - (cp2 - namebuf) - 1);
570: strlcat(namebuf, "!", sizeof(namebuf));
1.1 deraadt 571: goto newname;
572: }
573: cp++;
574: }
575: return(savestr(namebuf));
576: }
577:
578: /*
579: * Count the occurances of c in str
580: */
581: int
1.20 millert 582: charcount(char *str, int c)
1.1 deraadt 583: {
1.13 millert 584: char *cp;
585: int i;
1.1 deraadt 586:
587: for (i = 0, cp = str; *cp; cp++)
588: if (*cp == c)
589: i++;
590: return(i);
591: }
592:
593: /*
594: * Copy s1 to s2, return pointer to null in s2.
595: */
596: char *
1.20 millert 597: copy(char *s1, char *s2)
1.1 deraadt 598: {
599:
1.2 deraadt 600: while ((*s2++ = *s1++) != '\0')
1.1 deraadt 601: ;
1.5 millert 602: return(s2 - 1);
1.1 deraadt 603: }
604:
605: /*
606: * See if the given header field is supposed to be ignored.
607: */
608: int
1.20 millert 609: isign(char *field, struct ignoretab ignore[2])
1.1 deraadt 610: {
1.5 millert 611: char realfld[LINESIZE];
1.1 deraadt 612:
613: if (ignore == ignoreall)
1.5 millert 614: return(1);
1.1 deraadt 615: /*
616: * Lower-case the string, so that "Status" and "status"
617: * will hash to the same place.
618: */
1.20 millert 619: istrlcpy(realfld, field, sizeof(realfld));
1.1 deraadt 620: if (ignore[1].i_count > 0)
1.5 millert 621: return(!member(realfld, ignore + 1));
1.1 deraadt 622: else
1.5 millert 623: return(member(realfld, ignore));
1.1 deraadt 624: }
625:
626: int
1.20 millert 627: member(char *realfield, struct ignoretab *table)
1.1 deraadt 628: {
1.13 millert 629: struct ignore *igp;
1.1 deraadt 630:
631: for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
632: if (*igp->i_field == *realfield &&
633: equal(igp->i_field, realfield))
1.5 millert 634: return(1);
635: return(0);
1.14 millert 636: }
637:
638: void
1.20 millert 639: clearnew(void)
1.14 millert 640: {
641: struct message *mp;
642:
643: for (mp = &message[0]; mp < &message[msgCount]; mp++) {
644: if (mp->m_flag & MNEW) {
645: mp->m_flag &= ~MNEW;
646: mp->m_flag |= MSTATUS;
647: }
648: }
1.1 deraadt 649: }