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