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