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