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