1 /* 2 * Copyright (c) 1980, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #ifndef lint 35 #if 0 36 static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 37 #endif 38 static const char rcsid[] = 39 "$FreeBSD$"; 40 #endif /* not lint */ 41 42 #include "rcv.h" 43 #include <sys/file.h> 44 #include <sys/wait.h> 45 46 #include <unistd.h> 47 #include <paths.h> 48 #include <errno.h> 49 #include "extern.h" 50 51 /* 52 * Mail -- a mail program 53 * 54 * File I/O. 55 */ 56 57 extern int wait_status; 58 59 /* 60 * Set up the input pointers while copying the mail file into /tmp. 61 */ 62 void 63 setptr(ibuf, offset) 64 FILE *ibuf; 65 off_t offset; 66 { 67 int c, count; 68 char *cp, *cp2; 69 struct message this; 70 FILE *mestmp; 71 int maybe, inhead; 72 char linebuf[LINESIZE], pathbuf[PATHSIZE]; 73 int omsgCount; 74 75 /* Get temporary file. */ 76 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 77 if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 78 err(1, "can't open %s", pathbuf); 79 (void)rm(pathbuf); 80 81 if (offset == 0) { 82 msgCount = 0; 83 } else { 84 /* Seek into the file to get to the new messages */ 85 (void)fseeko(ibuf, offset, SEEK_SET); 86 /* 87 * We need to make "offset" a pointer to the end of 88 * the temp file that has the copy of the mail file. 89 * If any messages have been edited, this will be 90 * different from the offset into the mail file. 91 */ 92 (void)fseeko(otf, (off_t)0, SEEK_END); 93 offset = ftello(otf); 94 } 95 omsgCount = msgCount; 96 maybe = 1; 97 inhead = 0; 98 this.m_flag = MUSED|MNEW; 99 this.m_size = 0; 100 this.m_lines = 0; 101 this.m_block = 0; 102 this.m_offset = 0; 103 for (;;) { 104 if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 105 if (append(&this, mestmp)) 106 errx(1, "temporary file"); 107 makemessage(mestmp, omsgCount); 108 return; 109 } 110 count = strlen(linebuf); 111 /* 112 * Transforms lines ending in <CR><LF> to just <LF>. 113 * This allows mail to be able to read Eudora mailboxes. 114 */ 115 if (count >= 2 && linebuf[count - 1] == '\n' && 116 linebuf[count - 2] == '\r') { 117 count--; 118 linebuf[count - 1] = '\n'; 119 } 120 121 (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 122 if (ferror(otf)) 123 errx(1, "/tmp"); 124 if (count) 125 linebuf[count - 1] = '\0'; 126 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 127 msgCount++; 128 if (append(&this, mestmp)) 129 errx(1, "temporary file"); 130 this.m_flag = MUSED|MNEW; 131 this.m_size = 0; 132 this.m_lines = 0; 133 this.m_block = blockof(offset); 134 this.m_offset = boffsetof(offset); 135 inhead = 1; 136 } else if (linebuf[0] == 0) { 137 inhead = 0; 138 } else if (inhead) { 139 for (cp = linebuf, cp2 = "status";; cp++) { 140 if ((c = *cp2++) == '\0') { 141 while (isspace((unsigned char)*cp++)) 142 ; 143 if (cp[-1] != ':') 144 break; 145 while ((c = *cp++) != '\0') 146 if (c == 'R') 147 this.m_flag |= MREAD; 148 else if (c == 'O') 149 this.m_flag &= ~MNEW; 150 inhead = 0; 151 break; 152 } 153 if (*cp != c && *cp != toupper((unsigned char)c)) 154 break; 155 } 156 } 157 offset += count; 158 this.m_size += count; 159 this.m_lines++; 160 maybe = linebuf[0] == 0; 161 } 162 } 163 164 /* 165 * Drop the passed line onto the passed output buffer. 166 * If a write error occurs, return -1, else the count of 167 * characters written, including the newline if requested. 168 */ 169 int 170 putline(obuf, linebuf, outlf) 171 FILE *obuf; 172 char *linebuf; 173 int outlf; 174 { 175 int c; 176 177 c = strlen(linebuf); 178 (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 179 if (outlf) { 180 fprintf(obuf, "\n"); 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(ibuf, linebuf, linesize) 195 FILE *ibuf; 196 char *linebuf; 197 int linesize; 198 { 199 int n; 200 201 clearerr(ibuf); 202 if (fgets(linebuf, linesize, ibuf) == NULL) 203 return (-1); 204 n = strlen(linebuf); 205 if (n > 0 && linebuf[n - 1] == '\n') 206 linebuf[--n] = '\0'; 207 if (n > 0 && linebuf[n - 1] == '\r') 208 linebuf[--n] = '\0'; 209 return (n); 210 } 211 212 /* 213 * Return a file buffer all ready to read up the 214 * passed message pointer. 215 */ 216 FILE * 217 setinput(mp) 218 struct message *mp; 219 { 220 221 (void)fflush(otf); 222 if (fseeko(itf, 223 positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) 224 err(1, "fseeko"); 225 return (itf); 226 } 227 228 /* 229 * Take the data out of the passed ghost file and toss it into 230 * a dynamically allocated message structure. 231 */ 232 void 233 makemessage(f, omsgCount) 234 FILE *f; 235 int omsgCount; 236 { 237 size_t size; 238 struct message *nmessage; 239 240 size = (msgCount + 1) * sizeof(struct message); 241 nmessage = (struct message *)realloc(message, size); 242 if (nmessage == NULL) 243 errx(1, "Insufficient memory for %d messages\n", 244 msgCount); 245 if (omsgCount == 0 || message == NULL) 246 dot = nmessage; 247 else 248 dot = nmessage + (dot - message); 249 message = nmessage; 250 size -= (omsgCount + 1) * sizeof(struct message); 251 (void)fflush(f); 252 (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 253 if (read(fileno(f), (char *)&message[omsgCount], size) != size) 254 errx(1, "Message temporary file corrupted"); 255 message[msgCount].m_size = 0; 256 message[msgCount].m_lines = 0; 257 (void)Fclose(f); 258 } 259 260 /* 261 * Append the passed message descriptor onto the temp file. 262 * If the write fails, return 1, else 0 263 */ 264 int 265 append(mp, f) 266 struct message *mp; 267 FILE *f; 268 { 269 return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); 270 } 271 272 /* 273 * Delete a file, but only if the file is a plain file. 274 */ 275 int 276 rm(name) 277 char *name; 278 { 279 struct stat sb; 280 281 if (stat(name, &sb) < 0) 282 return (-1); 283 if (!S_ISREG(sb.st_mode)) { 284 errno = EISDIR; 285 return (-1); 286 } 287 return (unlink(name)); 288 } 289 290 static int sigdepth; /* depth of holdsigs() */ 291 static sigset_t nset, oset; 292 /* 293 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 294 */ 295 void 296 holdsigs() 297 { 298 299 if (sigdepth++ == 0) { 300 (void)sigemptyset(&nset); 301 (void)sigaddset(&nset, SIGHUP); 302 (void)sigaddset(&nset, SIGINT); 303 (void)sigaddset(&nset, SIGQUIT); 304 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 305 } 306 } 307 308 /* 309 * Release signals SIGHUP, SIGINT, and SIGQUIT. 310 */ 311 void 312 relsesigs() 313 { 314 315 if (--sigdepth == 0) 316 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 317 } 318 319 /* 320 * Determine the size of the file possessed by 321 * the passed buffer. 322 */ 323 off_t 324 fsize(iob) 325 FILE *iob; 326 { 327 struct stat sbuf; 328 329 if (fstat(fileno(iob), &sbuf) < 0) 330 return (0); 331 return (sbuf.st_size); 332 } 333 334 /* 335 * Evaluate the string given as a new mailbox name. 336 * Supported meta characters: 337 * % for my system mail box 338 * %user for user's system mail box 339 * # for previous file 340 * & invoker's mbox file 341 * +file file in folder directory 342 * any shell meta character 343 * Return the file name as a dynamic string. 344 */ 345 char * 346 expand(name) 347 char *name; 348 { 349 char xname[PATHSIZE]; 350 char cmdbuf[PATHSIZE]; /* also used for file names */ 351 int pid, l; 352 char *cp, *sh; 353 int pivec[2]; 354 struct stat sbuf; 355 356 /* 357 * The order of evaluation is "%" and "#" expand into constants. 358 * "&" can expand into "+". "+" can expand into shell meta characters. 359 * Shell meta characters expand into constants. 360 * This way, we make no recursive expansion. 361 */ 362 switch (*name) { 363 case '%': 364 findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 365 return (savestr(xname)); 366 case '#': 367 if (name[1] != 0) 368 break; 369 if (prevfile[0] == 0) { 370 printf("No previous file\n"); 371 return (NULL); 372 } 373 return (savestr(prevfile)); 374 case '&': 375 if (name[1] == 0 && (name = value("MBOX")) == NULL) 376 name = "~/mbox"; 377 /* fall through */ 378 } 379 if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 380 (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 381 name = savestr(xname); 382 } 383 /* catch the most common shell meta character */ 384 if (name[0] == '~' && homedir != NULL && 385 (name[1] == '/' || name[1] == '\0')) { 386 (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 387 name = savestr(xname); 388 } 389 if (!strpbrk(name, "~{[*?$`'\"\\")) 390 return (name); 391 if (pipe(pivec) < 0) { 392 warn("pipe"); 393 return (name); 394 } 395 (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 396 if ((sh = value("SHELL")) == NULL) 397 sh = _PATH_CSHELL; 398 pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); 399 if (pid < 0) { 400 (void)close(pivec[0]); 401 (void)close(pivec[1]); 402 return (NULL); 403 } 404 (void)close(pivec[1]); 405 l = read(pivec[0], xname, BUFSIZ); 406 (void)close(pivec[0]); 407 if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 408 WTERMSIG(wait_status) != SIGPIPE) { 409 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 410 return (NULL); 411 } 412 if (l < 0) { 413 warn("read"); 414 return (NULL); 415 } 416 if (l == 0) { 417 fprintf(stderr, "\"%s\": No match.\n", name); 418 return (NULL); 419 } 420 if (l == BUFSIZ) { 421 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 422 return (NULL); 423 } 424 xname[l] = '\0'; 425 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 426 ; 427 cp[1] = '\0'; 428 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 429 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 430 return (NULL); 431 } 432 return (savestr(xname)); 433 } 434 435 /* 436 * Determine the current folder directory name. 437 */ 438 int 439 getfold(name, namelen) 440 char *name; 441 int namelen; 442 { 443 char *folder; 444 int copylen; 445 446 if ((folder = value("folder")) == NULL) 447 return (-1); 448 if (*folder == '/') 449 copylen = strlcpy(name, folder, namelen); 450 else 451 copylen = snprintf(name, namelen, "%s/%s", 452 homedir ? homedir : ".", folder); 453 return (copylen < 0 || copylen >= namelen ? (-1) : (0)); 454 } 455 456 /* 457 * Return the name of the dead.letter file. 458 */ 459 char * 460 getdeadletter() 461 { 462 char *cp; 463 464 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 465 cp = expand("~/dead.letter"); 466 else if (*cp != '/') { 467 char buf[PATHSIZE]; 468 469 (void)snprintf(buf, sizeof(buf), "~/%s", cp); 470 cp = expand(buf); 471 } 472 return (cp); 473 } 474