Annotation of src/usr.bin/mail/fio.c, Revision 1.22
1.22 ! mpech 1: /* $OpenBSD: fio.c,v 1.21 2001/12/18 16:55:06 millert Exp $ */
1.6 millert 2: /* $NetBSD: fio.c,v 1.8 1997/07/07 22:57:55 phil 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[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95";
1.2 deraadt 40: #else
1.22 ! mpech 41: static const char rcsid[] = "$OpenBSD: fio.c,v 1.21 2001/12/18 16:55:06 millert Exp $";
1.2 deraadt 42: #endif
1.1 deraadt 43: #endif /* not lint */
44:
45: #include "rcv.h"
46: #include <sys/file.h>
47: #include <sys/wait.h>
48:
49: #include <unistd.h>
50: #include <paths.h>
51: #include <errno.h>
52: #include "extern.h"
53:
54: /*
55: * Mail -- a mail program
56: *
57: * File I/O.
58: */
59:
1.19 millert 60: static volatile sig_atomic_t fiosignal;
61:
1.1 deraadt 62: /*
1.18 millert 63: * Wrapper for read() to catch EINTR.
64: */
65: ssize_t
1.20 millert 66: myread(int fd, char *buf, int len)
1.18 millert 67: {
68: ssize_t nread;
69:
70: while ((nread = read(fd, buf, len)) == -1 && errno == EINTR)
71: ;
72: return(nread);
73: }
74:
75: /*
1.1 deraadt 76: * Set up the input pointers while copying the mail file into /tmp.
77: */
78: void
1.20 millert 79: setptr(FILE *ibuf, off_t offset)
1.1 deraadt 80: {
1.14 millert 81: int c, count;
82: char *cp, *cp2;
1.1 deraadt 83: struct message this;
84: FILE *mestmp;
1.14 millert 85: int maybe, inhead, omsgCount;
1.5 deraadt 86: char linebuf[LINESIZE], pathbuf[PATHSIZE];
1.1 deraadt 87:
88: /* Get temporary file. */
1.6 millert 89: (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir);
90: if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL)
91: err(1, "can't open %s", pathbuf);
1.10 millert 92: (void)rm(pathbuf);
1.1 deraadt 93:
1.6 millert 94: if (offset == 0) {
95: msgCount = 0;
96: } else {
97: /* Seek into the file to get to the new messages */
1.7 millert 98: (void)fseek(ibuf, offset, 0);
1.6 millert 99: /*
100: * We need to make "offset" a pointer to the end of
101: * the temp file that has the copy of the mail file.
102: * If any messages have been edited, this will be
103: * different from the offset into the mail file.
104: */
1.7 millert 105: (void)fseek(otf, 0L, SEEK_END);
1.6 millert 106: offset = ftell(otf);
107: }
108: omsgCount = msgCount;
1.1 deraadt 109: maybe = 1;
110: inhead = 0;
111: this.m_flag = MUSED|MNEW;
112: this.m_size = 0;
113: this.m_lines = 0;
114: this.m_block = 0;
115: this.m_offset = 0;
116: for (;;) {
1.6 millert 117: if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) {
118: if (append(&this, mestmp))
119: err(1, "temporary file");
120: makemessage(mestmp, omsgCount);
1.1 deraadt 121: return;
122: }
123: count = strlen(linebuf);
1.13 millert 124: /*
125: * Transforms lines ending in <CR><LF> to just <LF>.
126: * This allows mail to be able to read Eudora mailboxes
127: * that reside on a DOS partition.
128: */
129: if (count >= 2 && linebuf[count-1] == '\n' &&
130: linebuf[count - 2] == '\r')
131: linebuf[count - 2] = linebuf[--count];
132:
1.7 millert 133: (void)fwrite(linebuf, sizeof(*linebuf), count, otf);
1.6 millert 134: if (ferror(otf))
135: err(1, "/tmp");
1.15 deraadt 136: if (count)
137: linebuf[count - 1] = '\0';
1.1 deraadt 138: if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
139: msgCount++;
1.6 millert 140: if (append(&this, mestmp))
141: err(1, "temporary file");
1.1 deraadt 142: this.m_flag = MUSED|MNEW;
143: this.m_size = 0;
144: this.m_lines = 0;
145: this.m_block = blockof(offset);
146: this.m_offset = offsetof(offset);
147: inhead = 1;
148: } else if (linebuf[0] == 0) {
149: inhead = 0;
150: } else if (inhead) {
151: for (cp = linebuf, cp2 = "status";; cp++) {
152: if ((c = *cp2++) == 0) {
153: while (isspace(*cp++))
154: ;
155: if (cp[-1] != ':')
156: break;
1.2 deraadt 157: while ((c = *cp++) != '\0')
1.1 deraadt 158: if (c == 'R')
159: this.m_flag |= MREAD;
160: else if (c == 'O')
161: this.m_flag &= ~MNEW;
162: inhead = 0;
163: break;
164: }
165: if (*cp != c && *cp != toupper(c))
166: break;
167: }
168: }
169: offset += count;
170: this.m_size += count;
171: this.m_lines++;
172: maybe = linebuf[0] == 0;
173: }
174: }
175:
176: /*
177: * Drop the passed line onto the passed output buffer.
178: * If a write error occurs, return -1, else the count of
1.6 millert 179: * characters written, including the newline if requested.
1.1 deraadt 180: */
181: int
1.20 millert 182: putline(FILE *obuf, char *linebuf, int outlf)
1.1 deraadt 183: {
1.14 millert 184: int c;
1.1 deraadt 185:
186: c = strlen(linebuf);
1.7 millert 187: (void)fwrite(linebuf, sizeof(*linebuf), c, obuf);
1.6 millert 188: if (outlf) {
1.7 millert 189: (void)putc('\n', obuf);
1.6 millert 190: c++;
191: }
1.1 deraadt 192: if (ferror(obuf))
1.6 millert 193: return(-1);
194: return(c);
1.1 deraadt 195: }
196:
197: /*
198: * Read up a line from the specified input into the line
199: * buffer. Return the number of characters read. Do not
1.13 millert 200: * include the newline (or carriage return) at the end.
1.1 deraadt 201: */
202: int
1.20 millert 203: readline(FILE *ibuf, char *linebuf, int linesize, int *signo)
1.1 deraadt 204: {
1.19 millert 205: struct sigaction act;
206: struct sigaction savetstp;
207: struct sigaction savettou;
208: struct sigaction savettin;
209: struct sigaction saveint;
210: struct sigaction savehup;
211: sigset_t oset;
1.14 millert 212: int n;
1.1 deraadt 213:
1.19 millert 214: /*
215: * Setup signal handlers if the caller asked us to catch signals.
216: * Note that we do not restart system calls since we need the
217: * read to be interuptible.
218: */
219: if (signo) {
220: fiosignal = 0;
221: sigemptyset(&act.sa_mask);
222: act.sa_flags = 0;
223: act.sa_handler = fioint;
224: if (sigaction(SIGINT, NULL, &saveint) == 0 &&
225: saveint.sa_handler != SIG_IGN) {
226: (void)sigaction(SIGINT, &act, &saveint);
227: (void)sigprocmask(SIG_UNBLOCK, &intset, &oset);
228: }
229: if (sigaction(SIGHUP, NULL, &savehup) == 0 &&
230: savehup.sa_handler != SIG_IGN)
231: (void)sigaction(SIGHUP, &act, &savehup);
232: (void)sigaction(SIGTSTP, &act, &savetstp);
233: (void)sigaction(SIGTTOU, &act, &savettou);
234: (void)sigaction(SIGTTIN, &act, &savettin);
235: }
236:
1.1 deraadt 237: clearerr(ibuf);
1.19 millert 238: if (fgets(linebuf, linesize, ibuf) == NULL) {
239: if (ferror(ibuf))
240: clearerr(ibuf);
241: n = -1;
242: } else {
243: n = strlen(linebuf);
244: if (n > 0 && linebuf[n - 1] == '\n')
245: linebuf[--n] = '\0';
246: if (n > 0 && linebuf[n - 1] == '\r')
247: linebuf[--n] = '\0';
248: }
249:
250: if (signo) {
251: (void)sigprocmask(SIG_SETMASK, &oset, NULL);
252: (void)sigaction(SIGINT, &saveint, NULL);
253: (void)sigaction(SIGHUP, &savehup, NULL);
254: (void)sigaction(SIGTSTP, &savetstp, NULL);
255: (void)sigaction(SIGTTOU, &savettou, NULL);
256: (void)sigaction(SIGTTIN, &savettin, NULL);
257: *signo = fiosignal;
258: }
1.6 millert 259:
260: return(n);
1.1 deraadt 261: }
262:
263: /*
264: * Return a file buffer all ready to read up the
265: * passed message pointer.
266: */
267: FILE *
1.20 millert 268: setinput(struct message *mp)
1.1 deraadt 269: {
270:
271: fflush(otf);
1.14 millert 272: if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0)
273: err(1, "fseek");
1.6 millert 274: return(itf);
1.1 deraadt 275: }
276:
277: /*
278: * Take the data out of the passed ghost file and toss it into
279: * a dynamically allocated message structure.
280: */
281: void
1.20 millert 282: makemessage(FILE *f, int omsgCount)
1.1 deraadt 283: {
1.21 millert 284: size_t size;
285: struct message *nmessage;
1.1 deraadt 286:
1.21 millert 287: size = (msgCount + 1) * sizeof(struct message);
288: nmessage = (struct message *)realloc(message, size);
289: if (nmessage == 0)
1.22 ! mpech 290: errx(1, "Insufficient memory for %d messages",
1.21 millert 291: msgCount);
292: if (omsgCount == 0 || message == NULL)
293: dot = nmessage;
294: else
295: dot = nmessage + (dot - message);
296: message = nmessage;
1.6 millert 297: size -= (omsgCount + 1) * sizeof(struct message);
1.1 deraadt 298: fflush(f);
1.7 millert 299: (void)lseek(fileno(f), (off_t)sizeof(*message), 0);
1.18 millert 300: if (myread(fileno(f), (void *) &message[omsgCount], size) != size)
1.14 millert 301: errx(1, "Message temporary file corrupted");
1.1 deraadt 302: message[msgCount].m_size = 0;
303: message[msgCount].m_lines = 0;
1.6 millert 304: (void)Fclose(f);
1.1 deraadt 305: }
306:
307: /*
308: * Append the passed message descriptor onto the temp file.
309: * If the write fails, return 1, else 0
310: */
311: int
1.20 millert 312: append(struct message *mp, FILE *f)
1.1 deraadt 313: {
1.20 millert 314:
1.6 millert 315: return(fwrite((char *) mp, sizeof(*mp), 1, f) != 1);
1.1 deraadt 316: }
317:
318: /*
1.16 millert 319: * Delete or truncate a file, but only if the file is a plain file.
1.1 deraadt 320: */
321: int
1.20 millert 322: rm(char *name)
1.1 deraadt 323: {
324: struct stat sb;
325:
326: if (stat(name, &sb) < 0)
327: return(-1);
328: if (!S_ISREG(sb.st_mode)) {
329: errno = EISDIR;
330: return(-1);
331: }
1.16 millert 332: if (unlink(name) == -1) {
333: if (errno == EPERM)
334: return(truncate(name, 0));
335: else
336: return(-1);
337: }
338: return(0);
1.1 deraadt 339: }
340:
341: static int sigdepth; /* depth of holdsigs() */
1.2 deraadt 342: static sigset_t nset, oset;
1.1 deraadt 343: /*
344: * Hold signals SIGHUP, SIGINT, and SIGQUIT.
345: */
346: void
1.20 millert 347: holdsigs(void)
1.1 deraadt 348: {
349:
1.2 deraadt 350: if (sigdepth++ == 0) {
351: sigemptyset(&nset);
352: sigaddset(&nset, SIGHUP);
353: sigaddset(&nset, SIGINT);
354: sigaddset(&nset, SIGQUIT);
355: sigprocmask(SIG_BLOCK, &nset, &oset);
356: }
1.1 deraadt 357: }
358:
359: /*
360: * Release signals SIGHUP, SIGINT, and SIGQUIT.
361: */
362: void
1.20 millert 363: relsesigs(void)
1.1 deraadt 364: {
365:
366: if (--sigdepth == 0)
1.2 deraadt 367: sigprocmask(SIG_SETMASK, &oset, NULL);
1.1 deraadt 368: }
369:
370: /*
1.19 millert 371: * Unblock and ignore a signal
372: */
373: int
1.20 millert 374: ignoresig(int sig, struct sigaction *oact, sigset_t *oset)
1.19 millert 375: {
376: struct sigaction act;
377: sigset_t nset;
378: int error;
379:
380: sigemptyset(&act.sa_mask);
381: act.sa_flags = SA_RESTART;
382: act.sa_handler = SIG_IGN;
383: error = sigaction(sig, &act, oact);
384:
385: if (error == 0) {
386: sigemptyset(&nset);
387: sigaddset(&nset, sig);
388: (void)sigprocmask(SIG_UNBLOCK, &nset, oset);
389: } else if (oset != NULL)
390: (void)sigprocmask(SIG_BLOCK, NULL, oset);
391:
392: return(error);
393: }
394:
395: /*
1.1 deraadt 396: * Determine the size of the file possessed by
397: * the passed buffer.
398: */
399: off_t
1.20 millert 400: fsize(FILE *iob)
1.1 deraadt 401: {
402: struct stat sbuf;
403:
404: if (fstat(fileno(iob), &sbuf) < 0)
1.6 millert 405: return(0);
406: return(sbuf.st_size);
1.1 deraadt 407: }
408:
409: /*
410: * Evaluate the string given as a new mailbox name.
411: * Supported meta characters:
412: * % for my system mail box
413: * %user for user's system mail box
414: * # for previous file
415: * & invoker's mbox file
416: * +file file in folder directory
417: * any shell meta character
418: * Return the file name as a dynamic string.
419: */
420: char *
1.20 millert 421: expand(char *name)
1.1 deraadt 422: {
423: char xname[PATHSIZE];
424: char cmdbuf[PATHSIZE]; /* also used for file names */
1.20 millert 425: pid_t pid;
426: int l;
1.14 millert 427: char *cp, *shell;
1.1 deraadt 428: int pivec[2];
429: struct stat sbuf;
1.12 millert 430: extern int wait_status;
1.1 deraadt 431:
432: /*
433: * The order of evaluation is "%" and "#" expand into constants.
434: * "&" can expand into "+". "+" can expand into shell meta characters.
435: * Shell meta characters expand into constants.
436: * This way, we make no recursive expansion.
437: */
438: switch (*name) {
439: case '%':
1.6 millert 440: findmail(name[1] ? name + 1 : myname, xname, sizeof(xname));
441: return(savestr(xname));
1.1 deraadt 442: case '#':
443: if (name[1] != 0)
444: break;
445: if (prevfile[0] == 0) {
1.6 millert 446: puts("No previous file");
1.8 millert 447: return(NULL);
1.1 deraadt 448: }
1.6 millert 449: return(savestr(prevfile));
1.1 deraadt 450: case '&':
1.8 millert 451: if (name[1] == 0 && (name = value("MBOX")) == NULL)
1.1 deraadt 452: name = "~/mbox";
453: /* fall through */
454: }
1.6 millert 455: if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) {
1.10 millert 456: (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1);
1.1 deraadt 457: name = savestr(xname);
458: }
459: /* catch the most common shell meta character */
1.17 millert 460: if (name[0] == '~' && homedir && (name[1] == '/' || name[1] == '\0')) {
1.10 millert 461: (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1);
1.1 deraadt 462: name = savestr(xname);
463: }
1.20 millert 464: if (strpbrk(name, "~{[*?$`'\"\\") == NULL)
1.6 millert 465: return(name);
1.20 millert 466: /* XXX - just use glob(3) and env expansion instead? */
1.1 deraadt 467: if (pipe(pivec) < 0) {
1.6 millert 468: warn("pipe");
469: return(name);
1.1 deraadt 470: }
1.10 millert 471: (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
1.18 millert 472: shell = value("SHELL");
1.8 millert 473: pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL);
1.1 deraadt 474: if (pid < 0) {
1.6 millert 475: (void)close(pivec[0]);
476: (void)close(pivec[1]);
1.8 millert 477: return(NULL);
1.6 millert 478: }
479: (void)close(pivec[1]);
1.18 millert 480: l = myread(pivec[0], xname, PATHSIZE);
481: if (l < 0)
482: warn("read"); /* report error before errno changes */
1.6 millert 483: (void)close(pivec[0]);
1.12 millert 484: if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) &&
485: WTERMSIG(wait_status) != SIGPIPE) {
1.1 deraadt 486: fprintf(stderr, "\"%s\": Expansion failed.\n", name);
1.8 millert 487: return(NULL);
1.1 deraadt 488: }
1.18 millert 489: if (l < 0)
1.8 millert 490: return(NULL);
1.1 deraadt 491: if (l == 0) {
492: fprintf(stderr, "\"%s\": No match.\n", name);
1.8 millert 493: return(NULL);
1.1 deraadt 494: }
1.6 millert 495: if (l == PATHSIZE) {
1.1 deraadt 496: fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
1.8 millert 497: return(NULL);
1.1 deraadt 498: }
1.6 millert 499: xname[l] = '\0';
1.1 deraadt 500: for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
501: ;
502: cp[1] = '\0';
1.3 millert 503: if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
1.1 deraadt 504: fprintf(stderr, "\"%s\": Ambiguous.\n", name);
1.8 millert 505: return(NULL);
1.1 deraadt 506: }
1.6 millert 507: return(savestr(xname));
1.1 deraadt 508: }
509:
510: /*
511: * Determine the current folder directory name.
512: */
513: int
1.20 millert 514: getfold(char *name, int namelen)
1.1 deraadt 515: {
516: char *folder;
517:
1.8 millert 518: if ((folder = value("folder")) == NULL)
1.6 millert 519: return(-1);
1.20 millert 520: if (*folder == '/')
521: strlcpy(name, folder, namelen);
522: else
1.17 millert 523: (void)snprintf(name, namelen, "%s/%s", homedir ? homedir : ".",
524: folder);
1.6 millert 525: return(0);
1.1 deraadt 526: }
527:
528: /*
529: * Return the name of the dead.letter file.
530: */
531: char *
1.20 millert 532: getdeadletter(void)
1.1 deraadt 533: {
1.14 millert 534: char *cp;
1.1 deraadt 535:
1.8 millert 536: if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
1.1 deraadt 537: cp = expand("~/dead.letter");
538: else if (*cp != '/') {
539: char buf[PATHSIZE];
540:
1.7 millert 541: (void)snprintf(buf, sizeof(buf), "~/%s", cp);
1.1 deraadt 542: cp = expand(buf);
543: }
1.6 millert 544: return(cp);
1.19 millert 545: }
546:
547: /*
548: * Signal handler used by readline() to catch SIGINT, SIGHUP, SIGTSTP,
549: * SIGTTOU, SIGTTIN.
550: */
551: void
1.20 millert 552: fioint(int s)
1.19 millert 553: {
554:
555: fiosignal = s;
1.1 deraadt 556: }