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