Annotation of src/usr.bin/mail/fio.c, Revision 1.29
1.29 ! tobias 1: /* $OpenBSD: fio.c,v 1.28 2007/05/25 21:27:16 krw 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.29 ! tobias 37: static const char rcsid[] = "$OpenBSD: fio.c,v 1.28 2007/05/25 21:27:16 krw 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.29 ! tobias 94: (void)fseeko(ibuf, offset, SEEK_SET);
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.29 ! tobias 271: if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), SEEK_SET)
! 272: < 0)
1.14 millert 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.26 deraadt 299: (void)lseek(fileno(f), (off_t)sizeof(*message), SEEK_SET);
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)
1.25 deraadt 334: return(truncate(name, (off_t)0));
1.16 millert 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: }