1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: static char *sccsid = "@(#)fio.c 5.3 (Berkeley) 9/5/85"; 9: #endif not lint 10: 11: #include "rcv.h" 12: #include <sys/stat.h> 13: #include <errno.h> 14: 15: /* 16: * Mail -- a mail program 17: * 18: * File I/O. 19: */ 20: 21: /* 22: * Set up the input pointers while copying the mail file into 23: * /tmp. 24: */ 25: 26: setptr(ibuf) 27: FILE *ibuf; 28: { 29: register int c; 30: register char *cp, *cp2; 31: register int count, l; 32: long s; 33: off_t offset; 34: char linebuf[LINESIZE]; 35: char wbuf[LINESIZE]; 36: int maybe, mestmp, flag, inhead; 37: struct message this; 38: extern char tempSet[]; 39: 40: if ((mestmp = opentemp(tempSet)) < 0) 41: exit(1); 42: msgCount = 0; 43: offset = 0; 44: s = 0L; 45: l = 0; 46: maybe = 1; 47: flag = MUSED|MNEW; 48: for (;;) { 49: if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 50: this.m_flag = flag; 51: flag = MUSED|MNEW; 52: this.m_offset = offsetof(offset); 53: this.m_block = blockof(offset); 54: this.m_size = s; 55: this.m_lines = l; 56: if (append(&this, mestmp)) { 57: perror(tempSet); 58: exit(1); 59: } 60: fclose(ibuf); 61: makemessage(mestmp); 62: close(mestmp); 63: return; 64: } 65: count = strlen(linebuf); 66: fputs(linebuf, otf); 67: cp = linebuf + (count - 1); 68: if (*cp == '\n') 69: *cp = 0; 70: if (ferror(otf)) { 71: perror("/tmp"); 72: exit(1); 73: } 74: if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 75: msgCount++; 76: this.m_flag = flag; 77: flag = MUSED|MNEW; 78: inhead = 1; 79: this.m_block = blockof(offset); 80: this.m_offset = offsetof(offset); 81: this.m_size = s; 82: this.m_lines = l; 83: s = 0L; 84: l = 0; 85: if (append(&this, mestmp)) { 86: perror(tempSet); 87: exit(1); 88: } 89: } 90: if (linebuf[0] == 0) 91: inhead = 0; 92: if (inhead && (cp = index(linebuf, ':'))) { 93: *cp = 0; 94: if (icequal(linebuf, "status")) { 95: ++cp; 96: if (index(cp, 'R')) 97: flag |= MREAD; 98: if (index(cp, 'O')) 99: flag &= ~MNEW; 100: inhead = 0; 101: } 102: } 103: offset += count; 104: s += (long) count; 105: l++; 106: maybe = 0; 107: if (linebuf[0] == 0) 108: maybe = 1; 109: } 110: } 111: 112: /* 113: * Drop the passed line onto the passed output buffer. 114: * If a write error occurs, return -1, else the count of 115: * characters written, including the newline. 116: */ 117: 118: putline(obuf, linebuf) 119: FILE *obuf; 120: char *linebuf; 121: { 122: register int c; 123: 124: c = strlen(linebuf); 125: fputs(linebuf, obuf); 126: putc('\n', obuf); 127: if (ferror(obuf)) 128: return(-1); 129: return(c+1); 130: } 131: 132: /* 133: * Read up a line from the specified input into the line 134: * buffer. Return the number of characters read. Do not 135: * include the newline at the end. 136: */ 137: 138: readline(ibuf, linebuf) 139: FILE *ibuf; 140: char *linebuf; 141: { 142: register int n; 143: 144: clearerr(ibuf); 145: if (fgets(linebuf, LINESIZE, ibuf) == NULL) 146: return(0); 147: n = strlen(linebuf); 148: if (n >= 1 && linebuf[n-1] == '\n') 149: linebuf[n-1] = '\0'; 150: return(n); 151: } 152: 153: /* 154: * Return a file buffer all ready to read up the 155: * passed message pointer. 156: */ 157: 158: FILE * 159: setinput(mp) 160: register struct message *mp; 161: { 162: off_t off; 163: 164: fflush(otf); 165: off = mp->m_block; 166: off <<= 9; 167: off += mp->m_offset; 168: if (fseek(itf, off, 0) < 0) { 169: perror("fseek"); 170: panic("temporary file seek"); 171: } 172: return(itf); 173: } 174: 175: /* 176: * Take the data out of the passed ghost file and toss it into 177: * a dynamically allocated message structure. 178: */ 179: 180: makemessage(f) 181: { 182: register struct message *m; 183: register char *mp; 184: register count; 185: 186: mp = calloc((unsigned) (msgCount + 1), sizeof *m); 187: if (mp == NOSTR) { 188: printf("Insufficient memory for %d messages\n", msgCount); 189: exit(1); 190: } 191: if (message != (struct message *) 0) 192: cfree((char *) message); 193: message = (struct message *) mp; 194: dot = message; 195: lseek(f, 0L, 0); 196: while (count = read(f, mp, BUFSIZ)) 197: mp += count; 198: for (m = &message[0]; m < &message[msgCount]; m++) { 199: m->m_size = (m+1)->m_size; 200: m->m_lines = (m+1)->m_lines; 201: m->m_flag = (m+1)->m_flag; 202: } 203: message[msgCount].m_size = 0L; 204: message[msgCount].m_lines = 0; 205: } 206: 207: /* 208: * Append the passed message descriptor onto the temp file. 209: * If the write fails, return 1, else 0 210: */ 211: 212: append(mp, f) 213: struct message *mp; 214: { 215: if (write(f, (char *) mp, sizeof *mp) != sizeof *mp) 216: return(1); 217: return(0); 218: } 219: 220: /* 221: * Delete a file, but only if the file is a plain file. 222: */ 223: 224: remove(name) 225: char name[]; 226: { 227: struct stat statb; 228: extern int errno; 229: 230: if (stat(name, &statb) < 0) 231: return(-1); 232: if ((statb.st_mode & S_IFMT) != S_IFREG) { 233: errno = EISDIR; 234: return(-1); 235: } 236: return(unlink(name)); 237: } 238: 239: /* 240: * Terminate an editing session by attempting to write out the user's 241: * file from the temporary. Save any new stuff appended to the file. 242: */ 243: edstop() 244: { 245: register int gotcha, c; 246: register struct message *mp; 247: FILE *obuf, *ibuf, *readstat; 248: struct stat statb; 249: char tempname[30], *id; 250: int (*sigs[3])(); 251: 252: if (readonly) 253: return; 254: holdsigs(); 255: if (Tflag != NOSTR) { 256: if ((readstat = fopen(Tflag, "w")) == NULL) 257: Tflag = NOSTR; 258: } 259: for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) { 260: if (mp->m_flag & MNEW) { 261: mp->m_flag &= ~MNEW; 262: mp->m_flag |= MSTATUS; 263: } 264: if (mp->m_flag & (MODIFY|MDELETED|MSTATUS)) 265: gotcha++; 266: if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) { 267: if ((id = hfield("article-id", mp)) != NOSTR) 268: fprintf(readstat, "%s\n", id); 269: } 270: } 271: if (Tflag != NOSTR) 272: fclose(readstat); 273: if (!gotcha || Tflag != NOSTR) 274: goto done; 275: ibuf = NULL; 276: if (stat(editfile, &statb) >= 0 && statb.st_size > mailsize) { 277: strcpy(tempname, "/tmp/mboxXXXXXX"); 278: mktemp(tempname); 279: if ((obuf = fopen(tempname, "w")) == NULL) { 280: perror(tempname); 281: relsesigs(); 282: reset(0); 283: } 284: if ((ibuf = fopen(editfile, "r")) == NULL) { 285: perror(editfile); 286: fclose(obuf); 287: remove(tempname); 288: relsesigs(); 289: reset(0); 290: } 291: fseek(ibuf, mailsize, 0); 292: while ((c = getc(ibuf)) != EOF) 293: putc(c, obuf); 294: fclose(ibuf); 295: fclose(obuf); 296: if ((ibuf = fopen(tempname, "r")) == NULL) { 297: perror(tempname); 298: remove(tempname); 299: relsesigs(); 300: reset(0); 301: } 302: remove(tempname); 303: } 304: printf("\"%s\" ", editfile); 305: fflush(stdout); 306: if ((obuf = fopen(editfile, "r+")) == NULL) { 307: perror(editfile); 308: relsesigs(); 309: reset(0); 310: } 311: trunc(obuf); 312: c = 0; 313: for (mp = &message[0]; mp < &message[msgCount]; mp++) { 314: if ((mp->m_flag & MDELETED) != 0) 315: continue; 316: c++; 317: if (send(mp, obuf, 0) < 0) { 318: perror(editfile); 319: relsesigs(); 320: reset(0); 321: } 322: } 323: gotcha = (c == 0 && ibuf == NULL); 324: if (ibuf != NULL) { 325: while ((c = getc(ibuf)) != EOF) 326: putc(c, obuf); 327: fclose(ibuf); 328: } 329: fflush(obuf); 330: if (ferror(obuf)) { 331: perror(editfile); 332: relsesigs(); 333: reset(0); 334: } 335: fclose(obuf); 336: if (gotcha) { 337: remove(editfile); 338: printf("removed\n"); 339: } 340: else 341: printf("complete\n"); 342: fflush(stdout); 343: 344: done: 345: relsesigs(); 346: } 347: 348: static int sigdepth = 0; /* depth of holdsigs() */ 349: static int omask = 0; 350: /* 351: * Hold signals SIGHUP - SIGQUIT. 352: */ 353: holdsigs() 354: { 355: register int i; 356: 357: if (sigdepth++ == 0) 358: omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT)); 359: } 360: 361: /* 362: * Release signals SIGHUP - SIGQUIT 363: */ 364: relsesigs() 365: { 366: register int i; 367: 368: if (--sigdepth == 0) 369: sigsetmask(omask); 370: } 371: 372: /* 373: * Open a temp file by creating, closing, unlinking, and 374: * reopening. Return the open file descriptor. 375: */ 376: 377: opentemp(file) 378: char file[]; 379: { 380: register int f; 381: 382: if ((f = creat(file, 0600)) < 0) { 383: perror(file); 384: return(-1); 385: } 386: close(f); 387: if ((f = open(file, 2)) < 0) { 388: perror(file); 389: remove(file); 390: return(-1); 391: } 392: remove(file); 393: return(f); 394: } 395: 396: /* 397: * Determine the size of the file possessed by 398: * the passed buffer. 399: */ 400: 401: off_t 402: fsize(iob) 403: FILE *iob; 404: { 405: register int f; 406: struct stat sbuf; 407: 408: f = fileno(iob); 409: if (fstat(f, &sbuf) < 0) 410: return(0); 411: return(sbuf.st_size); 412: } 413: 414: /* 415: * Take a file name, possibly with shell meta characters 416: * in it and expand it by using "sh -c echo filename" 417: * Return the file name as a dynamic string. 418: */ 419: 420: char * 421: expand(name) 422: char name[]; 423: { 424: char xname[BUFSIZ]; 425: char cmdbuf[BUFSIZ]; 426: register int pid, l, rc; 427: register char *cp, *Shell; 428: int s, pivec[2], (*sigint)(); 429: struct stat sbuf; 430: 431: if (name[0] == '+' && getfold(cmdbuf) >= 0) { 432: sprintf(xname, "%s/%s", cmdbuf, name + 1); 433: return(expand(savestr(xname))); 434: } 435: if (!anyof(name, "~{[*?$`'\"\\")) 436: return(name); 437: if (pipe(pivec) < 0) { 438: perror("pipe"); 439: return(name); 440: } 441: sprintf(cmdbuf, "echo %s", name); 442: if ((pid = vfork()) == 0) { 443: sigchild(); 444: Shell = value("SHELL"); 445: if (Shell == NOSTR) 446: Shell = SHELL; 447: close(pivec[0]); 448: close(1); 449: dup(pivec[1]); 450: close(pivec[1]); 451: close(2); 452: execl(Shell, Shell, "-c", cmdbuf, 0); 453: _exit(1); 454: } 455: if (pid == -1) { 456: perror("fork"); 457: close(pivec[0]); 458: close(pivec[1]); 459: return(NOSTR); 460: } 461: close(pivec[1]); 462: l = read(pivec[0], xname, BUFSIZ); 463: close(pivec[0]); 464: while (wait(&s) != pid); 465: ; 466: s &= 0377; 467: if (s != 0 && s != SIGPIPE) { 468: fprintf(stderr, "\"Echo\" failed\n"); 469: goto err; 470: } 471: if (l < 0) { 472: perror("read"); 473: goto err; 474: } 475: if (l == 0) { 476: fprintf(stderr, "\"%s\": No match\n", name); 477: goto err; 478: } 479: if (l == BUFSIZ) { 480: fprintf(stderr, "Buffer overflow expanding \"%s\"\n", name); 481: goto err; 482: } 483: xname[l] = 0; 484: for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 485: ; 486: *++cp = '\0'; 487: if (any(' ', xname) && stat(xname, &sbuf) < 0) { 488: fprintf(stderr, "\"%s\": Ambiguous\n", name); 489: goto err; 490: } 491: return(savestr(xname)); 492: 493: err: 494: return(NOSTR); 495: } 496: 497: /* 498: * Determine the current folder directory name. 499: */ 500: getfold(name) 501: char *name; 502: { 503: char *folder; 504: 505: if ((folder = value("folder")) == NOSTR) 506: return(-1); 507: if (*folder == '/') 508: strcpy(name, folder); 509: else 510: sprintf(name, "%s/%s", homedir, folder); 511: return(0); 512: } 513: 514: /* 515: * A nicer version of Fdopen, which allows us to fclose 516: * without losing the open file. 517: */ 518: 519: FILE * 520: Fdopen(fildes, mode) 521: char *mode; 522: { 523: register int f; 524: FILE *fdopen(); 525: 526: f = dup(fildes); 527: if (f < 0) { 528: perror("dup"); 529: return(NULL); 530: } 531: return(fdopen(f, mode)); 532: }