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