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