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