1 /* $OpenBSD: fio.c,v 1.24 2003/07/07 21:36:51 deraadt Exp $ */ 2 /* $NetBSD: fio.c,v 1.8 1997/07/07 22:57:55 phil Exp $ */ 3 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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #ifndef lint 34 #if 0 35 static const char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 36 #else 37 static const char rcsid[] = "$OpenBSD: fio.c,v 1.24 2003/07/07 21:36:51 deraadt Exp $"; 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include <sys/file.h> 43 #include <sys/wait.h> 44 45 #include <unistd.h> 46 #include <paths.h> 47 #include <errno.h> 48 #include "extern.h" 49 50 /* 51 * Mail -- a mail program 52 * 53 * File I/O. 54 */ 55 56 static volatile sig_atomic_t fiosignal; 57 58 /* 59 * Wrapper for read() to catch EINTR. 60 */ 61 static ssize_t 62 myread(int fd, char *buf, int len) 63 { 64 ssize_t nread; 65 66 while ((nread = read(fd, buf, len)) == -1 && errno == EINTR) 67 ; 68 return(nread); 69 } 70 71 /* 72 * Set up the input pointers while copying the mail file into /tmp. 73 */ 74 void 75 setptr(FILE *ibuf, off_t offset) 76 { 77 int c, count; 78 char *cp, *cp2; 79 struct message this; 80 FILE *mestmp; 81 int maybe, inhead, omsgCount; 82 char linebuf[LINESIZE], pathbuf[PATHSIZE]; 83 84 /* Get temporary file. */ 85 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 86 if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 87 err(1, "can't open %s", pathbuf); 88 (void)rm(pathbuf); 89 90 if (offset == 0) { 91 msgCount = 0; 92 } else { 93 /* Seek into the file to get to the new messages */ 94 (void)fseek(ibuf, offset, 0); 95 /* 96 * We need to make "offset" a pointer to the end of 97 * the temp file that has the copy of the mail file. 98 * If any messages have been edited, this will be 99 * different from the offset into the mail file. 100 */ 101 (void)fseek(otf, 0L, SEEK_END); 102 offset = ftell(otf); 103 } 104 omsgCount = msgCount; 105 maybe = 1; 106 inhead = 0; 107 this.m_flag = MUSED|MNEW; 108 this.m_size = 0; 109 this.m_lines = 0; 110 this.m_block = 0; 111 this.m_offset = 0; 112 for (;;) { 113 if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 114 if (append(&this, mestmp)) 115 err(1, "temporary file"); 116 makemessage(mestmp, omsgCount); 117 return; 118 } 119 count = strlen(linebuf); 120 /* 121 * Transforms lines ending in <CR><LF> to just <LF>. 122 * This allows mail to be able to read Eudora mailboxes 123 * that reside on a DOS partition. 124 */ 125 if (count >= 2 && linebuf[count-1] == '\n' && 126 linebuf[count - 2] == '\r') 127 linebuf[count - 2] = linebuf[--count]; 128 129 (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 130 if (ferror(otf)) 131 err(1, "/tmp"); 132 if (count) 133 linebuf[count - 1] = '\0'; 134 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 135 msgCount++; 136 if (append(&this, mestmp)) 137 err(1, "temporary file"); 138 this.m_flag = MUSED|MNEW; 139 this.m_size = 0; 140 this.m_lines = 0; 141 this.m_block = blockof(offset); 142 this.m_offset = offsetof(offset); 143 inhead = 1; 144 } else if (linebuf[0] == 0) { 145 inhead = 0; 146 } else if (inhead) { 147 for (cp = linebuf, cp2 = "status";; cp++) { 148 if ((c = *cp2++) == 0) { 149 while (isspace(*cp++)) 150 ; 151 if (cp[-1] != ':') 152 break; 153 while ((c = *cp++) != '\0') 154 if (c == 'R') 155 this.m_flag |= MREAD; 156 else if (c == 'O') 157 this.m_flag &= ~MNEW; 158 inhead = 0; 159 break; 160 } 161 if (*cp != c && *cp != toupper(c)) 162 break; 163 } 164 } 165 offset += count; 166 this.m_size += count; 167 this.m_lines++; 168 maybe = linebuf[0] == 0; 169 } 170 } 171 172 /* 173 * Drop the passed line onto the passed output buffer. 174 * If a write error occurs, return -1, else the count of 175 * characters written, including the newline if requested. 176 */ 177 int 178 putline(FILE *obuf, char *linebuf, int outlf) 179 { 180 int c; 181 182 c = strlen(linebuf); 183 (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 184 if (outlf) { 185 (void)putc('\n', obuf); 186 c++; 187 } 188 if (ferror(obuf)) 189 return(-1); 190 return(c); 191 } 192 193 /* 194 * Read up a line from the specified input into the line 195 * buffer. Return the number of characters read. Do not 196 * include the newline (or carriage return) at the end. 197 */ 198 int 199 readline(FILE *ibuf, char *linebuf, int linesize, int *signo) 200 { 201 struct sigaction act; 202 struct sigaction savetstp; 203 struct sigaction savettou; 204 struct sigaction savettin; 205 struct sigaction saveint; 206 struct sigaction savehup; 207 sigset_t oset; 208 int n; 209 210 /* 211 * Setup signal handlers if the caller asked us to catch signals. 212 * Note that we do not restart system calls since we need the 213 * read to be interuptible. 214 */ 215 if (signo) { 216 fiosignal = 0; 217 sigemptyset(&act.sa_mask); 218 act.sa_flags = 0; 219 act.sa_handler = fioint; 220 if (sigaction(SIGINT, NULL, &saveint) == 0 && 221 saveint.sa_handler != SIG_IGN) { 222 (void)sigaction(SIGINT, &act, &saveint); 223 (void)sigprocmask(SIG_UNBLOCK, &intset, &oset); 224 } 225 if (sigaction(SIGHUP, NULL, &savehup) == 0 && 226 savehup.sa_handler != SIG_IGN) 227 (void)sigaction(SIGHUP, &act, &savehup); 228 (void)sigaction(SIGTSTP, &act, &savetstp); 229 (void)sigaction(SIGTTOU, &act, &savettou); 230 (void)sigaction(SIGTTIN, &act, &savettin); 231 } 232 233 clearerr(ibuf); 234 if (fgets(linebuf, linesize, ibuf) == NULL) { 235 if (ferror(ibuf)) 236 clearerr(ibuf); 237 n = -1; 238 } else { 239 n = strlen(linebuf); 240 if (n > 0 && linebuf[n - 1] == '\n') 241 linebuf[--n] = '\0'; 242 if (n > 0 && linebuf[n - 1] == '\r') 243 linebuf[--n] = '\0'; 244 } 245 246 if (signo) { 247 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 248 (void)sigaction(SIGINT, &saveint, NULL); 249 (void)sigaction(SIGHUP, &savehup, NULL); 250 (void)sigaction(SIGTSTP, &savetstp, NULL); 251 (void)sigaction(SIGTTOU, &savettou, NULL); 252 (void)sigaction(SIGTTIN, &savettin, NULL); 253 *signo = fiosignal; 254 } 255 256 return(n); 257 } 258 259 /* 260 * Return a file buffer all ready to read up the 261 * passed message pointer. 262 */ 263 FILE * 264 setinput(struct message *mp) 265 { 266 267 fflush(otf); 268 if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) 269 err(1, "fseek"); 270 return(itf); 271 } 272 273 /* 274 * Take the data out of the passed ghost file and toss it into 275 * a dynamically allocated message structure. 276 */ 277 void 278 makemessage(FILE *f, int omsgCount) 279 { 280 size_t size; 281 struct message *nmessage; 282 283 size = (msgCount + 1) * sizeof(struct message); 284 nmessage = (struct message *)realloc(message, size); 285 if (nmessage == 0) 286 errx(1, "Insufficient memory for %d messages", 287 msgCount); 288 if (omsgCount == 0 || message == NULL) 289 dot = nmessage; 290 else 291 dot = nmessage + (dot - message); 292 message = nmessage; 293 size -= (omsgCount + 1) * sizeof(struct message); 294 fflush(f); 295 (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 296 if (myread(fileno(f), (void *) &message[omsgCount], size) != size) 297 errx(1, "Message temporary file corrupted"); 298 message[msgCount].m_size = 0; 299 message[msgCount].m_lines = 0; 300 (void)Fclose(f); 301 } 302 303 /* 304 * Append the passed message descriptor onto the temp file. 305 * If the write fails, return 1, else 0 306 */ 307 int 308 append(struct message *mp, FILE *f) 309 { 310 311 return(fwrite((char *) mp, sizeof(*mp), 1, f) != 1); 312 } 313 314 /* 315 * Delete or truncate a file, but only if the file is a plain file. 316 */ 317 int 318 rm(char *name) 319 { 320 struct stat sb; 321 322 if (stat(name, &sb) < 0) 323 return(-1); 324 if (!S_ISREG(sb.st_mode)) { 325 errno = EISDIR; 326 return(-1); 327 } 328 if (unlink(name) == -1) { 329 if (errno == EPERM) 330 return(truncate(name, 0)); 331 else 332 return(-1); 333 } 334 return(0); 335 } 336 337 static int sigdepth; /* depth of holdsigs() */ 338 static sigset_t nset, oset; 339 /* 340 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 341 */ 342 void 343 holdsigs(void) 344 { 345 346 if (sigdepth++ == 0) { 347 sigemptyset(&nset); 348 sigaddset(&nset, SIGHUP); 349 sigaddset(&nset, SIGINT); 350 sigaddset(&nset, SIGQUIT); 351 sigprocmask(SIG_BLOCK, &nset, &oset); 352 } 353 } 354 355 /* 356 * Release signals SIGHUP, SIGINT, and SIGQUIT. 357 */ 358 void 359 relsesigs(void) 360 { 361 362 if (--sigdepth == 0) 363 sigprocmask(SIG_SETMASK, &oset, NULL); 364 } 365 366 /* 367 * Unblock and ignore a signal 368 */ 369 int 370 ignoresig(int sig, struct sigaction *oact, sigset_t *oset) 371 { 372 struct sigaction act; 373 sigset_t nset; 374 int error; 375 376 sigemptyset(&act.sa_mask); 377 act.sa_flags = SA_RESTART; 378 act.sa_handler = SIG_IGN; 379 error = sigaction(sig, &act, oact); 380 381 if (error == 0) { 382 sigemptyset(&nset); 383 sigaddset(&nset, sig); 384 (void)sigprocmask(SIG_UNBLOCK, &nset, oset); 385 } else if (oset != NULL) 386 (void)sigprocmask(SIG_BLOCK, NULL, oset); 387 388 return(error); 389 } 390 391 /* 392 * Determine the size of the file possessed by 393 * the passed buffer. 394 */ 395 off_t 396 fsize(FILE *iob) 397 { 398 struct stat sbuf; 399 400 if (fstat(fileno(iob), &sbuf) < 0) 401 return(0); 402 return(sbuf.st_size); 403 } 404 405 /* 406 * Evaluate the string given as a new mailbox name. 407 * Supported meta characters: 408 * % for my system mail box 409 * %user for user's system mail box 410 * # for previous file 411 * & invoker's mbox file 412 * +file file in folder directory 413 * any shell meta character 414 * Return the file name as a dynamic string. 415 */ 416 char * 417 expand(char *name) 418 { 419 char xname[PATHSIZE]; 420 char cmdbuf[PATHSIZE]; /* also used for file names */ 421 pid_t pid; 422 int l; 423 char *cp, *shell; 424 int pivec[2]; 425 struct stat sbuf; 426 extern int wait_status; 427 428 /* 429 * The order of evaluation is "%" and "#" expand into constants. 430 * "&" can expand into "+". "+" can expand into shell meta characters. 431 * Shell meta characters expand into constants. 432 * This way, we make no recursive expansion. 433 */ 434 switch (*name) { 435 case '%': 436 findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 437 return(savestr(xname)); 438 case '#': 439 if (name[1] != 0) 440 break; 441 if (prevfile[0] == 0) { 442 puts("No previous file"); 443 return(NULL); 444 } 445 return(savestr(prevfile)); 446 case '&': 447 if (name[1] == 0 && (name = value("MBOX")) == NULL) 448 name = "~/mbox"; 449 /* fall through */ 450 } 451 if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 452 (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 453 name = savestr(xname); 454 } 455 /* catch the most common shell meta character */ 456 if (name[0] == '~' && homedir && (name[1] == '/' || name[1] == '\0')) { 457 (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 458 name = savestr(xname); 459 } 460 if (strpbrk(name, "~{[*?$`'\"\\") == NULL) 461 return(name); 462 /* XXX - just use glob(3) and env expansion instead? */ 463 if (pipe(pivec) < 0) { 464 warn("pipe"); 465 return(name); 466 } 467 (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 468 shell = value("SHELL"); 469 pid = start_command(shell, 0, -1, pivec[1], "-c", cmdbuf, NULL); 470 if (pid < 0) { 471 (void)close(pivec[0]); 472 (void)close(pivec[1]); 473 return(NULL); 474 } 475 (void)close(pivec[1]); 476 l = myread(pivec[0], xname, PATHSIZE); 477 if (l < 0) 478 warn("read"); /* report error before errno changes */ 479 (void)close(pivec[0]); 480 if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 481 WTERMSIG(wait_status) != SIGPIPE) { 482 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 483 return(NULL); 484 } 485 if (l < 0) 486 return(NULL); 487 if (l == 0) { 488 fprintf(stderr, "\"%s\": No match.\n", name); 489 return(NULL); 490 } 491 if (l == PATHSIZE) { 492 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 493 return(NULL); 494 } 495 xname[l] = '\0'; 496 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 497 ; 498 cp[1] = '\0'; 499 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 500 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 501 return(NULL); 502 } 503 return(savestr(xname)); 504 } 505 506 /* 507 * Determine the current folder directory name. 508 */ 509 int 510 getfold(char *name, int namelen) 511 { 512 char *folder; 513 514 if ((folder = value("folder")) == NULL) 515 return(-1); 516 if (*folder == '/') 517 strlcpy(name, folder, namelen); 518 else 519 (void)snprintf(name, namelen, "%s/%s", homedir ? homedir : ".", 520 folder); 521 return(0); 522 } 523 524 /* 525 * Return the name of the dead.letter file. 526 */ 527 char * 528 getdeadletter(void) 529 { 530 char *cp; 531 532 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 533 cp = expand("~/dead.letter"); 534 else if (*cp != '/') { 535 char buf[PATHSIZE]; 536 537 (void)snprintf(buf, sizeof(buf), "~/%s", cp); 538 cp = expand(buf); 539 } 540 return(cp); 541 } 542 543 /* 544 * Signal handler used by readline() to catch SIGINT, SIGHUP, SIGTSTP, 545 * SIGTTOU, SIGTTIN. 546 */ 547 void 548 fioint(int s) 549 { 550 551 fiosignal = s; 552 } 553