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