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 36static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 37#endif 38__attribute__((__used__)) 39static const char rcsid[] = 40 "$FreeBSD: src/usr.bin/mail/fio.c,v 1.12 2002/06/30 05:25:06 obrien Exp $"; 41#endif /* not lint */ 42 43#include <sys/cdefs.h> 44 45#include "rcv.h" 46#include <sys/file.h> 47#include <sys/wait.h> 48 49#include <unistd.h> 50#include <paths.h> 51#include <errno.h> 52#include "extern.h" 53 54/* 55 * Mail -- a mail program 56 * 57 * File I/O. 58 */ 59 60extern int wait_status; 61 62/* 63 * Set up the input pointers while copying the mail file into /tmp. 64 */ 65void 66setptr(ibuf, offset) 67 FILE *ibuf; 68 off_t offset; 69{ 70 int c, count; 71 char *cp, *cp2; 72 struct message this; 73 FILE *mestmp; 74 int maybe, inhead; 75 char linebuf[LINESIZE], pathbuf[PATHSIZE]; 76 int omsgCount; 77 78 /* Get temporary file. */ 79 (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 80 if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 81 err(1, "can't open %s", pathbuf); 82 (void)rm(pathbuf); 83 84 if (offset == 0) { 85 msgCount = 0; 86 } else { 87 /* Seek into the file to get to the new messages */ 88 (void)fseeko(ibuf, offset, SEEK_SET); 89 /* 90 * We need to make "offset" a pointer to the end of 91 * the temp file that has the copy of the mail file. 92 * If any messages have been edited, this will be 93 * different from the offset into the mail file. 94 */ 95 (void)fseeko(otf, (off_t)0, SEEK_END); 96 offset = ftello(otf); 97 } 98 omsgCount = msgCount; 99 maybe = 1; 100 inhead = 0; 101 this.m_flag = MUSED|MNEW; 102 this.m_size = 0; 103 this.m_lines = 0; 104 this.m_block = 0; 105 this.m_offset = 0; 106 for (;;) { 107 if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 108 if (append(&this, mestmp)) 109 errx(1, "temporary file"); 110 makemessage(mestmp, omsgCount); 111 return; 112 } 113 count = strlen(linebuf); 114 /* 115 * Transforms lines ending in <CR><LF> to just <LF>. 116 * This allows mail to be able to read Eudora mailboxes. 117 */ 118 if (count >= 2 && linebuf[count - 1] == '\n' && 119 linebuf[count - 2] == '\r') { 120 count--; 121 linebuf[count - 1] = '\n'; 122 } 123 124 (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 125 if (ferror(otf)) 126 errx(1, "/tmp"); 127 if (count) 128 linebuf[count - 1] = '\0'; 129 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 130 msgCount++; 131 if (append(&this, mestmp)) 132 errx(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 = boffsetof(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 = *cp2++) == '\0') { 144 while (isspace((unsigned char)*cp++)) 145 ; 146 if (cp[-1] != ':') 147 break; 148 while ((c = *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((unsigned char)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 */ 172int 173putline(obuf, linebuf, outlf) 174 FILE *obuf; 175 char *linebuf; 176 int outlf; 177{ 178 int c; 179 180 c = strlen(linebuf); 181 (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 182 if (outlf) { 183 fprintf(obuf, "\n"); 184 c++; 185 } 186 if (ferror(obuf)) 187 return (-1); 188 return (c); 189} 190 191/* 192 * Read up a line from the specified input into the line 193 * buffer. Return the number of characters read. Do not 194 * include the newline (or carriage return) at the end. 195 */ 196int 197readline(ibuf, linebuf, linesize) 198 FILE *ibuf; 199 char *linebuf; 200 int linesize; 201{ 202 int n; 203 204 clearerr(ibuf); 205 if (fgets(linebuf, linesize, ibuf) == NULL) 206 return (-1); 207 n = strlen(linebuf); 208 if (n > 0 && linebuf[n - 1] == '\n') 209 linebuf[--n] = '\0'; 210 if (n > 0 && linebuf[n - 1] == '\r') 211 linebuf[--n] = '\0'; 212 return (n); 213} 214 215/* 216 * Return a file buffer all ready to read up the 217 * passed message pointer. 218 */ 219FILE * 220setinput(mp) 221 struct message *mp; 222{ 223 224 (void)fflush(otf); 225 if (fseeko(itf, 226 positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) 227 err(1, "fseeko"); 228 return (itf); 229} 230 231/* 232 * Take the data out of the passed ghost file and toss it into 233 * a dynamically allocated message structure. 234 */ 235void 236makemessage(f, omsgCount) 237 FILE *f; 238 int omsgCount; 239{ 240 size_t size; 241 struct message *nmessage; 242 243 size = (msgCount + 1) * sizeof(struct message); 244 nmessage = (struct message *)realloc(message, size); 245 if (nmessage == NULL) 246 errx(1, "Insufficient memory for %d messages\n", 247 msgCount); 248 if (omsgCount == 0 || message == NULL) 249 dot = nmessage; 250 else 251 dot = nmessage + (dot - message); 252 message = nmessage; 253 size -= (omsgCount + 1) * sizeof(struct message); 254 (void)fflush(f); 255 (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 256 if (read(fileno(f), (char *)&message[omsgCount], size) != size) 257 errx(1, "Message temporary file corrupted"); 258 message[msgCount].m_size = 0; 259 message[msgCount].m_lines = 0; 260 (void)Fclose(f); 261} 262 263/* 264 * Append the passed message descriptor onto the temp file. 265 * If the write fails, return 1, else 0 266 */ 267int 268append(mp, f) 269 struct message *mp; 270 FILE *f; 271{ 272 return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); 273} 274 275/* 276 * Delete a file, but only if the file is a plain file. 277 */ 278int 279rm(name) 280 char *name; 281{ 282 struct stat sb; 283 284 if (stat(name, &sb) < 0) 285 return (-1); 286 if (!S_ISREG(sb.st_mode)) { 287 errno = EISDIR; 288 return (-1); 289 } 290 return (unlink(name)); 291} 292 293static int sigdepth; /* depth of holdsigs() */ 294static sigset_t nset, oset; 295/* 296 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 297 */ 298void 299holdsigs() 300{ 301 302 if (sigdepth++ == 0) { 303 (void)sigemptyset(&nset); 304 (void)sigaddset(&nset, SIGHUP); 305 (void)sigaddset(&nset, SIGINT); 306 (void)sigaddset(&nset, SIGQUIT); 307 (void)sigprocmask(SIG_BLOCK, &nset, &oset); 308 } 309} 310 311/* 312 * Release signals SIGHUP, SIGINT, and SIGQUIT. 313 */ 314void 315relsesigs() 316{ 317 318 if (--sigdepth == 0) 319 (void)sigprocmask(SIG_SETMASK, &oset, NULL); 320} 321 322/* 323 * Determine the size of the file possessed by 324 * the passed buffer. 325 */ 326off_t 327fsize(iob) 328 FILE *iob; 329{ 330 struct stat sbuf; 331 332 if (fstat(fileno(iob), &sbuf) < 0) 333 return (0); 334 return (sbuf.st_size); 335} 336 337/* 338 * Evaluate the string given as a new mailbox name. 339 * Supported meta characters: 340 * % for my system mail box 341 * %user for user's system mail box 342 * # for previous file 343 * & invoker's mbox file 344 * +file file in folder directory 345 * any shell meta character 346 * Return the file name as a dynamic string. 347 */ 348char * 349expand(name) 350 char *name; 351{ 352 char xname[PATHSIZE]; 353 char cmdbuf[PATHSIZE]; /* also used for file names */ 354 int pid, l; 355 char *cp, *sh; 356 int pivec[2]; 357 struct stat sbuf; 358 359 /* 360 * The order of evaluation is "%" and "#" expand into constants. 361 * "&" can expand into "+". "+" can expand into shell meta characters. 362 * Shell meta characters expand into constants. 363 * This way, we make no recursive expansion. 364 */ 365 switch (*name) { 366 case '%': 367 findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 368 return (savestr(xname)); 369 case '#': 370 if (name[1] != 0) 371 break; 372 if (prevfile[0] == 0) { 373 printf("No previous file\n"); 374 return (NULL); 375 } 376 return (savestr(prevfile)); 377 case '&': 378 if (name[1] == 0 && (name = value("MBOX")) == NULL) 379 name = "~/mbox"; 380 /* fall through */ 381 } 382 if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 383 (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 384 name = savestr(xname); 385 } 386 /* catch the most common shell meta character */ 387 if (name[0] == '~' && homedir != NULL && 388 (name[1] == '/' || name[1] == '\0')) { 389 (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 390 name = savestr(xname); 391 } 392 if (!strpbrk(name, "~{[*?$`'\"\\")) 393 return (name); 394 if (pipe(pivec) < 0) { 395 warn("pipe"); 396 return (name); 397 } 398 (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 399 if ((sh = value("SHELL")) == NULL) 400 sh = _PATH_BSHELL; 401 pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); 402 if (pid < 0) { 403 (void)close(pivec[0]); 404 (void)close(pivec[1]); 405 return (NULL); 406 } 407 (void)close(pivec[1]); 408 l = read(pivec[0], xname, BUFSIZ); 409 (void)close(pivec[0]); 410 if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 411 WTERMSIG(wait_status) != SIGPIPE) { 412 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 413 return (NULL); 414 } 415 if (l < 0) { 416 warn("read"); 417 return (NULL); 418 } 419 if (l == 0) { 420 fprintf(stderr, "\"%s\": No match.\n", name); 421 return (NULL); 422 } 423 if (l == BUFSIZ) { 424 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 425 return (NULL); 426 } 427 xname[l] = '\0'; 428 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 429 ; 430 cp[1] = '\0'; 431 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 432 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 433 return (NULL); 434 } 435 return (savestr(xname)); 436} 437 438/* 439 * Determine the current folder directory name. 440 */ 441int 442getfold(name, namelen) 443 char *name; 444 int namelen; 445{ 446 char *folder; 447 int copylen; 448 449 if ((folder = value("folder")) == NULL) 450 return (-1); 451 if (*folder == '/') 452 copylen = strlcpy(name, folder, namelen); 453 else 454 copylen = snprintf(name, namelen, "%s/%s", 455 homedir ? homedir : ".", folder); 456 return (copylen < 0 || copylen >= namelen ? (-1) : (0)); 457} 458 459/* 460 * Return the name of the dead.letter file. 461 */ 462char * 463getdeadletter() 464{ 465 char *cp; 466 467 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 468 cp = expand("~/dead.letter"); 469 else if (*cp != '/') { 470 char buf[PATHSIZE]; 471 472 (void)snprintf(buf, sizeof(buf), "~/%s", cp); 473 cp = expand(buf); 474 } 475 return (cp); 476} 477