1: /*
2: * Copyright (c) 1980 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #if !defined(lint) && defined(DOSCCS)
35: static char sccsid[] = "@(#)aux.c 5.20 (Berkeley) 6/25/90";
36: #endif
37:
38: #include "rcv.h"
39: #include <sys/stat.h>
40: #include <sys/time.h>
41:
42: /*
43: * Mail -- a mail program
44: *
45: * Auxiliary functions.
46: */
47:
48: /*
49: * Return a pointer to a dynamic copy of the argument.
50: */
51: char *
52: savestr(str)
53: char *str;
54: {
55: char *new;
56: int size = strlen(str) + 1;
57:
58: if ((new = salloc(size)) != NOSTR)
59: bcopy(str, new, size);
60: return new;
61: }
62:
63: /*
64: * Announce a fatal error and die.
65: */
66:
67: /*VARARGS1*/
68: panic(fmt, a, b)
69: char *fmt;
70: {
71: fprintf(stderr, "panic: ");
72: fprintf(stderr, fmt, a, b);
73: putc('\n', stderr);
74: fflush(stdout);
75: abort();
76: }
77:
78: /*
79: * Touch the named message by setting its MTOUCH flag.
80: * Touched messages have the effect of not being sent
81: * back to the system mailbox on exit.
82: */
83: touch(mp)
84: register struct message *mp;
85: {
86:
87: mp->m_flag |= MTOUCH;
88: if ((mp->m_flag & MREAD) == 0)
89: mp->m_flag |= MREAD|MSTATUS;
90: }
91:
92: /*
93: * Test to see if the passed file name is a directory.
94: * Return true if it is.
95: */
96: isdir(name)
97: char name[];
98: {
99: struct stat sbuf;
100:
101: if (stat(name, &sbuf) < 0)
102: return(0);
103: return((sbuf.st_mode & S_IFMT) == S_IFDIR);
104: }
105:
106: /*
107: * Count the number of arguments in the given string raw list.
108: */
109: argcount(argv)
110: char **argv;
111: {
112: register char **ap;
113:
114: for (ap = argv; *ap++ != NOSTR;)
115: ;
116: return ap - argv - 1;
117: }
118:
119: /*
120: * Return the desired header line from the passed message
121: * pointer (or NOSTR if the desired header field is not available).
122: */
123: char *
124: hfield(field, mp)
125: char field[];
126: struct message *mp;
127: {
128: register FILE *ibuf;
129: char linebuf[LINESIZE];
130: register int lc;
131: register char *hfield;
132: char *colon;
133:
134: ibuf = setinput(mp);
135: if ((lc = mp->m_lines - 1) < 0)
136: return NOSTR;
137: if (readline(ibuf, linebuf, LINESIZE) < 0)
138: return NOSTR;
139: while (lc > 0) {
140: if ((lc = gethfield(ibuf, linebuf, lc, &colon)) < 0)
141: return NOSTR;
142: if (hfield = ishfield(linebuf, colon, field))
143: return savestr(hfield);
144: }
145: return NOSTR;
146: }
147:
148: /*
149: * Return the next header field found in the given message.
150: * Return >= 0 if something found, < 0 elsewise.
151: * "colon" is set to point to the colon in the header.
152: * Must deal with \ continuations & other such fraud.
153: */
154: gethfield(f, linebuf, rem, colon)
155: register FILE *f;
156: char linebuf[];
157: register int rem;
158: char **colon;
159: {
160: char line2[LINESIZE];
161: register char *cp, *cp2;
162: register int c;
163:
164: for (;;) {
165: if (--rem < 0)
166: return -1;
167: if ((c = readline(f, linebuf, LINESIZE)) <= 0)
168: return -1;
169: for (cp = linebuf; isprint(*cp) && *cp != ' ' && *cp != ':';
170: cp++)
171: ;
172: if (*cp != ':' || cp == linebuf)
173: continue;
174: /*
175: * I guess we got a headline.
176: * Handle wraparounding
177: */
178: *colon = cp;
179: cp = linebuf + c;
180: for (;;) {
181: while (--cp >= linebuf && (*cp == ' ' || *cp == '\t'))
182: ;
183: cp++;
184: if (rem <= 0)
185: break;
186: ungetc(c = getc(f), f);
187: if (c != ' ' && c != '\t')
188: break;
189: if ((c = readline(f, line2, LINESIZE)) < 0)
190: break;
191: rem--;
192: for (cp2 = line2; *cp2 == ' ' || *cp2 == '\t'; cp2++)
193: ;
194: c -= cp2 - line2;
195: if (cp + c >= linebuf + LINESIZE - 2)
196: break;
197: *cp++ = ' ';
198: bcopy(cp2, cp, c);
199: cp += c;
200: }
201: *cp = 0;
202: return rem;
203: }
204: /* NOTREACHED */
205: }
206:
207: /*
208: * Check whether the passed line is a header line of
209: * the desired breed. Return the field body, or 0.
210: */
211:
212: char*
213: ishfield(linebuf, colon, field)
214: char linebuf[], field[];
215: char *colon;
216: {
217: register char *cp = colon;
218:
219: *cp = 0;
220: if (strcasecmp(linebuf, field) != 0) {
221: *cp = ':';
222: return 0;
223: }
224: *cp = ':';
225: for (cp++; *cp == ' ' || *cp == '\t'; cp++)
226: ;
227: return cp;
228: }
229:
230: /*
231: * Copy a string, lowercasing it as we go.
232: */
233: istrcpy(dest, src)
234: register char *dest, *src;
235: {
236:
237: do {
238: if (isupper(*src))
239: *dest++ = tolower(*src);
240: else
241: *dest++ = *src;
242: } while (*src++ != 0);
243: }
244:
245: /*
246: * The following code deals with input stacking to do source
247: * commands. All but the current file pointer are saved on
248: * the stack.
249: */
250:
251: static int ssp; /* Top of file stack */
252: struct sstack {
253: FILE *s_file; /* File we were in. */
254: int s_cond; /* Saved state of conditionals */
255: int s_loading; /* Loading .mailrc, etc. */
256: } sstack[NOFILE];
257:
258: /*
259: * Pushdown current input file and switch to a new one.
260: * Set the global flag "sourcing" so that others will realize
261: * that they are no longer reading from a tty (in all probability).
262: */
263: source(arglist)
264: char **arglist;
265: {
266: FILE *fi;
267: char *cp;
268:
269: if ((cp = expand(*arglist)) == NOSTR)
270: return(1);
271: if ((fi = Fopen(cp, "r")) == NULL) {
272: perror(cp);
273: return(1);
274: }
275: if (ssp >= NOFILE - 1) {
276: printf("Too much \"sourcing\" going on.\n");
277: Fclose(fi);
278: return(1);
279: }
280: sstack[ssp].s_file = input;
281: sstack[ssp].s_cond = cond;
282: sstack[ssp].s_loading = loading;
283: ssp++;
284: loading = 0;
285: cond = CANY;
286: input = fi;
287: sourcing++;
288: return(0);
289: }
290:
291: /*
292: * Pop the current input back to the previous level.
293: * Update the "sourcing" flag as appropriate.
294: */
295: unstack()
296: {
297: if (ssp <= 0) {
298: printf("\"Source\" stack over-pop.\n");
299: sourcing = 0;
300: return(1);
301: }
302: Fclose(input);
303: if (cond != CANY)
304: printf("Unmatched \"if\"\n");
305: ssp--;
306: cond = sstack[ssp].s_cond;
307: loading = sstack[ssp].s_loading;
308: input = sstack[ssp].s_file;
309: if (ssp == 0)
310: sourcing = loading;
311: return(0);
312: }
313:
314: /*
315: * Touch the indicated file.
316: * This is nifty for the shell.
317: */
318: alter(name)
319: char *name;
320: {
321: struct stat sb;
322: struct timeval tv[2];
323: time_t time();
324:
325: if (stat(name, &sb))
326: return;
327: tv[0].tv_sec = time((time_t *)0) + 1;
328: tv[1].tv_sec = sb.st_mtime;
329: tv[0].tv_usec = tv[1].tv_usec = 0;
330: (void)utimes(name, tv);
331: }
332:
333: /*
334: * Examine the passed line buffer and
335: * return true if it is all blanks and tabs.
336: */
337: blankline(linebuf)
338: char linebuf[];
339: {
340: register char *cp;
341:
342: for (cp = linebuf; *cp; cp++)
343: if (*cp != ' ' && *cp != '\t')
344: return(0);
345: return(1);
346: }
347:
348: /*
349: * Get sender's name from this message. If the message has
350: * a bunch of arpanet stuff in it, we may have to skin the name
351: * before returning it.
352: */
353: char *
354: nameof(mp, reptype)
355: register struct message *mp;
356: {
357: register char *cp, *cp2;
358:
359: cp = skin(name1(mp, reptype));
360: if (reptype != 0 || charcount(cp, '!') < 2)
361: return(cp);
362: cp2 = rindex(cp, '!');
363: cp2--;
364: while (cp2 > cp && *cp2 != '!')
365: cp2--;
366: if (*cp2 == '!')
367: return(cp2 + 1);
368: return(cp);
369: }
370:
371: /*
372: * Start of a "comment".
373: * Ignore it.
374: */
375: *
376: skip_comment(cp)
377: register char *cp;
378: {
379: register nesting = 1;
380:
381: for (; nesting > 0 && *cp; cp++) {
382: switch (*cp) {
383: case '\\':
384: if (cp[1])
385: cp++;
386: break;
387: case '(':
388: nesting++;
389: break;
390: case ')':
391: nesting--;
392: break;
393: }
394: }
395: return cp;
396: }
397:
398: /*
399: * Skin an arpa net address according to the RFC 822 interpretation
400: * of "host-phrase."
401: */
402: char *
403: skin(name)
404: char *name;
405: {
406: register int c;
407: register char *cp, *cp2;
408: char *bufend;
409: int gotlt, lastsp;
410: char nbuf[BUFSIZ];
411:
412: if (name == NOSTR)
413: return(NOSTR);
414: if (index(name, '(') == NOSTR && index(name, '<') == NOSTR
415: && index(name, ' ') == NOSTR)
416: return(name);
417: gotlt = 0;
418: lastsp = 0;
419: bufend = nbuf;
420: for (cp = name, cp2 = bufend; c = *cp++; ) {
421: switch (c) {
422: case '(':
423: cp = skip_comment(cp);
424: lastsp = 0;
425: break;
426:
427: case '"':
428: /*
429: * Start of a "quoted-string".
430: * Copy it in its entirety.
431: */
432: while (c = *cp) {
433: cp++;
434: if (c == '"')
435: break;
436: if (c != '\\')
437: *cp2++ = c;
438: else if (c = *cp) {
439: *cp2++ = c;
440: cp++;
441: }
442: }
443: lastsp = 0;
444: break;
445:
446: case ' ':
447: if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
448: cp += 3, *cp2++ = '@';
449: else
450: if (cp[0] == '@' && cp[1] == ' ')
451: cp += 2, *cp2++ = '@';
452: else
453: lastsp = 1;
454: break;
455:
456: case '<':
457: cp2 = bufend;
458: gotlt++;
459: lastsp = 0;
460: break;
461:
462: case '>':
463: if (gotlt) {
464: gotlt = 0;
465: while ((c = *cp) && c != ',') {
466: cp++;
467: if (c == '(')
468: cp = skip_comment(cp);
469: else if (c == '"')
470: while (c = *cp) {
471: cp++;
472: if (c == '"')
473: break;
474: if (c == '\\' && *cp)
475: cp++;
476: }
477: }
478: lastsp = 0;
479: break;
480: }
481: /* Fall into . . . */
482:
483: default:
484: if (lastsp) {
485: lastsp = 0;
486: *cp2++ = ' ';
487: }
488: *cp2++ = c;
489: if (c == ',' && !gotlt) {
490: *cp2++ = ' ';
491: for (; *cp == ' '; cp++)
492: ;
493: lastsp = 0;
494: bufend = cp2;
495: }
496: }
497: }
498: *cp2 = 0;
499:
500: return(savestr(nbuf));
501: }
502:
503: /*
504: * Fetch the sender's name from the passed message.
505: * Reptype can be
506: * 0 -- get sender's name for display purposes
507: * 1 -- get sender's name for reply
508: * 2 -- get sender's name for Reply
509: */
510: char *
511: name1(mp, reptype)
512: register struct message *mp;
513: {
514: char namebuf[LINESIZE];
515: char linebuf[LINESIZE];
516: register char *cp, *cp2;
517: register FILE *ibuf;
518: int first = 1;
519:
520: if ((cp = hfield("from", mp)) != NOSTR)
521: return cp;
522: if (reptype == 0 && (cp = hfield("sender", mp)) != NOSTR)
523: return cp;
524: ibuf = setinput(mp);
525: namebuf[0] = 0;
526: if (readline(ibuf, linebuf, LINESIZE) < 0)
527: return(savestr(namebuf));
528: newname:
529: for (cp = linebuf; *cp && *cp != ' '; cp++)
530: ;
531: for (; *cp == ' ' || *cp == '\t'; cp++)
532: ;
533: for (cp2 = &namebuf[strlen(namebuf)];
534: *cp && *cp != ' ' && *cp != '\t' && cp2 < namebuf + LINESIZE - 1;)
535: *cp2++ = *cp++;
536: *cp2 = '\0';
537: if (readline(ibuf, linebuf, LINESIZE) < 0)
538: return(savestr(namebuf));
539: if ((cp = index(linebuf, 'F')) == NULL)
540: return(savestr(namebuf));
541: if (strncmp(cp, "From", 4) != 0)
542: return(savestr(namebuf));
543: while ((cp = index(cp, 'r')) != NULL) {
544: if (strncmp(cp, "remote", 6) == 0) {
545: if ((cp = index(cp, 'f')) == NULL)
546: break;
547: if (strncmp(cp, "from", 4) != 0)
548: break;
549: if ((cp = index(cp, ' ')) == NULL)
550: break;
551: cp++;
552: if (first) {
553: strcpy(namebuf, cp);
554: first = 0;
555: } else
556: strcpy(rindex(namebuf, '!')+1, cp);
557: strcat(namebuf, "!");
558: goto newname;
559: }
560: cp++;
561: }
562: return(savestr(namebuf));
563: }
564:
565: /*
566: * Count the occurances of c in str
567: */
568: charcount(str, c)
569: char *str;
570: {
571: register char *cp;
572: register int i;
573:
574: for (i = 0, cp = str; *cp; cp++)
575: if (*cp == c)
576: i++;
577: return(i);
578: }
579:
580: /*
581: * Are any of the characters in the two strings the same?
582: */
583: anyof(s1, s2)
584: register char *s1, *s2;
585: {
586:
587: while (*s1)
588: if (index(s2, *s1++))
589: return 1;
590: return 0;
591: }
592:
593: /*
594: * Convert c to upper case
595: */
596: raise(c)
597: register c;
598: {
599:
600: if (islower(c))
601: return toupper(c);
602: return c;
603: }
604:
605: /*
606: * Copy s1 to s2, return pointer to null in s2.
607: */
608: char *
609: copy(s1, s2)
610: register char *s1, *s2;
611: {
612:
613: while (*s2++ = *s1++)
614: ;
615: return s2 - 1;
616: }
617:
618: /*
619: * See if the given header field is supposed to be ignored.
620: */
621: isign(field, ignore)
622: char *field;
623: struct ignoretab ignore[2];
624: {
625: char realfld[BUFSIZ];
626:
627: if (ignore == ignoreall)
628: return 1;
629: /*
630: * Lower-case the string, so that "Status" and "status"
631: * will hash to the same place.
632: */
633: istrcpy(realfld, field);
634: if (ignore[1].i_count > 0)
635: return (!member(realfld, ignore + 1));
636: else
637: return (member(realfld, ignore));
638: }
639:
640: member(realfield, table)
641: register char *realfield;
642: struct ignoretab *table;
643: {
644: register struct ignore *igp;
645:
646: for (igp = table->i_head[hash(realfield)]; igp != 0; igp = igp->i_link)
647: if (*igp->i_field == *realfield &&
648: equal(igp->i_field, realfield))
649: return (1);
650: return (0);
651: }