11590Srgrimes/* 21590Srgrimes * Copyright (c) 1980, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 301590Srgrimes#ifndef lint 3174769Smikeh#if 0 3288150Smikehstatic char sccsid[] = "@(#)fio.c 8.2 (Berkeley) 4/20/95"; 3374769Smikeh#endif 341590Srgrimes#endif /* not lint */ 3599112Sobrien#include <sys/cdefs.h> 3699112Sobrien__FBSDID("$FreeBSD$"); 371590Srgrimes 381590Srgrimes#include "rcv.h" 391590Srgrimes#include <sys/file.h> 401590Srgrimes#include <sys/wait.h> 411590Srgrimes 421590Srgrimes#include <unistd.h> 431590Srgrimes#include <paths.h> 441590Srgrimes#include <errno.h> 451590Srgrimes#include "extern.h" 461590Srgrimes 471590Srgrimes/* 481590Srgrimes * Mail -- a mail program 491590Srgrimes * 501590Srgrimes * File I/O. 511590Srgrimes */ 521590Srgrimes 5377274Smikehextern int wait_status; 5477274Smikeh 551590Srgrimes/* 561590Srgrimes * Set up the input pointers while copying the mail file into /tmp. 571590Srgrimes */ 581590Srgrimesvoid 59216564Scharniersetptr(FILE *ibuf, off_t offset) 601590Srgrimes{ 6177274Smikeh int c, count; 6277274Smikeh char *cp, *cp2; 631590Srgrimes struct message this; 641590Srgrimes FILE *mestmp; 651590Srgrimes int maybe, inhead; 6674769Smikeh char linebuf[LINESIZE], pathbuf[PATHSIZE]; 6788150Smikeh int omsgCount; 681590Srgrimes 691590Srgrimes /* Get temporary file. */ 7074769Smikeh (void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir); 7174769Smikeh if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL) 7274769Smikeh err(1, "can't open %s", pathbuf); 7377274Smikeh (void)rm(pathbuf); 741590Srgrimes 7588150Smikeh if (offset == 0) { 7688150Smikeh msgCount = 0; 7788150Smikeh } else { 7888150Smikeh /* Seek into the file to get to the new messages */ 7988227Sache (void)fseeko(ibuf, offset, SEEK_SET); 8088150Smikeh /* 8188150Smikeh * We need to make "offset" a pointer to the end of 8288150Smikeh * the temp file that has the copy of the mail file. 8388150Smikeh * If any messages have been edited, this will be 8488150Smikeh * different from the offset into the mail file. 8588150Smikeh */ 8688227Sache (void)fseeko(otf, (off_t)0, SEEK_END); 8788227Sache offset = ftello(otf); 8888150Smikeh } 8988150Smikeh omsgCount = msgCount; 901590Srgrimes maybe = 1; 911590Srgrimes inhead = 0; 921590Srgrimes this.m_flag = MUSED|MNEW; 931590Srgrimes this.m_size = 0; 941590Srgrimes this.m_lines = 0; 951590Srgrimes this.m_block = 0; 961590Srgrimes this.m_offset = 0; 971590Srgrimes for (;;) { 9874769Smikeh if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) { 9974769Smikeh if (append(&this, mestmp)) 10074769Smikeh errx(1, "temporary file"); 10188150Smikeh makemessage(mestmp, omsgCount); 1021590Srgrimes return; 1031590Srgrimes } 1041590Srgrimes count = strlen(linebuf); 10576455Smikeh /* 10676455Smikeh * Transforms lines ending in <CR><LF> to just <LF>. 10776455Smikeh * This allows mail to be able to read Eudora mailboxes. 10876455Smikeh */ 10976455Smikeh if (count >= 2 && linebuf[count - 1] == '\n' && 11076455Smikeh linebuf[count - 2] == '\r') { 11176455Smikeh count--; 11276455Smikeh linebuf[count - 1] = '\n'; 11376455Smikeh } 11476455Smikeh 11577274Smikeh (void)fwrite(linebuf, sizeof(*linebuf), count, otf); 11674769Smikeh if (ferror(otf)) 11774769Smikeh errx(1, "/tmp"); 11874769Smikeh if (count) 11974769Smikeh linebuf[count - 1] = '\0'; 1201590Srgrimes if (maybe && linebuf[0] == 'F' && ishead(linebuf)) { 1211590Srgrimes msgCount++; 12274769Smikeh if (append(&this, mestmp)) 12374769Smikeh errx(1, "temporary file"); 1241590Srgrimes this.m_flag = MUSED|MNEW; 1251590Srgrimes this.m_size = 0; 1261590Srgrimes this.m_lines = 0; 1271590Srgrimes this.m_block = blockof(offset); 12867496Sphk this.m_offset = boffsetof(offset); 1291590Srgrimes inhead = 1; 1301590Srgrimes } else if (linebuf[0] == 0) { 1311590Srgrimes inhead = 0; 1321590Srgrimes } else if (inhead) { 1331590Srgrimes for (cp = linebuf, cp2 = "status";; cp++) { 13477274Smikeh if ((c = *cp2++) == '\0') { 13588227Sache while (isspace((unsigned char)*cp++)) 1361590Srgrimes ; 1371590Srgrimes if (cp[-1] != ':') 1381590Srgrimes break; 13977274Smikeh while ((c = *cp++) != '\0') 1401590Srgrimes if (c == 'R') 1411590Srgrimes this.m_flag |= MREAD; 1421590Srgrimes else if (c == 'O') 1431590Srgrimes this.m_flag &= ~MNEW; 1441590Srgrimes inhead = 0; 1451590Srgrimes break; 1461590Srgrimes } 14788227Sache if (*cp != c && *cp != toupper((unsigned char)c)) 1481590Srgrimes break; 1491590Srgrimes } 1501590Srgrimes } 1511590Srgrimes offset += count; 1521590Srgrimes this.m_size += count; 1531590Srgrimes this.m_lines++; 1541590Srgrimes maybe = linebuf[0] == 0; 1551590Srgrimes } 1561590Srgrimes} 1571590Srgrimes 1581590Srgrimes/* 1591590Srgrimes * Drop the passed line onto the passed output buffer. 1601590Srgrimes * If a write error occurs, return -1, else the count of 16188150Smikeh * characters written, including the newline if requested. 1621590Srgrimes */ 1631590Srgrimesint 164216564Scharnierputline(FILE *obuf, char *linebuf, int outlf) 1651590Srgrimes{ 16677274Smikeh int c; 1671590Srgrimes 1681590Srgrimes c = strlen(linebuf); 16977274Smikeh (void)fwrite(linebuf, sizeof(*linebuf), c, obuf); 17088150Smikeh if (outlf) { 17188150Smikeh fprintf(obuf, "\n"); 17288150Smikeh c++; 17388150Smikeh } 1741590Srgrimes if (ferror(obuf)) 1751590Srgrimes return (-1); 17688150Smikeh return (c); 1771590Srgrimes} 1781590Srgrimes 1791590Srgrimes/* 1801590Srgrimes * Read up a line from the specified input into the line 1811590Srgrimes * buffer. Return the number of characters read. Do not 18276455Smikeh * include the newline (or carriage return) at the end. 1831590Srgrimes */ 1841590Srgrimesint 185216564Scharnierreadline(FILE *ibuf, char *linebuf, int linesize) 1861590Srgrimes{ 18777274Smikeh int n; 1881590Srgrimes 1891590Srgrimes clearerr(ibuf); 1901590Srgrimes if (fgets(linebuf, linesize, ibuf) == NULL) 19177274Smikeh return (-1); 1921590Srgrimes n = strlen(linebuf); 1931590Srgrimes if (n > 0 && linebuf[n - 1] == '\n') 1941590Srgrimes linebuf[--n] = '\0'; 19576455Smikeh if (n > 0 && linebuf[n - 1] == '\r') 19676455Smikeh linebuf[--n] = '\0'; 19777274Smikeh return (n); 1981590Srgrimes} 1991590Srgrimes 2001590Srgrimes/* 2011590Srgrimes * Return a file buffer all ready to read up the 2021590Srgrimes * passed message pointer. 2031590Srgrimes */ 2041590SrgrimesFILE * 205216564Scharniersetinput(struct message *mp) 2061590Srgrimes{ 2071590Srgrimes 20877274Smikeh (void)fflush(otf); 20982793Sache if (fseeko(itf, 21082793Sache positionof(mp->m_block, mp->m_offset), SEEK_SET) < 0) 21188227Sache err(1, "fseeko"); 2121590Srgrimes return (itf); 2131590Srgrimes} 2141590Srgrimes 2151590Srgrimes/* 2161590Srgrimes * Take the data out of the passed ghost file and toss it into 2171590Srgrimes * a dynamically allocated message structure. 2181590Srgrimes */ 2191590Srgrimesvoid 220216564Scharniermakemessage(FILE *f, int omsgCount) 2211590Srgrimes{ 22288150Smikeh size_t size; 22388150Smikeh struct message *nmessage; 2241590Srgrimes 22588150Smikeh size = (msgCount + 1) * sizeof(struct message); 22688150Smikeh nmessage = (struct message *)realloc(message, size); 22788150Smikeh if (nmessage == NULL) 22888150Smikeh errx(1, "Insufficient memory for %d messages\n", 22988150Smikeh msgCount); 23088150Smikeh if (omsgCount == 0 || message == NULL) 23188150Smikeh dot = nmessage; 23288150Smikeh else 23388150Smikeh dot = nmessage + (dot - message); 23488150Smikeh message = nmessage; 23588150Smikeh size -= (omsgCount + 1) * sizeof(struct message); 23677274Smikeh (void)fflush(f); 23777274Smikeh (void)lseek(fileno(f), (off_t)sizeof(*message), 0); 23888150Smikeh if (read(fileno(f), (char *)&message[omsgCount], size) != size) 23974769Smikeh errx(1, "Message temporary file corrupted"); 2401590Srgrimes message[msgCount].m_size = 0; 2411590Srgrimes message[msgCount].m_lines = 0; 24277274Smikeh (void)Fclose(f); 2431590Srgrimes} 2441590Srgrimes 2451590Srgrimes/* 2461590Srgrimes * Append the passed message descriptor onto the temp file. 2471590Srgrimes * If the write fails, return 1, else 0 2481590Srgrimes */ 2491590Srgrimesint 250216564Scharnierappend(struct message *mp, FILE *f) 2511590Srgrimes{ 25277274Smikeh return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1); 2531590Srgrimes} 2541590Srgrimes 2551590Srgrimes/* 2561590Srgrimes * Delete a file, but only if the file is a plain file. 2571590Srgrimes */ 2581590Srgrimesint 259216564Scharnierrm(char *name) 2601590Srgrimes{ 2611590Srgrimes struct stat sb; 2621590Srgrimes 2631590Srgrimes if (stat(name, &sb) < 0) 26477274Smikeh return (-1); 2651590Srgrimes if (!S_ISREG(sb.st_mode)) { 2661590Srgrimes errno = EISDIR; 26777274Smikeh return (-1); 2681590Srgrimes } 26977274Smikeh return (unlink(name)); 2701590Srgrimes} 2711590Srgrimes 2721590Srgrimesstatic int sigdepth; /* depth of holdsigs() */ 27388150Smikehstatic sigset_t nset, oset; 2741590Srgrimes/* 2751590Srgrimes * Hold signals SIGHUP, SIGINT, and SIGQUIT. 2761590Srgrimes */ 2771590Srgrimesvoid 278216564Scharnierholdsigs(void) 2791590Srgrimes{ 2801590Srgrimes 28188150Smikeh if (sigdepth++ == 0) { 28288150Smikeh (void)sigemptyset(&nset); 28388150Smikeh (void)sigaddset(&nset, SIGHUP); 28488150Smikeh (void)sigaddset(&nset, SIGINT); 28588150Smikeh (void)sigaddset(&nset, SIGQUIT); 28688150Smikeh (void)sigprocmask(SIG_BLOCK, &nset, &oset); 28788150Smikeh } 2881590Srgrimes} 2891590Srgrimes 2901590Srgrimes/* 2911590Srgrimes * Release signals SIGHUP, SIGINT, and SIGQUIT. 2921590Srgrimes */ 2931590Srgrimesvoid 294216564Scharnierrelsesigs(void) 2951590Srgrimes{ 2961590Srgrimes 2971590Srgrimes if (--sigdepth == 0) 29888150Smikeh (void)sigprocmask(SIG_SETMASK, &oset, NULL); 2991590Srgrimes} 3001590Srgrimes 3011590Srgrimes/* 3021590Srgrimes * Determine the size of the file possessed by 3031590Srgrimes * the passed buffer. 3041590Srgrimes */ 3051590Srgrimesoff_t 306216564Scharnierfsize(FILE *iob) 3071590Srgrimes{ 3081590Srgrimes struct stat sbuf; 3091590Srgrimes 3101590Srgrimes if (fstat(fileno(iob), &sbuf) < 0) 31177274Smikeh return (0); 31277274Smikeh return (sbuf.st_size); 3131590Srgrimes} 3141590Srgrimes 3151590Srgrimes/* 3161590Srgrimes * Evaluate the string given as a new mailbox name. 3171590Srgrimes * Supported meta characters: 3181590Srgrimes * % for my system mail box 3191590Srgrimes * %user for user's system mail box 3201590Srgrimes * # for previous file 3211590Srgrimes * & invoker's mbox file 3221590Srgrimes * +file file in folder directory 3231590Srgrimes * any shell meta character 3241590Srgrimes * Return the file name as a dynamic string. 3251590Srgrimes */ 3261590Srgrimeschar * 327216564Scharnierexpand(char *name) 3281590Srgrimes{ 3291590Srgrimes char xname[PATHSIZE]; 3301590Srgrimes char cmdbuf[PATHSIZE]; /* also used for file names */ 33177274Smikeh int pid, l; 33277274Smikeh char *cp, *sh; 3331590Srgrimes int pivec[2]; 3341590Srgrimes struct stat sbuf; 3351590Srgrimes 3361590Srgrimes /* 3371590Srgrimes * The order of evaluation is "%" and "#" expand into constants. 3381590Srgrimes * "&" can expand into "+". "+" can expand into shell meta characters. 3391590Srgrimes * Shell meta characters expand into constants. 3401590Srgrimes * This way, we make no recursive expansion. 3411590Srgrimes */ 3421590Srgrimes switch (*name) { 3431590Srgrimes case '%': 34474769Smikeh findmail(name[1] ? name + 1 : myname, xname, sizeof(xname)); 34577274Smikeh return (savestr(xname)); 3461590Srgrimes case '#': 3471590Srgrimes if (name[1] != 0) 3481590Srgrimes break; 3491590Srgrimes if (prevfile[0] == 0) { 3501590Srgrimes printf("No previous file\n"); 35177274Smikeh return (NULL); 3521590Srgrimes } 35377274Smikeh return (savestr(prevfile)); 3541590Srgrimes case '&': 35577274Smikeh if (name[1] == 0 && (name = value("MBOX")) == NULL) 3561590Srgrimes name = "~/mbox"; 3571590Srgrimes /* fall through */ 3581590Srgrimes } 35974769Smikeh if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) { 36077274Smikeh (void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1); 3611590Srgrimes name = savestr(xname); 3621590Srgrimes } 3631590Srgrimes /* catch the most common shell meta character */ 36474769Smikeh if (name[0] == '~' && homedir != NULL && 36574769Smikeh (name[1] == '/' || name[1] == '\0')) { 36677274Smikeh (void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1); 3671590Srgrimes name = savestr(xname); 3681590Srgrimes } 36974769Smikeh if (!strpbrk(name, "~{[*?$`'\"\\")) 37077274Smikeh return (name); 3711590Srgrimes if (pipe(pivec) < 0) { 37274769Smikeh warn("pipe"); 37377274Smikeh return (name); 3741590Srgrimes } 37577274Smikeh (void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name); 37677274Smikeh if ((sh = value("SHELL")) == NULL) 37777274Smikeh sh = _PATH_CSHELL; 37877274Smikeh pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL); 3791590Srgrimes if (pid < 0) { 38077274Smikeh (void)close(pivec[0]); 38177274Smikeh (void)close(pivec[1]); 38277274Smikeh return (NULL); 3831590Srgrimes } 38477274Smikeh (void)close(pivec[1]); 3851590Srgrimes l = read(pivec[0], xname, BUFSIZ); 38677274Smikeh (void)close(pivec[0]); 38777274Smikeh if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) && 38877274Smikeh WTERMSIG(wait_status) != SIGPIPE) { 3891590Srgrimes fprintf(stderr, "\"%s\": Expansion failed.\n", name); 39077274Smikeh return (NULL); 3911590Srgrimes } 3921590Srgrimes if (l < 0) { 39374769Smikeh warn("read"); 39477274Smikeh return (NULL); 3951590Srgrimes } 3961590Srgrimes if (l == 0) { 3971590Srgrimes fprintf(stderr, "\"%s\": No match.\n", name); 39877274Smikeh return (NULL); 3991590Srgrimes } 4001590Srgrimes if (l == BUFSIZ) { 4011590Srgrimes fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name); 40277274Smikeh return (NULL); 4031590Srgrimes } 40474769Smikeh xname[l] = '\0'; 4051590Srgrimes for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--) 4061590Srgrimes ; 4071590Srgrimes cp[1] = '\0'; 40874769Smikeh if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) { 4091590Srgrimes fprintf(stderr, "\"%s\": Ambiguous.\n", name); 41077274Smikeh return (NULL); 4111590Srgrimes } 41277274Smikeh return (savestr(xname)); 4131590Srgrimes} 4141590Srgrimes 4151590Srgrimes/* 4161590Srgrimes * Determine the current folder directory name. 4171590Srgrimes */ 4181590Srgrimesint 419216564Scharniergetfold(char *name, int namelen) 4201590Srgrimes{ 4211590Srgrimes char *folder; 42274769Smikeh int copylen; 4231590Srgrimes 42477274Smikeh if ((folder = value("folder")) == NULL) 4251590Srgrimes return (-1); 4261590Srgrimes if (*folder == '/') 42774769Smikeh copylen = strlcpy(name, folder, namelen); 4281590Srgrimes else 42977274Smikeh copylen = snprintf(name, namelen, "%s/%s", 43077274Smikeh homedir ? homedir : ".", folder); 43181979Sbrian return (copylen < 0 || copylen >= namelen ? (-1) : (0)); 4321590Srgrimes} 4331590Srgrimes 4341590Srgrimes/* 4351590Srgrimes * Return the name of the dead.letter file. 4361590Srgrimes */ 4371590Srgrimeschar * 438216564Scharniergetdeadletter(void) 4391590Srgrimes{ 44077274Smikeh char *cp; 4411590Srgrimes 44277274Smikeh if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL) 4431590Srgrimes cp = expand("~/dead.letter"); 4441590Srgrimes else if (*cp != '/') { 4451590Srgrimes char buf[PATHSIZE]; 4461590Srgrimes 44777274Smikeh (void)snprintf(buf, sizeof(buf), "~/%s", cp); 4481590Srgrimes cp = expand(buf); 4491590Srgrimes } 45077274Smikeh return (cp); 4511590Srgrimes} 452