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