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