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