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