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