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