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