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