1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. 4: * 5: * Redistribution and use in source and binary forms, with or without 6: * modification, are permitted provided that the following conditions 7: * are met: 8: * 1. Redistributions of source code must retain the above copyright 9: * notice, this list of conditions and the following disclaimer. 10: * 2. Redistributions in binary form must reproduce the above copyright 11: * notice, this list of conditions and the following disclaimer in the 12: * documentation and/or other materials provided with the distribution. 13: * 3. All advertising materials mentioning features or use of this software 14: * must display the following acknowledgement: 15: * This product includes software developed by the University of 16: * California, Berkeley and its contributors. 17: * 4. Neither the name of the University nor the names of its contributors 18: * may be used to endorse or promote products derived from this software 19: * without specific prior written permission. 20: * 21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31: * SUCH DAMAGE. 32: */ 33: 34: #if !defined(lint) && defined(DOSCCS) 35: static char sccsid[] = "@(#)fio.c 5.24.2 (2.11BSD) 1997/11/3"; 36: #endif 37: 38: #include "rcv.h" 39: #include <sys/stat.h> 40: #include <sys/file.h> 41: #include <sys/wait.h> 42: #include <paths.h> 43: #include <errno.h> 44: 45: /* 46: * Mail -- a mail program 47: * 48: * File I/O. 49: */ 50: 51: /* 52: * Set up the input pointers while copying the mail file into /tmp. 53: */ 54: setptr(ibuf) 55: register FILE *ibuf; 56: { 57: register int c, count; 58: register char *cp, *cp2; 59: struct message this; 60: FILE *mestmp; 61: off_t offset; 62: int maybe, inhead; 63: char linebuf[LINESIZE]; 64: 65: /* Get temporary file. */ 66: (void)sprintf(linebuf, "%smail.XXXXXX", _PATH_TMP); 67: if ((c = mkstemp(linebuf)) == -1 || 68: (mestmp = Fdopen(c, "r+")) == NULL) { 69: (void)fprintf(stderr, "mail: can't open %s\n", linebuf); 70: exit(1); 71: } 72: (void)unlink(linebuf); 73: 74: msgCount = 0; 75: maybe = 1; 76: inhead = 0; 77: offset = 0; 78: this.m_flag = MUSED|MNEW; 79: this.m_size = 0; 80: this.m_lines = 0; 81: this.m_block = 0; 82: this.m_offset = 0; 83: for (;;) { 84: if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 85: if (append(&this, mestmp)) { 86: perror("temporary file"); 87: exit(1); 88: } 89: makemessage(mestmp); 90: return; 91: } 92: count = strlen(linebuf); 93: (void) fwrite(linebuf, sizeof *linebuf, count, otf); 94: if (ferror(otf)) { 95: perror("/tmp"); 96: exit(1); 97: } 98: linebuf[count - 1] = 0; 99: if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 100: msgCount++; 101: if (append(&this, mestmp)) { 102: perror("temporary file"); 103: exit(1); 104: } 105: this.m_flag = MUSED|MNEW; 106: this.m_size = 0; 107: this.m_lines = 0; 108: this.m_block = blockof(offset); 109: this.m_offset = offstof(offset); 110: inhead = 1; 111: } else if (linebuf[0] == 0) { 112: inhead = 0; 113: } else if (inhead) { 114: for (cp = linebuf, cp2 = "status";; cp++) { 115: if ((c = *cp2++) == 0) { 116: while (isspace(*cp++)) 117: ; 118: if (cp[-1] != ':') 119: break; 120: while (c = *cp++) 121: if (c == 'R') 122: this.m_flag |= MREAD; 123: else if (c == 'O') 124: this.m_flag &= ~MNEW; 125: inhead = 0; 126: break; 127: } 128: if (*cp != c && *cp != toupper(c)) 129: break; 130: } 131: } 132: offset += count; 133: this.m_size += count; 134: this.m_lines++; 135: maybe = linebuf[0] == 0; 136: } 137: } 138: 139: /* 140: * Drop the passed line onto the passed output buffer. 141: * If a write error occurs, return -1, else the count of 142: * characters written, including the newline. 143: */ 144: putline(obuf, linebuf) 145: register FILE *obuf; 146: char *linebuf; 147: { 148: register int c; 149: 150: c = strlen(linebuf); 151: (void) fwrite(linebuf, sizeof *linebuf, c, obuf); 152: (void) putc('\n', obuf); 153: if (ferror(obuf)) 154: return (-1); 155: return (c + 1); 156: } 157: 158: /* 159: * Read up a line from the specified input into the line 160: * buffer. Return the number of characters read. Do not 161: * include the newline at the end. 162: */ 163: readline(ibuf, linebuf, linesize) 164: FILE *ibuf; 165: register char *linebuf; 166: { 167: register int n; 168: 169: clearerr(ibuf); 170: if (fgets(linebuf, linesize, ibuf) == NULL) 171: return -1; 172: n = strlen(linebuf); 173: if (n > 0 && linebuf[n - 1] == '\n') 174: linebuf[--n] = '\0'; 175: return n; 176: } 177: 178: /* 179: * Return a file buffer all ready to read up the 180: * passed message pointer. 181: */ 182: FILE * 183: setinput(mp) 184: register struct message *mp; 185: { 186: 187: fflush(otf); 188: if (fseek(itf, positionof(mp->m_block, mp->m_offset), 0) < 0) { 189: perror("fseek"); 190: panic("temporary file seek"); 191: } 192: return (itf); 193: } 194: 195: /* 196: * Take the data out of the passed ghost file and toss it into 197: * a dynamically allocated message structure. 198: */ 199: makemessage(f) 200: FILE *f; 201: { 202: register size = (msgCount + 1) * sizeof (struct message); 203: 204: if (message != 0) 205: free((char *) message); 206: if ((message = (struct message *) malloc((unsigned) size)) == 0) 207: panic("Insufficient memory for %d messages", msgCount); 208: dot = message; 209: size -= sizeof (struct message); 210: fflush(f); 211: (void) lseek(fileno(f), (long) sizeof *message, 0); 212: if (read(fileno(f), (char *) message, size) != size) 213: panic("Message temporary file corrupted"); 214: message[msgCount].m_size = 0; 215: message[msgCount].m_lines = 0; 216: Fclose(f); 217: } 218: 219: /* 220: * Append the passed message descriptor onto the temp file. 221: * If the write fails, return 1, else 0 222: */ 223: append(mp, f) 224: struct message *mp; 225: FILE *f; 226: { 227: return fwrite((char *) mp, sizeof *mp, 1, f) != 1; 228: } 229: 230: /* 231: * Delete a file, but only if the file is a plain file. 232: */ 233: rm(name) 234: char *name; 235: { 236: struct stat sb; 237: 238: if (stat(name, &sb) < 0) 239: return(-1); 240: if (!S_ISREG(sb.st_mode)) { 241: errno = EISDIR; 242: return(-1); 243: } 244: return(unlink(name)); 245: } 246: 247: static int sigdepth; /* depth of holdsigs() */ 248: static long omask; 249: /* 250: * Hold signals SIGHUP, SIGINT, and SIGQUIT. 251: */ 252: holdsigs() 253: { 254: 255: if (sigdepth++ == 0) 256: omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)); 257: } 258: 259: /* 260: * Release signals SIGHUP, SIGINT, and SIGQUIT. 261: */ 262: relsesigs() 263: { 264: 265: if (--sigdepth == 0) 266: sigsetmask(omask); 267: } 268: 269: /* 270: * Determine the size of the file possessed by 271: * the passed buffer. 272: */ 273: off_t 274: fsize(iob) 275: FILE *iob; 276: { 277: struct stat sbuf; 278: 279: if (fstat(fileno(iob), &sbuf) < 0) 280: return 0; 281: return sbuf.st_size; 282: } 283: 284: /* 285: * Evaluate the string given as a new mailbox name. 286: * Supported meta characters: 287: * % for my system mail box 288: * %user for user's system mail box 289: * # for previous file 290: * & invoker's mbox file 291: * +file file in folder directory 292: * any shell meta character 293: * Return the file name as a dynamic string. 294: */ 295: char * 296: expand(name) 297: register char *name; 298: { 299: char xname[PATHSIZE]; 300: char cmdbuf[PATHSIZE]; /* also used for file names */ 301: register int pid, l; 302: register char *cp, *shell; 303: int pivec[2]; 304: struct stat sbuf; 305: extern union wait wait_status; 306: 307: /* 308: * The order of evaluation is "%" and "#" expand into constants. 309: * "&" can expand into "+". "+" can expand into shell meta characters. 310: * Shell meta characters expand into constants. 311: * This way, we make no recursive expansion. 312: */ 313: switch (*name) { 314: case '%': 315: findmail(name[1] ? name + 1 : myname, xname); 316: return savestr(xname); 317: case '#': 318: if (name[1] != 0) 319: break; 320: if (prevfile[0] == 0) { 321: printf("No previous file\n"); 322: return NOSTR; 323: } 324: return savestr(prevfile); 325: case '&': 326: if (name[1] == 0 && (name = value("MBOX")) == NOSTR) 327: name = "~/mbox"; 328: /* fall through */ 329: } 330: if (name[0] == '+' && getfold(cmdbuf) >= 0) { 331: sprintf(xname, "%s/%s", cmdbuf, name + 1); 332: name = savestr(xname); 333: } 334: /* catch the most common shell meta character */ 335: if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 336: sprintf(xname, "%s%s", homedir, name + 1); 337: name = savestr(xname); 338: } 339: if (!anyof(name, "~{[*?$`'\"\\")) 340: return name; 341: if (pipe(pivec) < 0) { 342: perror("pipe"); 343: return name; 344: } 345: sprintf(cmdbuf, "echo %s", name); 346: if ((shell = value("SHELL")) == NOSTR) 347: shell = _PATH_CSHELL; 348: pid = start_command(shell, 0L, -1, pivec[1], "-c", cmdbuf, NOSTR); 349: if (pid < 0) { 350: close(pivec[0]); 351: close(pivec[1]); 352: return NOSTR; 353: } 354: close(pivec[1]); 355: l = read(pivec[0], xname, BUFSIZ); 356: close(pivec[0]); 357: if (wait_child(pid) < 0 && wait_status.w_termsig != SIGPIPE) { 358: fprintf(stderr, "\"%s\": Expansion failed.\n", name); 359: return NOSTR; 360: } 361: if (l < 0) { 362: perror("read"); 363: return NOSTR; 364: } 365: if (l == 0) { 366: fprintf(stderr, "\"%s\": No match.\n", name); 367: return NOSTR; 368: } 369: if (l == BUFSIZ) { 370: fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 371: return NOSTR; 372: } 373: xname[l] = 0; 374: for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 375: ; 376: cp[1] = '\0'; 377: if (index(xname, ' ') && stat(xname, &sbuf) < 0) { 378: fprintf(stderr, "\"%s\": Ambiguous.\n", name); 379: return NOSTR; 380: } 381: return savestr(xname); 382: } 383: 384: /* 385: * Determine the current folder directory name. 386: */ 387: getfold(name) 388: char *name; 389: { 390: register char *folder; 391: 392: if ((folder = value("folder")) == NOSTR) 393: return (-1); 394: if (*folder == '/') 395: strcpy(name, folder); 396: else 397: sprintf(name, "%s/%s", homedir, folder); 398: return (0); 399: } 400: 401: /* 402: * Return the name of the dead.letter file. 403: */ 404: char * 405: getdeadletter() 406: { 407: register char *cp; 408: 409: if ((cp = value("DEAD")) == NOSTR || (cp = expand(cp)) == NOSTR) 410: cp = expand("~/dead.letter"); 411: else if (*cp != '/') { 412: char buf[PATHSIZE]; 413: 414: (void) sprintf(buf, "~/%s", cp); 415: cp = expand(buf); 416: } 417: return cp; 418: }