fio.c revision 81979
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 * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3574769Smikeh#if 0
361590Srgrimesstatic char sccsid[] = "@(#)fio.c	8.1 (Berkeley) 6/6/93";
3774769Smikeh#endif
3874769Smikehstatic const char rcsid[] =
3974769Smikeh  "$FreeBSD: head/usr.bin/mail/fio.c 81979 2001-08-20 14:46:40Z brian $";
401590Srgrimes#endif /* not lint */
411590Srgrimes
421590Srgrimes#include "rcv.h"
431590Srgrimes#include <sys/file.h>
441590Srgrimes#include <sys/wait.h>
451590Srgrimes
461590Srgrimes#include <unistd.h>
471590Srgrimes#include <paths.h>
481590Srgrimes#include <errno.h>
491590Srgrimes#include "extern.h"
501590Srgrimes
511590Srgrimes/*
521590Srgrimes * Mail -- a mail program
531590Srgrimes *
541590Srgrimes * File I/O.
551590Srgrimes */
561590Srgrimes
5777274Smikehextern int wait_status;
5877274Smikeh
591590Srgrimes/*
601590Srgrimes * Set up the input pointers while copying the mail file into /tmp.
611590Srgrimes */
621590Srgrimesvoid
631590Srgrimessetptr(ibuf)
6477274Smikeh	FILE *ibuf;
651590Srgrimes{
6677274Smikeh	int c, count;
6777274Smikeh	char *cp, *cp2;
681590Srgrimes	struct message this;
691590Srgrimes	FILE *mestmp;
701590Srgrimes	off_t offset;
711590Srgrimes	int maybe, inhead;
7274769Smikeh	char linebuf[LINESIZE], pathbuf[PATHSIZE];
731590Srgrimes
741590Srgrimes	/* Get temporary file. */
7574769Smikeh	(void)snprintf(pathbuf, sizeof(pathbuf), "%s/mail.XXXXXXXXXX", tmpdir);
7674769Smikeh	if ((c = mkstemp(pathbuf)) == -1 || (mestmp = Fdopen(c, "r+")) == NULL)
7774769Smikeh		err(1, "can't open %s", pathbuf);
7877274Smikeh	(void)rm(pathbuf);
791590Srgrimes
801590Srgrimes	msgCount = 0;
811590Srgrimes	maybe = 1;
821590Srgrimes	inhead = 0;
831590Srgrimes	offset = 0;
841590Srgrimes	this.m_flag = MUSED|MNEW;
851590Srgrimes	this.m_size = 0;
861590Srgrimes	this.m_lines = 0;
871590Srgrimes	this.m_block = 0;
881590Srgrimes	this.m_offset = 0;
891590Srgrimes	for (;;) {
9074769Smikeh		if (fgets(linebuf, sizeof(linebuf), ibuf) == NULL) {
9174769Smikeh			if (append(&this, mestmp))
9274769Smikeh				errx(1, "temporary file");
931590Srgrimes			makemessage(mestmp);
941590Srgrimes			return;
951590Srgrimes		}
961590Srgrimes		count = strlen(linebuf);
9776455Smikeh		/*
9876455Smikeh		 * Transforms lines ending in <CR><LF> to just <LF>.
9976455Smikeh		 * This allows mail to be able to read Eudora mailboxes.
10076455Smikeh		 */
10176455Smikeh		if (count >= 2 && linebuf[count - 1] == '\n' &&
10276455Smikeh		    linebuf[count - 2] == '\r') {
10376455Smikeh			count--;
10476455Smikeh			linebuf[count - 1] = '\n';
10576455Smikeh		}
10676455Smikeh
10777274Smikeh		(void)fwrite(linebuf, sizeof(*linebuf), count, otf);
10874769Smikeh		if (ferror(otf))
10974769Smikeh			errx(1, "/tmp");
11074769Smikeh		if (count)
11174769Smikeh			linebuf[count - 1] = '\0';
1121590Srgrimes		if (maybe && linebuf[0] == 'F' && ishead(linebuf)) {
1131590Srgrimes			msgCount++;
11474769Smikeh			if (append(&this, mestmp))
11574769Smikeh				errx(1, "temporary file");
1161590Srgrimes			this.m_flag = MUSED|MNEW;
1171590Srgrimes			this.m_size = 0;
1181590Srgrimes			this.m_lines = 0;
1191590Srgrimes			this.m_block = blockof(offset);
12067496Sphk			this.m_offset = boffsetof(offset);
1211590Srgrimes			inhead = 1;
1221590Srgrimes		} else if (linebuf[0] == 0) {
1231590Srgrimes			inhead = 0;
1241590Srgrimes		} else if (inhead) {
1251590Srgrimes			for (cp = linebuf, cp2 = "status";; cp++) {
12677274Smikeh				if ((c = *cp2++) == '\0') {
1271590Srgrimes					while (isspace(*cp++))
1281590Srgrimes						;
1291590Srgrimes					if (cp[-1] != ':')
1301590Srgrimes						break;
13177274Smikeh					while ((c = *cp++) != '\0')
1321590Srgrimes						if (c == 'R')
1331590Srgrimes							this.m_flag |= MREAD;
1341590Srgrimes						else if (c == 'O')
1351590Srgrimes							this.m_flag &= ~MNEW;
1361590Srgrimes					inhead = 0;
1371590Srgrimes					break;
1381590Srgrimes				}
1391590Srgrimes				if (*cp != c && *cp != toupper(c))
1401590Srgrimes					break;
1411590Srgrimes			}
1421590Srgrimes		}
1431590Srgrimes		offset += count;
1441590Srgrimes		this.m_size += count;
1451590Srgrimes		this.m_lines++;
1461590Srgrimes		maybe = linebuf[0] == 0;
1471590Srgrimes	}
1481590Srgrimes}
1491590Srgrimes
1501590Srgrimes/*
1511590Srgrimes * Drop the passed line onto the passed output buffer.
1521590Srgrimes * If a write error occurs, return -1, else the count of
1531590Srgrimes * characters written, including the newline.
1541590Srgrimes */
1551590Srgrimesint
1561590Srgrimesputline(obuf, linebuf)
1571590Srgrimes	FILE *obuf;
1581590Srgrimes	char *linebuf;
1591590Srgrimes{
16077274Smikeh	int c;
1611590Srgrimes
1621590Srgrimes	c = strlen(linebuf);
16377274Smikeh	(void)fwrite(linebuf, sizeof(*linebuf), c, obuf);
16477274Smikeh	fprintf(obuf, "\n");
1651590Srgrimes	if (ferror(obuf))
1661590Srgrimes		return (-1);
1671590Srgrimes	return (c + 1);
1681590Srgrimes}
1691590Srgrimes
1701590Srgrimes/*
1711590Srgrimes * Read up a line from the specified input into the line
1721590Srgrimes * buffer.  Return the number of characters read.  Do not
17376455Smikeh * include the newline (or carriage return) at the end.
1741590Srgrimes */
1751590Srgrimesint
1761590Srgrimesreadline(ibuf, linebuf, linesize)
1771590Srgrimes	FILE *ibuf;
1781590Srgrimes	char *linebuf;
1791590Srgrimes	int linesize;
1801590Srgrimes{
18177274Smikeh	int n;
1821590Srgrimes
1831590Srgrimes	clearerr(ibuf);
1841590Srgrimes	if (fgets(linebuf, linesize, ibuf) == NULL)
18577274Smikeh		return (-1);
1861590Srgrimes	n = strlen(linebuf);
1871590Srgrimes	if (n > 0 && linebuf[n - 1] == '\n')
1881590Srgrimes		linebuf[--n] = '\0';
18976455Smikeh	if (n > 0 && linebuf[n - 1] == '\r')
19076455Smikeh		linebuf[--n] = '\0';
19177274Smikeh	return (n);
1921590Srgrimes}
1931590Srgrimes
1941590Srgrimes/*
1951590Srgrimes * Return a file buffer all ready to read up the
1961590Srgrimes * passed message pointer.
1971590Srgrimes */
1981590SrgrimesFILE *
1991590Srgrimessetinput(mp)
20077274Smikeh	struct message *mp;
2011590Srgrimes{
2021590Srgrimes
20377274Smikeh	(void)fflush(otf);
20474769Smikeh	if (fseek(itf, (long)positionof(mp->m_block, mp->m_offset), 0) < 0)
20574769Smikeh		err(1, "fseek");
2061590Srgrimes	return (itf);
2071590Srgrimes}
2081590Srgrimes
2091590Srgrimes/*
2101590Srgrimes * Take the data out of the passed ghost file and toss it into
2111590Srgrimes * a dynamically allocated message structure.
2121590Srgrimes */
2131590Srgrimesvoid
2141590Srgrimesmakemessage(f)
2151590Srgrimes	FILE *f;
2161590Srgrimes{
21777274Smikeh	int size = (msgCount + 1) * sizeof(struct message);
2181590Srgrimes
2191590Srgrimes	if (message != 0)
22077274Smikeh		(void)free(message);
22177274Smikeh	if ((message = malloc((unsigned)size)) == NULL)
22274769Smikeh		err(1, "Out of memory");
2231590Srgrimes	dot = message;
22477274Smikeh	size -= sizeof(struct message);
22577274Smikeh	(void)fflush(f);
22677274Smikeh	(void)lseek(fileno(f), (off_t)sizeof(*message), 0);
22777274Smikeh	if (read(fileno(f), (char *)message, size) != size)
22874769Smikeh		errx(1, "Message temporary file corrupted");
2291590Srgrimes	message[msgCount].m_size = 0;
2301590Srgrimes	message[msgCount].m_lines = 0;
23177274Smikeh	(void)Fclose(f);
2321590Srgrimes}
2331590Srgrimes
2341590Srgrimes/*
2351590Srgrimes * Append the passed message descriptor onto the temp file.
2361590Srgrimes * If the write fails, return 1, else 0
2371590Srgrimes */
2381590Srgrimesint
2391590Srgrimesappend(mp, f)
2401590Srgrimes	struct message *mp;
2411590Srgrimes	FILE *f;
2421590Srgrimes{
24377274Smikeh	return (fwrite((char *)mp, sizeof(*mp), 1, f) != 1);
2441590Srgrimes}
2451590Srgrimes
2461590Srgrimes/*
2471590Srgrimes * Delete a file, but only if the file is a plain file.
2481590Srgrimes */
2491590Srgrimesint
2501590Srgrimesrm(name)
2511590Srgrimes	char *name;
2521590Srgrimes{
2531590Srgrimes	struct stat sb;
2541590Srgrimes
2551590Srgrimes	if (stat(name, &sb) < 0)
25677274Smikeh		return (-1);
2571590Srgrimes	if (!S_ISREG(sb.st_mode)) {
2581590Srgrimes		errno = EISDIR;
25977274Smikeh		return (-1);
2601590Srgrimes	}
26177274Smikeh	return (unlink(name));
2621590Srgrimes}
2631590Srgrimes
2641590Srgrimesstatic int sigdepth;		/* depth of holdsigs() */
2651590Srgrimesstatic int omask;
2661590Srgrimes/*
2671590Srgrimes * Hold signals SIGHUP, SIGINT, and SIGQUIT.
2681590Srgrimes */
2691590Srgrimesvoid
2701590Srgrimesholdsigs()
2711590Srgrimes{
2721590Srgrimes
2731590Srgrimes	if (sigdepth++ == 0)
2741590Srgrimes		omask = sigblock(sigmask(SIGHUP)|sigmask(SIGINT)|sigmask(SIGQUIT));
2751590Srgrimes}
2761590Srgrimes
2771590Srgrimes/*
2781590Srgrimes * Release signals SIGHUP, SIGINT, and SIGQUIT.
2791590Srgrimes */
2801590Srgrimesvoid
2811590Srgrimesrelsesigs()
2821590Srgrimes{
2831590Srgrimes
2841590Srgrimes	if (--sigdepth == 0)
28577274Smikeh		(void)sigsetmask(omask);
2861590Srgrimes}
2871590Srgrimes
2881590Srgrimes/*
2891590Srgrimes * Determine the size of the file possessed by
2901590Srgrimes * the passed buffer.
2911590Srgrimes */
2921590Srgrimesoff_t
2931590Srgrimesfsize(iob)
2941590Srgrimes	FILE *iob;
2951590Srgrimes{
2961590Srgrimes	struct stat sbuf;
2971590Srgrimes
2981590Srgrimes	if (fstat(fileno(iob), &sbuf) < 0)
29977274Smikeh		return (0);
30077274Smikeh	return (sbuf.st_size);
3011590Srgrimes}
3021590Srgrimes
3031590Srgrimes/*
3041590Srgrimes * Evaluate the string given as a new mailbox name.
3051590Srgrimes * Supported meta characters:
3061590Srgrimes *	%	for my system mail box
3071590Srgrimes *	%user	for user's system mail box
3081590Srgrimes *	#	for previous file
3091590Srgrimes *	&	invoker's mbox file
3101590Srgrimes *	+file	file in folder directory
3111590Srgrimes *	any shell meta character
3121590Srgrimes * Return the file name as a dynamic string.
3131590Srgrimes */
3141590Srgrimeschar *
3151590Srgrimesexpand(name)
31677274Smikeh	char *name;
3171590Srgrimes{
3181590Srgrimes	char xname[PATHSIZE];
3191590Srgrimes	char cmdbuf[PATHSIZE];		/* also used for file names */
32077274Smikeh	int pid, l;
32177274Smikeh	char *cp, *sh;
3221590Srgrimes	int pivec[2];
3231590Srgrimes	struct stat sbuf;
3241590Srgrimes
3251590Srgrimes	/*
3261590Srgrimes	 * The order of evaluation is "%" and "#" expand into constants.
3271590Srgrimes	 * "&" can expand into "+".  "+" can expand into shell meta characters.
3281590Srgrimes	 * Shell meta characters expand into constants.
3291590Srgrimes	 * This way, we make no recursive expansion.
3301590Srgrimes	 */
3311590Srgrimes	switch (*name) {
3321590Srgrimes	case '%':
33374769Smikeh		findmail(name[1] ? name + 1 : myname, xname, sizeof(xname));
33477274Smikeh		return (savestr(xname));
3351590Srgrimes	case '#':
3361590Srgrimes		if (name[1] != 0)
3371590Srgrimes			break;
3381590Srgrimes		if (prevfile[0] == 0) {
3391590Srgrimes			printf("No previous file\n");
34077274Smikeh			return (NULL);
3411590Srgrimes		}
34277274Smikeh		return (savestr(prevfile));
3431590Srgrimes	case '&':
34477274Smikeh		if (name[1] == 0 && (name = value("MBOX")) == NULL)
3451590Srgrimes			name = "~/mbox";
3461590Srgrimes		/* fall through */
3471590Srgrimes	}
34874769Smikeh	if (name[0] == '+' && getfold(cmdbuf, sizeof(cmdbuf)) >= 0) {
34977274Smikeh		(void)snprintf(xname, sizeof(xname), "%s/%s", cmdbuf, name + 1);
3501590Srgrimes		name = savestr(xname);
3511590Srgrimes	}
3521590Srgrimes	/* catch the most common shell meta character */
35374769Smikeh	if (name[0] == '~' && homedir != NULL &&
35474769Smikeh	    (name[1] == '/' || name[1] == '\0')) {
35577274Smikeh		(void)snprintf(xname, sizeof(xname), "%s%s", homedir, name + 1);
3561590Srgrimes		name = savestr(xname);
3571590Srgrimes	}
35874769Smikeh	if (!strpbrk(name, "~{[*?$`'\"\\"))
35977274Smikeh		return (name);
3601590Srgrimes	if (pipe(pivec) < 0) {
36174769Smikeh		warn("pipe");
36277274Smikeh		return (name);
3631590Srgrimes	}
36477274Smikeh	(void)snprintf(cmdbuf, sizeof(cmdbuf), "echo %s", name);
36577274Smikeh	if ((sh = value("SHELL")) == NULL)
36677274Smikeh		sh = _PATH_CSHELL;
36777274Smikeh	pid = start_command(sh, 0, -1, pivec[1], "-c", cmdbuf, NULL);
3681590Srgrimes	if (pid < 0) {
36977274Smikeh		(void)close(pivec[0]);
37077274Smikeh		(void)close(pivec[1]);
37177274Smikeh		return (NULL);
3721590Srgrimes	}
37377274Smikeh	(void)close(pivec[1]);
3741590Srgrimes	l = read(pivec[0], xname, BUFSIZ);
37577274Smikeh	(void)close(pivec[0]);
37677274Smikeh	if (wait_child(pid) < 0 && WIFSIGNALED(wait_status) &&
37777274Smikeh	    WTERMSIG(wait_status) != SIGPIPE) {
3781590Srgrimes		fprintf(stderr, "\"%s\": Expansion failed.\n", name);
37977274Smikeh		return (NULL);
3801590Srgrimes	}
3811590Srgrimes	if (l < 0) {
38274769Smikeh		warn("read");
38377274Smikeh		return (NULL);
3841590Srgrimes	}
3851590Srgrimes	if (l == 0) {
3861590Srgrimes		fprintf(stderr, "\"%s\": No match.\n", name);
38777274Smikeh		return (NULL);
3881590Srgrimes	}
3891590Srgrimes	if (l == BUFSIZ) {
3901590Srgrimes		fprintf(stderr, "\"%s\": Expansion buffer overflow.\n", name);
39177274Smikeh		return (NULL);
3921590Srgrimes	}
39374769Smikeh	xname[l] = '\0';
3941590Srgrimes	for (cp = &xname[l-1]; *cp == '\n' && cp > xname; cp--)
3951590Srgrimes		;
3961590Srgrimes	cp[1] = '\0';
39774769Smikeh	if (strchr(xname, ' ') && stat(xname, &sbuf) < 0) {
3981590Srgrimes		fprintf(stderr, "\"%s\": Ambiguous.\n", name);
39977274Smikeh		return (NULL);
4001590Srgrimes	}
40177274Smikeh	return (savestr(xname));
4021590Srgrimes}
4031590Srgrimes
4041590Srgrimes/*
4051590Srgrimes * Determine the current folder directory name.
4061590Srgrimes */
4071590Srgrimesint
40874769Smikehgetfold(name, namelen)
4091590Srgrimes	char *name;
41074769Smikeh	int namelen;
4111590Srgrimes{
4121590Srgrimes	char *folder;
41374769Smikeh	int copylen;
4141590Srgrimes
41577274Smikeh	if ((folder = value("folder")) == NULL)
4161590Srgrimes		return (-1);
4171590Srgrimes	if (*folder == '/')
41874769Smikeh		copylen = strlcpy(name, folder, namelen);
4191590Srgrimes	else
42077274Smikeh		copylen = snprintf(name, namelen, "%s/%s",
42177274Smikeh		    homedir ? homedir : ".", folder);
42281979Sbrian	return (copylen < 0 || copylen >= namelen ? (-1) : (0));
4231590Srgrimes}
4241590Srgrimes
4251590Srgrimes/*
4261590Srgrimes * Return the name of the dead.letter file.
4271590Srgrimes */
4281590Srgrimeschar *
4291590Srgrimesgetdeadletter()
4301590Srgrimes{
43177274Smikeh	char *cp;
4321590Srgrimes
43377274Smikeh	if ((cp = value("DEAD")) == NULL || (cp = expand(cp)) == NULL)
4341590Srgrimes		cp = expand("~/dead.letter");
4351590Srgrimes	else if (*cp != '/') {
4361590Srgrimes		char buf[PATHSIZE];
4371590Srgrimes
43877274Smikeh		(void)snprintf(buf, sizeof(buf), "~/%s", cp);
4391590Srgrimes		cp = expand(buf);
4401590Srgrimes	}
44177274Smikeh	return (cp);
4421590Srgrimes}
443