1 /* $NetBSD: fio.c,v 1.23 2003/10/31 01:25:54 ross Exp $ */ 2 3 /* 4 * Copyright (c) 1980, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 36 #else 37 __RCSID("$NetBSD: fio.c,v 1.23 2003/10/31 01:25:54 ross Exp $"); 38 #endif 39 #endif /* not lint */ 40 41 #include "rcv.h" 42 #include "extern.h" 43 44 /* 45 * Mail -- a mail program 46 * 47 * File I/O. 48 */ 49 extern int wait_status; 50 extern char *tmpdir; 51 52 /* 53 * Set up the input pointers while copying the mail file into /tmp. 54 */ 55 void 56 setptr(FILE *ibuf, off_t offset) 57 { 58 int c, len; 59 char *cp, *cp2; 60 struct message this; 61 FILE *mestmp; 62 int maybe, inhead; 63 char linebuf[LINESIZE]; 64 int omsgCount; 65 66 /* Get temporary file. */ 67 (void)snprintf(linebuf, LINESIZE, "%s/mail.XXXXXX", tmpdir); 68 if ((c = mkstemp(linebuf)) == -1 || 69 (mestmp = Fdopen(c, "r+")) == NULL) { 70 (void)fprintf(stderr, "mail: can't open %s\n", linebuf); 71 exit(1); 72 } 73 (void)unlink(linebuf); 74 75 if (offset == 0) { 76 msgCount = 0; 77 } else { 78 /* Seek into the file to get to the new messages */ 79 (void)fseek(ibuf, offset, 0); 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 (void)fseek(otf, 0L, 2); 87 offset = ftell(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_blines = 0; 96 this.m_block = 0; 97 this.m_offset = 0; 98 for (;;) { 99 if (fgets(linebuf, LINESIZE, ibuf) == NULL) { 100 if (append(&this, mestmp)) { 101 warn("temporary file"); 102 exit(1); 103 } 104 makemessage(mestmp, omsgCount); 105 return; 106 } 107 len = strlen(linebuf); 108 /* 109 * Transforms lines ending in <CR><LF> to just <LF>. 110 * This allows mail to be able to read Eudora mailboxes 111 * that reside on a DOS partition. 112 */ 113 if (len >= 2 && linebuf[len-1] == '\n' && linebuf[len-2] == '\r') { 114 linebuf[len-2] = '\n'; 115 len--; 116 } 117 (void)fwrite(linebuf, sizeof *linebuf, len, otf); 118 if (ferror(otf)) { 119 warn("/tmp"); 120 exit(1); 121 } 122 if(len) 123 linebuf[len - 1] = 0; 124 if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 125 msgCount++; 126 if (append(&this, mestmp)) { 127 warn("temporary file"); 128 exit(1); 129 } 130 this.m_flag = MUSED|MNEW; 131 this.m_size = 0; 132 this.m_lines = 0; 133 this.m_blines = 0; 134 this.m_block = blockof(offset); 135 this.m_offset = offsetof(offset); 136 inhead = 1; 137 } else if (linebuf[0] == 0) { 138 inhead = 0; 139 } else if (inhead) { 140 for (cp = linebuf, cp2 = "status";; cp++) { 141 if ((c = *cp2++) == 0) { 142 while (isspace((unsigned char)*cp++)) 143 ; 144 if (cp[-1] != ':') 145 break; 146 while ((c = *cp++) != '\0') 147 if (c == 'R') 148 this.m_flag |= MREAD; 149 else if (c == 'O') 150 this.m_flag &= ~MNEW; 151 inhead = 0; 152 break; 153 } 154 if (*cp != c && *cp != toupper(c)) 155 break; 156 } 157 } 158 offset += len; 159 this.m_size += len; 160 this.m_lines++; 161 if (!inhead) { 162 int lines_plus_wraps = 1; 163 int linelen = strlen(linebuf); 164 165 if (linelen > screenwidth) { 166 lines_plus_wraps = linelen / screenwidth; 167 if (linelen % screenwidth != 0) 168 ++lines_plus_wraps; 169 } 170 this.m_blines += lines_plus_wraps; 171 } 172 maybe = linebuf[0] == 0; 173 } 174 } 175 176 /* 177 * Drop the passed line onto the passed output buffer. 178 * If a write error occurs, return -1, else the count of 179 * characters written, including the newline if requested. 180 */ 181 int 182 putline(FILE *obuf, char *linebuf, int outlf) 183 { 184 int c; 185 186 c = strlen(linebuf); 187 (void)fwrite(linebuf, sizeof *linebuf, c, obuf); 188 if (outlf) { 189 (void)putc('\n', obuf); 190 c++; 191 } 192 if (ferror(obuf)) 193 return (-1); 194 return (c); 195 } 196 197 /* 198 * Read up a line from the specified input into the line 199 * buffer. Return the number of characters read. Do not 200 * include the newline at the end. 201 */ 202 int 203 readline(FILE *ibuf, char *linebuf, int linesize) 204 { 205 int n; 206 207 clearerr(ibuf); 208 if (fgets(linebuf, linesize, ibuf) == NULL) 209 return -1; 210 n = strlen(linebuf); 211 if (n > 0 && linebuf[n - 1] == '\n') 212 linebuf[--n] = '\0'; 213 return n; 214 } 215 216 /* 217 * Return a file buffer all ready to read up the 218 * passed message pointer. 219 */ 220 FILE * 221 setinput(const struct message *mp) 222 { 223 224 fflush(otf); 225 if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0) 226 err(1, "fseek"); 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 */ 234 void 235 makemessage(FILE *f, int omsgCount) 236 { 237 int size = (msgCount + 1) * sizeof (struct message); 238 struct message *nmessage = realloc(message, size); 239 240 if (nmessage == NULL) 241 err(1, "Insufficient memory for %d messages", msgCount); 242 if (omsgCount == 0 || message == NULL) 243 dot = nmessage; 244 else 245 dot = nmessage + (dot - message); 246 message = nmessage; 247 size -= (omsgCount + 1) * sizeof (struct message); 248 fflush(f); 249 (void)lseek(fileno(f), (off_t)sizeof *message, 0); 250 if (read(fileno(f), (char *) &message[omsgCount], size) != size) 251 errx(1, "Message temporary file corrupted"); 252 message[msgCount].m_size = 0; 253 message[msgCount].m_lines = 0; 254 message[msgCount].m_blines = 0; 255 Fclose(f); 256 } 257 258 /* 259 * Append the passed message descriptor onto the temp file. 260 * If the write fails, return 1, else 0 261 */ 262 int 263 append(struct message *mp, FILE *f) 264 { 265 return fwrite((char *) mp, sizeof *mp, 1, f) != 1; 266 } 267 268 /* 269 * Delete a file, but only if the file is a plain file. 270 */ 271 int 272 rm(char *name) 273 { 274 struct stat sb; 275 276 if (stat(name, &sb) < 0) 277 return(-1); 278 if (!S_ISREG(sb.st_mode)) { 279 errno = EISDIR; 280 return(-1); 281 } 282 return(unlink(name)); 283 } 284 285 static int sigdepth; /* depth of holdsigs() */ 286 static sigset_t nset, oset; 287 /* 288 * Hold signals SIGHUP, SIGINT, and SIGQUIT. 289 */ 290 void 291 holdsigs(void) 292 { 293 294 if (sigdepth++ == 0) { 295 sigemptyset(&nset); 296 sigaddset(&nset, SIGHUP); 297 sigaddset(&nset, SIGINT); 298 sigaddset(&nset, SIGQUIT); 299 sigprocmask(SIG_BLOCK, &nset, &oset); 300 } 301 } 302 303 /* 304 * Release signals SIGHUP, SIGINT, and SIGQUIT. 305 */ 306 void 307 relsesigs(void) 308 { 309 310 if (--sigdepth == 0) 311 sigprocmask(SIG_SETMASK, &oset, NULL); 312 } 313 314 /* 315 * Determine the size of the file possessed by 316 * the passed buffer. 317 */ 318 off_t 319 fsize(FILE *iob) 320 { 321 struct stat sbuf; 322 323 if (fstat(fileno(iob), &sbuf) < 0) 324 return 0; 325 return sbuf.st_size; 326 } 327 328 /* 329 * Evaluate the string given as a new mailbox name. 330 * Supported meta characters: 331 * % for my system mail box 332 * %user for user's system mail box 333 * # for previous file 334 * & invoker's mbox file 335 * +file file in folder directory 336 * any shell meta character 337 * Return the file name as a dynamic string. 338 */ 339 char * 340 expand(char *name) 341 { 342 char xname[PATHSIZE]; 343 char cmdbuf[PATHSIZE]; /* also used for file names */ 344 int pid, l; 345 char *cp, *shellcmd; 346 int pivec[2]; 347 struct stat sbuf; 348 349 /* 350 * The order of evaluation is "%" and "#" expand into constants. 351 * "&" can expand into "+". "+" can expand into shell meta characters. 352 * Shell meta characters expand into constants. 353 * This way, we make no recursive expansion. 354 */ 355 switch (*name) { 356 case '%': 357 findmail(name[1] ? name + 1 : myname, xname); 358 return savestr(xname); 359 case '#': 360 if (name[1] != 0) 361 break; 362 if (prevfile[0] == 0) { 363 printf("No previous file\n"); 364 return NULL; 365 } 366 return savestr(prevfile); 367 case '&': 368 if (name[1] == 0 && (name = value("MBOX")) == NULL) 369 name = "~/mbox"; 370 /* fall through */ 371 } 372 if (name[0] == '+' && getfold(cmdbuf) >= 0) { 373 snprintf(xname, PATHSIZE, "%s/%s", cmdbuf, name + 1); 374 name = savestr(xname); 375 } 376 /* catch the most common shell meta character */ 377 if (name[0] == '~' && (name[1] == '/' || name[1] == '\0')) { 378 snprintf(xname, PATHSIZE, "%s%s", homedir, name + 1); 379 name = savestr(xname); 380 } 381 if (strpbrk(name, "~{[*?$`'\"\\") == NULL) 382 return name; 383 if (pipe(pivec) < 0) { 384 warn("pipe"); 385 return name; 386 } 387 snprintf(cmdbuf, PATHSIZE, "echo %s", name); 388 if ((shellcmd = value("SHELL")) == NULL) 389 shellcmd = _PATH_CSHELL; 390 pid = start_command(shellcmd, 0, -1, pivec[1], "-c", cmdbuf, NULL); 391 if (pid < 0) { 392 close(pivec[0]); 393 close(pivec[1]); 394 return NULL; 395 } 396 close(pivec[1]); 397 l = read(pivec[0], xname, PATHSIZE); 398 close(pivec[0]); 399 if (wait_child(pid) < 0 && WTERMSIG(wait_status) != SIGPIPE) { 400 fprintf(stderr, "\"%s\": Expansion failed.\n", name); 401 return NULL; 402 } 403 if (l < 0) { 404 warn("read"); 405 return NULL; 406 } 407 if (l == 0) { 408 fprintf(stderr, "\"%s\": No match.\n", name); 409 return NULL; 410 } 411 if (l == PATHSIZE) { 412 fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 413 return NULL; 414 } 415 xname[l] = '\0'; 416 for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 417 ; 418 cp[1] = '\0'; 419 if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 420 fprintf(stderr, "\"%s\": Ambiguous.\n", name); 421 return NULL; 422 } 423 return savestr(xname); 424 } 425 426 /* 427 * Determine the current folder directory name. 428 */ 429 int 430 getfold(char *name) 431 { 432 char *folder; 433 434 if ((folder = value("folder")) == NULL) 435 return (-1); 436 if (*folder == '/') { 437 strncpy(name, folder, PATHSIZE - 1); 438 name[PATHSIZE - 1] = '\0' ; 439 } 440 else 441 snprintf(name, PATHSIZE, "%s/%s", homedir, folder); 442 return (0); 443 } 444 445 /* 446 * Return the name of the dead.letter file. 447 */ 448 char * 449 getdeadletter(void) 450 { 451 char *cp; 452 453 if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 454 cp = expand("~/dead.letter"); 455 else if (*cp != '/') { 456 char buf[PATHSIZE]; 457 458 (void)snprintf(buf, PATHSIZE, "~/%s", cp); 459 cp = expand(buf); 460 } 461 return cp; 462 } 463