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