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