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