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