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