11558Srgrimes/* 21558Srgrimes * Copyright (c) 1980, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * Redistribution and use in source and binary forms, with or without 61558Srgrimes * modification, are permitted provided that the following conditions 71558Srgrimes * are met: 81558Srgrimes * 1. Redistributions of source code must retain the above copyright 91558Srgrimes * notice, this list of conditions and the following disclaimer. 101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111558Srgrimes * notice, this list of conditions and the following disclaimer in the 121558Srgrimes * documentation and/or other materials provided with the distribution. 131558Srgrimes * 4. Neither the name of the University nor the names of its contributors 141558Srgrimes * may be used to endorse or promote products derived from this software 151558Srgrimes * without specific prior written permission. 161558Srgrimes * 171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271558Srgrimes * SUCH DAMAGE. 281558Srgrimes */ 291558Srgrimes 30114589Sobrien#ifndef lint 311558Srgrimes#if 0 3238040Scharnierstatic char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 331558Srgrimes#endif 341558Srgrimes#endif /* not lint */ 351558Srgrimes#include <sys/cdefs.h> 361558Srgrimes__FBSDID("$FreeBSD$"); 371558Srgrimes 381558Srgrimes#include "rcv.h" 39114589Sobrien#include <sys/file.h> 4038040Scharnier#include <sys/wait.h> 41114589Sobrien 42114589Sobrien#include <unistd.h> 431558Srgrimes#include <paths.h> 441558Srgrimes#include <errno.h> 45102231Strhodes#include "extern.h" 461558Srgrimes 471558Srgrimes/* 4842873Sluoqi * Mail -- a mail program 4996478Sphk * 501558Srgrimes * File I/O. 511558Srgrimes */ 5298542Smckusick 5398542Smckusickextern int wait_status; 541558Srgrimes 55207141Sjeff/* 561558Srgrimes * Set up the input pointers while copying the mail file into /tmp. 57110174Sgordon */ 581558Srgrimesvoid 591558Srgrimessetptr(FILE *ibuf, off_t offset) 601558Srgrimes{ 61109597Sjmallett int c, count; 6238040Scharnier char *cp, *cp2; 631558Srgrimes struct message this; 641558Srgrimes FILE *mestmp; 65207141Sjeff int maybe, inhead; 6658047Ssheldonh char linebuf[LINESIZE], pathbuf[PATHSIZE]; 671558Srgrimes int omsgCount; 681558Srgrimes 691558Srgrimes /* Get temporary file. */ 701558Srgrimes (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 711558Srgrimes if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 72109597Sjmallett err(1, "can't open %s", pathbuf); 73109597Sjmallett (void)rm(pathbuf); 741558Srgrimes 7592883Simp if (offset == 0) { 7692883Simp msgCount = 0; 77207141Sjeff } else { 78207141Sjeff /* Seek into the file to get to the new messages */ 79207141Sjeff (void)fseeko(ibuf, offset, SEEK_SET); 801558Srgrimes /* 811558Srgrimes * We need to make "offset" a pointer to the end of 82109597Sjmallett * the temp file that has the copy of the mail file. 831558Srgrimes * If any messages have been edited, this will be 84207141Sjeff * different from the offset into the mail file. 85109963Sjmallett */ 8679750Sdd (void)fseeko(otf, (off_t)0, SEEK_END); 87127455Sbde offset = ftello(otf); 88207141Sjeff } 89207141Sjeff omsgCount = msgCount; 90207141Sjeff maybe = 1; 91127441Sbde inhead = 0; 9279750Sdd this.m_flag = MUSED|MNEW; 9342873Sluoqi this.m_size = 0; 9442873Sluoqi this.m_lines = 0; 951558Srgrimes this.m_block = 0; 96127441Sbde this.m_offset = 0; 97127441Sbde for (;;) { 98207141Sjeff if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 99207141Sjeff if (append(&this, mestmp)) 100207141Sjeff errx(1, "temporary file"); 101207141Sjeff makemessage(mestmp, omsgCount); 102127455Sbde return; 103127441Sbde } 104207141Sjeff count = strlen(linebuf); 105127441Sbde /* 106127441Sbde * Transforms lines ending in <CR><LF> to just <LF>. 107127441Sbde * This allows mail to be able to read Eudora mailboxes. 108127441Sbde */ 109127441Sbde if (count >= 2 && linebuf[count - 1] == '\n' && 110127441Sbde linebuf[count - 2] == '\r') { 111127441Sbde count--; 112127441Sbde linebuf[count - 1] = '\n'; 113127441Sbde } 114200796Strasz 115127441Sbde (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 116127441Sbde if (ferror(otf)) 117127441Sbde errx(1, "/tmp"); 118127441Sbde if (count) 119127441Sbde linebuf[count - 1] = '\0'; 120127441Sbde if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 121127441Sbde msgCount++; 122127441Sbde if (append(&this, mestmp)) 123127441Sbde errx(1, "temporary file"); 124127441Sbde this.m_flag = MUSED|MNEW; 125127441Sbde this.m_size = 0; 126127441Sbde this.m_lines = 0; 127127441Sbde this.m_block = blockof(offset); 128127441Sbde this.m_offset = boffsetof(offset); 129127441Sbde inhead = 1; 130127441Sbde } else if (linebuf[0] == 0) { 131127441Sbde inhead = 0; 132127441Sbde } else if (inhead) { 133127441Sbde for (cp = linebuf, cp2 = "status";; cp++) { 134127441Sbde if ((c = *cp2++) == '\0') { 135127441Sbde while (isspace((unsigned char)*cp++)) 136127441Sbde ; 137127441Sbde if (cp[-1] != ':') 138127441Sbde break; 139127441Sbde while ((c = *cp++) != '\0') 140127441Sbde if (c == 'R') 141127441Sbde this.m_flag |= MREAD; 142127441Sbde else if (c == 'O') 143127441Sbde this.m_flag &= ~MNEW; 144207141Sjeff inhead = 0; 145207141Sjeff break; 146207141Sjeff } 147207141Sjeff if (*cp != c && *cp != toupper((unsigned char)c)) 148207141Sjeff break; 149207141Sjeff } 150207141Sjeff } 151207141Sjeff offset += count; 152207141Sjeff this.m_size += count; 153207141Sjeff this.m_lines++; 154207141Sjeff maybe = linebuf[0] == 0; 155207141Sjeff } 156163842Spjd} 157163842Spjd 158163842Spjd/* 159163842Spjd * Drop the passed line onto the passed output buffer. 160163842Spjd * If a write error occurs, return -1, else the count of 161163842Spjd * characters written, including the newline if requested. 162163842Spjd */ 163163842Spjdint 164163842Spjdputline(FILE *obuf, char *linebuf, int outlf) 165163842Spjd{ 166163842Spjd int c; 167163842Spjd 168163842Spjd c = strlen(linebuf); 169127441Sbde (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 170127441Sbde if (outlf) { 171127441Sbde fprintf(obuf, "\n"); 172127441Sbde c++; 173127441Sbde } 174127441Sbde if (ferror(obuf)) 175127441Sbde return (-1); 176127441Sbde return (c); 177127441Sbde} 178127441Sbde 179127441Sbde/* 180127441Sbde * Read up a line from the specified input into the line 181127441Sbde * buffer. Return the number of characters read. Do not 182127441Sbde * include the newline (or carriage return) at the end. 183127441Sbde */ 184127441Sbdeint 185127441Sbdereadline(FILE *ibuf, char *linebuf, int linesize) 186127441Sbde{ 187127441Sbde int n; 188127441Sbde 189127441Sbde clearerr(ibuf); 190127441Sbde if (fgets(linebuf, linesize, ibuf) == NULL) 191127441Sbde return (-1); 192127441Sbde n = strlen(linebuf); 193127441Sbde if (n > 0 && linebuf[n - 1] == '\n') 194127441Sbde linebuf[--n] = '\0'; 195127441Sbde if (n > 0 && linebuf[n - 1] == '\r') 196127441Sbde linebuf[--n] = '\0'; 197127441Sbde return (n); 198127441Sbde} 199127441Sbde 200127441Sbde/* 201127441Sbde * Return a file buffer all ready to read up the 202127441Sbde * passed message pointer. 203127441Sbde */ 204127441SbdeFILE * 205127441Sbdesetinput(struct message *mp) 206127441Sbde{ 207127441Sbde 208200796Strasz (void)fflush(otf); 209200796Strasz if (fseeko(itf, 210200796Strasz positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) 211200796Strasz err(1, "fseeko"); 212200796Strasz return (itf); 213200796Strasz} 214200796Strasz 215200796Strasz/* 216200796Strasz * Take the data out of the passed ghost file and toss it into 217200796Strasz * a dynamically allocated message structure. 218200796Strasz */ 219200796Straszvoid 220127441Sbdemakemessage(FILE *f, int omsgCount) 221127441Sbde{ 222127441Sbde size_t size; 223127441Sbde struct message *nmessage; 224127455Sbde 225127455Sbde size = (msgCount + 1) * sizeof(struct message); 226127441Sbde nmessage = (struct message *)realloc(message, size); 227127441Sbde if (nmessage == NULL) 228127441Sbde errx(1, "Insufficient memory for %d messages\n", 229127441Sbde msgCount); 230127441Sbde if (omsgCount == 0 || message == NULL) 231127441Sbde dot = nmessage; 232127441Sbde else 233127441Sbde dot = nmessage + (dot - message); 234127441Sbde message = nmessage; 235127455Sbde size -= (omsgCount + 1) * sizeof(struct message); 236127441Sbde (void)fflush(f); 237127455Sbde (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 238127441Sbde if (read(fileno(f), (char *)&message[omsgCount], size) != size) 239127441Sbde errx(1, "Message temporary file corrupted"); 240127441Sbde message[msgCount].m_size = 0; 241127441Sbde message[msgCount].m_lines = 0; 242127441Sbde (void)Fclose(f); 243127441Sbde} 244127441Sbde 245127441Sbde/* 246127441Sbde * Append the passed message descriptor onto the temp file. 247127441Sbde * If the write fails, return 1, else 0 248127441Sbde */ 249127441Sbdeint 250127441Sbdeappend(struct message *mp, FILE *f) 251127441Sbde{ 252127441Sbde return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); 253127441Sbde} 254127441Sbde 255127441Sbde/* 256127441Sbde * Delete a file, but only if the file is a plain file. 257127441Sbde */ 258127441Sbdeint 259127441Sbderm(char *name) 260127441Sbde{ 261207141Sjeff struct stat sb; 262207141Sjeff 263207141Sjeff if (stat(name, &sb) < 0) 264207141Sjeff return (-1); 265207141Sjeff if (!S_ISREG(sb.st_mode)) { 266207141Sjeff errno = EISDIR; 267207141Sjeff return (-1); 268207141Sjeff } 269207141Sjeff return (unlink(name)); 270207141Sjeff} 271127441Sbde 272127441Sbdestatic int sigdepth; /* depth of holdsigs() */ 273105120Srwatsonstatic sigset_t nset, oset; 27469314Scharnier/* 27569314Scharnier * Hold signals SIGHUP, SIGINT, and SIGQUIT. 27669314Scharnier */ 277127441Sbdevoid 27869314Scharnierholdsigs(void) 279109963Sjmallett{ 280109963Sjmallett 281109963Sjmallett if (sigdepth++ == 0) { 282109963Sjmallett (void)sigemptyset(&nset); 283207421Sjeff (void)sigaddset(&nset, SIGHUP); 284207421Sjeff (void)sigaddset(&nset, SIGINT); 285207421Sjeff (void)sigaddset(&nset, SIGQUIT); 28669829Scharnier (void)sigprocmask(SIG_BLOCK, &nset, &oset); 28769829Scharnier } 28869829Scharnier} 28969829Scharnier 29069829Scharnier/* 29169829Scharnier * Release signals SIGHUP, SIGINT, and SIGQUIT. 29269829Scharnier */ 293110174Sgordonvoid 294110174Sgordonrelsesigs(void) 295110174Sgordon{ 296110174Sgordon 297105120Srwatson if (--sigdepth == 0) 298200796Strasz (void)sigprocmask(SIG_SETMASK, &oset, NULL); 299105120Srwatson} 300105120Srwatson 301105120Srwatson/* 302200796Strasz * Determine the size of the file possessed by 303200796Strasz * the passed buffer. 304200796Strasz */ 305105120Srwatsonoff_t 306105120Srwatsonfsize(FILE *iob) 307105120Srwatson{ 308105120Srwatson struct stat sbuf; 309105120Srwatson 310105120Srwatson if (fstat(fileno(iob), &sbuf) < 0) 311105120Srwatson return (0); 312105120Srwatson return (sbuf.st_size); 313105120Srwatson} 314105120Srwatson 315105120Srwatson/* 316105206Srwatson * Evaluate the string given as a new mailbox name. 317105120Srwatson * Supported meta characters: 318105120Srwatson * % for my system mail box 319105120Srwatson * %user for user's system mail box 32069829Scharnier * # for previous file 32169829Scharnier * & invoker's mbox file 322127455Sbde * +file file in folder directory 32369829Scharnier * any shell meta character 32469829Scharnier * Return the file name as a dynamic string. 32569829Scharnier */ 326127455Sbdechar * 32769829Scharnierexpand(char *name) 32869829Scharnier{ 32969829Scharnier char xname[PATHSIZE]; 33075377Smckusick char cmdbuf[PATHSIZE]; /* also used for file names */ 33175377Smckusick int pid, l; 332203769Smckusick char *cp, *sh; 33375377Smckusick int pivec[2]; 33475377Smckusick struct stat sbuf; 33575377Smckusick 33675377Smckusick /* 33775377Smckusick * The order of evaluation is "%" and "#" expand into constants. 33875377Smckusick * "&" can expand into "+". "+" can expand into shell meta characters. 33975377Smckusick * Shell meta characters expand into constants. 34075377Smckusick * This way, we make no recursive expansion. 341207141Sjeff */ 342207141Sjeff switch (*name) { 343207141Sjeff case '%': 344207141Sjeff findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 345207141Sjeff return (savestr(xname)); 346207141Sjeff case '#': 347207141Sjeff if (name[1] != 0) 348207141Sjeff break; 349207141Sjeff if (prevfile[0] == 0) { 350207141Sjeff printf("No previous file\n"); 351207141Sjeff return (NULL); 352207141Sjeff } 353207141Sjeff return (savestr(prevfile)); 354207141Sjeff case '&': 355207141Sjeff if (name[1] == 0 && (name = value("MBOX")) == NULL) 356207141Sjeff name = "~/mbox"; 357207141Sjeff /* fall through */ 358207141Sjeff } 359207141Sjeff if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 360207141Sjeff (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 361207141Sjeff name = savestr(xname); 362207141Sjeff } 363207141Sjeff /* catch the most common shell meta character */ 364207141Sjeff if (name[0] == '~' && homedir != NULL && 365207141Sjeff (name[1] == '/' || name[1] == '\0')) { 366207141Sjeff (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 367207141Sjeff name = savestr(xname); 368163842Spjd } 369163842Spjd if (!strpbrk(name, "~{[*?$`'\"\\")) 370163842Spjd return (name); 371163842Spjd if (pipe(pivec) < 0) { 372163842Spjd warn("pipe"); 373163842Spjd return (name); 374163842Spjd } 375163842Spjd (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 376163842Spjd if ((sh = value("SHELL")) == NULL) 377163842Spjd sh = _PATH_CSHELL; 378163842Spjd pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); 379163842Spjd if (pid < 0) { 380163842Spjd (void)close(pivec[0]); 381163842Spjd (void)close(pivec[1]); 382163842Spjd return (NULL); 383163842Spjd } 384163842Spjd (void)close(pivec[1]); 385163842Spjd l = read(pivec[0], xname, BUFSIZ); 386163842Spjd (void)close(pivec[0]); 387163842Spjd if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 388105120Srwatson WTERMSIG(wait_status) != SIGPIPE) { 389105120Srwatson fprintf(stderr, "\"%s\": Expansion failed.\n", name); 390105120Srwatson return (NULL); 391105120Srwatson } 392105120Srwatson if (l < 0) { 393105120Srwatson warn("read"); 394105120Srwatson return (NULL); 395105120Srwatson } 396105120Srwatson if (l == 0) { 397105120Srwatson fprintf(stderr, "\"%s\": No match.\n", name); 398105120Srwatson return (NULL); 399105120Srwatson } 400105120Srwatson if (l == BUFSIZ) { 401105120Srwatson fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 402105120Srwatson return (NULL); 403105120Srwatson } 404105206Srwatson xname[l] = '\0'; 405105120Srwatson for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 406105120Srwatson ; 407105120Srwatson cp[1] = '\0'; 40869829Scharnier if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 40969829Scharnier fprintf(stderr, "\"%s\": Ambiguous.\n", name); 410127455Sbde return (NULL); 41169829Scharnier } 41269829Scharnier return (savestr(xname)); 41369829Scharnier} 41469829Scharnier 41569829Scharnier/* 41669829Scharnier * Determine the current folder directory name. 41769829Scharnier */ 41869829Scharnierint 41969829Scharniergetfold(char *name, int namelen) 42069829Scharnier{ 42169829Scharnier char *folder; 422200796Strasz int copylen; 423200796Strasz 424200796Strasz if ((folder = value("folder")) == NULL) 425200796Strasz return (-1); 426200796Strasz if (*folder == '/') 427200796Strasz copylen = strlcpy(name, folder, namelen); 428200796Strasz else 429200796Strasz copylen = snprintf(name, namelen, "%s/%s", 430200796Strasz homedir ? homedir : ".", folder); 431200796Strasz return (copylen < 0 || copylen >= namelen ? (-1) : (0)); 432200796Strasz} 433200796Strasz 434200796Strasz/* 435200796Strasz * Return the name of the dead.letter file. 436200796Strasz */ 437200796Straszchar * 438200796Straszgetdeadletter(void) 439200796Strasz{ 440200796Strasz char *cp; 441200796Strasz 442200796Strasz if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 443200796Strasz cp = expand("~/dead.letter"); 444200796Strasz else if (*cp != '/') { 44569829Scharnier char buf[PATHSIZE]; 44669829Scharnier 44769829Scharnier (void)snprintf(buf, sizeof(buf), "~/%s", cp); 448127455Sbde cp = expand(buf); 44969829Scharnier } 450127455Sbde return (cp); 45175498Smckusick} 45275498Smckusick