xref: /openbsd/usr.bin/mail/main.c (revision 73471bf0)
1 /*	$OpenBSD: main.c,v 1.35 2021/01/26 18:21:47 deraadt Exp $	*/
2 /*	$NetBSD: main.c,v 1.7 1997/05/13 06:15:57 mikel Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include "rcv.h"
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36 #include "extern.h"
37 
38 int	msgCount;			/* Count of messages read in */
39 int	rcvmode;			/* True if receiving mail */
40 int	sawcom;				/* Set after first command */
41 int	senderr;			/* An error while checking */
42 int	edit;				/* Indicates editing a file */
43 int	readonly;			/* Will be unable to rewrite file */
44 int	noreset;			/* String resets suspended */
45 int	sourcing;			/* Currently reading variant file */
46 int	loading;			/* Loading user definitions */
47 int	cond;				/* Current state of conditional exc. */
48 FILE	*itf;				/* Input temp file buffer */
49 FILE	*otf;				/* Output temp file buffer */
50 int	image;				/* File descriptor for image of msg */
51 FILE	*input;				/* Current command input file */
52 char	mailname[PATHSIZE];		/* Name of current file */
53 char	prevfile[PATHSIZE];		/* Name of previous file */
54 char	*homedir;			/* Path name of home directory */
55 const char
56 	*myname;			/* My login name */
57 off_t	mailsize;			/* Size of system mailbox */
58 int	lexnumber;			/* Number of TNUMBER from scan() */
59 char	lexstring[STRINGLEN];		/* String from TSTRING, scan() */
60 int	regretp;			/* Pointer to TOS of regret tokens */
61 int	regretstack[REGDEP];		/* Stack of regretted tokens */
62 char	*string_stack[REGDEP];		/* Stack of regretted strings */
63 int	numberstack[REGDEP];		/* Stack of regretted numbers */
64 struct	message	*dot;			/* Pointer to current message */
65 struct	message	*message;		/* The actual message structure */
66 struct	var	*variables[HSHSIZE];	/* Pointer to active var list */
67 struct	grouphead	*groups[HSHSIZE];/* Pointer to active groups */
68 struct	ignoretab	ignore[2];	/* ignored and retained fields
69 					   0 is ignore, 1 is retain */
70 struct	ignoretab	saveignore[2];	/* ignored and retained fields
71 					   on save to folder */
72 struct	ignoretab	ignoreall[2];	/* special, ignore all headers */
73 char	**altnames;			/* List of alternate names for user */
74 int	debug;				/* Debug flag set */
75 int	screenwidth;			/* Screen width, or best guess */
76 int	screenheight;			/* Screen height, or best guess,
77 					   for "header" command */
78 int	realscreenheight;		/* the real screen height */
79 int	uflag;				/* Are we in -u mode? */
80 sigset_t intset;			/* Signal set that is just SIGINT */
81 
82 /*
83  * The pointers for the string allocation routines,
84  * there are NSPACE independent areas.
85  * The first holds STRINGSIZE bytes, the next
86  * twice as much, and so on.
87  */
88 struct strings stringdope[NSPACE];
89 
90 __dead	void	usage(void);
91 	int	main(int, char **);
92 
93 /*
94  * Mail -- a mail program
95  *
96  * Startup -- interface with user.
97  */
98 
99 int
100 main(int argc, char **argv)
101 {
102 	int i;
103 	struct name *to, *cc, *bcc, *smopts;
104 	char *fromaddr;
105 	char *subject;
106 	char *ef;
107 	char nosrc = 0;
108 	char *rc;
109 	extern const char version[];
110 
111 	if (pledge("stdio rpath wpath cpath getpw tmppath fattr tty flock proc exec",
112 	    NULL) == -1)
113 		err(1, "pledge");
114 
115 	/*
116 	 * Set up a reasonable environment.
117 	 * Figure out whether we are being run interactively,
118 	 * start the SIGCHLD catcher, and so forth.
119 	 */
120 	(void)signal(SIGCHLD, sigchild);
121 	(void)signal(SIGPIPE, SIG_IGN);
122 	if (isatty(0))
123 		assign("interactive", "");
124 	image = -1;
125 	/*
126 	 * Now, determine how we are being used.
127 	 * We successively pick off - flags.
128 	 * If there is anything left, it is the base of the list
129 	 * of users to mail to.  Argp will be set to point to the
130 	 * first of these users.
131 	 */
132 	ef = NULL;
133 	to = NULL;
134 	cc = NULL;
135 	bcc = NULL;
136 	smopts = NULL;
137 	fromaddr = NULL;
138 	subject = NULL;
139 	while ((i = getopt(argc, argv, "EINb:c:dfinr:s:u:v")) != -1) {
140 		switch (i) {
141 		case 'u':
142 			/*
143 			 * Next argument is person to pretend to be.
144 			 */
145 			if (strlen(optarg) >= LOGIN_NAME_MAX)
146 				errx(1, "username `%s' too long", optarg);
147 			unsetenv("MAIL");
148 			myname = optarg;
149 			uflag = 1;
150 			break;
151 		case 'i':
152 			/*
153 			 * User wants to ignore interrupts.
154 			 * Set the variable "ignore"
155 			 */
156 			assign("ignore", "");
157 			break;
158 		case 'd':
159 			debug++;
160 			break;
161 		case 'r':
162 			/*
163 			 * Set From: address
164 			 */
165 			fromaddr = optarg;
166 			break;
167 		case 's':
168 			/*
169 			 * Give a subject field for sending from
170 			 * non terminal
171 			 */
172 			subject = optarg;
173 			break;
174 		case 'f':
175 			/*
176 			 * User is specifying file to "edit" with Mail,
177 			 * as opposed to reading system mailbox.
178 			 * We read his mbox file unless another file
179 			 * is specified after the arguments.
180 			 */
181 			ef = "&";
182 			break;
183 		case 'n':
184 			/*
185 			 * User doesn't want to source /usr/lib/Mail.rc
186 			 */
187 			nosrc = 1;
188 			break;
189 		case 'N':
190 			/*
191 			 * Avoid initial header printing.
192 			 */
193 			assign("noheader", "");
194 			break;
195 		case 'v':
196 			/*
197 			 * Send mailer verbose flag
198 			 */
199 			assign("verbose", "");
200 			break;
201 		case 'I':
202 			/*
203 			 * We're interactive
204 			 */
205 			assign("interactive", "");
206 			break;
207 		case 'c':
208 			/*
209 			 * Get Carbon Copy Recipient list
210 			 */
211 			cc = cat(cc, nalloc(optarg, GCC));
212 			break;
213 		case 'b':
214 			/*
215 			 * Get Blind Carbon Copy Recipient list
216 			 */
217 			bcc = cat(bcc, nalloc(optarg, GBCC));
218 			break;
219 		case 'E':
220 			/*
221 			 * Don't send messages with an empty body.
222 			 */
223 			assign("skipempty", "");
224 			break;
225 		default:
226 			usage();
227 			/*NOTREACHED*/
228 		}
229 	}
230 	if (ef != NULL) {
231 		/* Check for optional mailbox file name. */
232 		if (optind < argc) {
233 			ef = argv[optind++];
234 			if (optind < argc)
235 			    errx(1, "Cannot give -f and people to send to");
236 		}
237 	} else {
238 		for (i = optind; argv[i]; i++)
239 			to = cat(to, nalloc(argv[i], GTO));
240 	}
241 	/*
242 	 * Check for inconsistent arguments.
243 	 */
244 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL ||
245 	    fromaddr != NULL))
246 		errx(1, "You must specify direct recipients with -s, -c, -b, "
247 		    "or -r");
248 	/*
249 	 * Block SIGINT except where we install an explicit handler for it.
250 	 */
251 	sigemptyset(&intset);
252 	sigaddset(&intset, SIGINT);
253 	(void)sigprocmask(SIG_BLOCK, &intset, NULL);
254 	/*
255 	 * Initialization.
256 	 */
257 	tinit();
258 	setscreensize();
259 	input = stdin;
260 	rcvmode = !to;
261 	spreserve();
262 	if (!nosrc)
263 		load(_PATH_MASTER_RC);
264 	/*
265 	 * Expand returns a savestr, but load only uses the file name
266 	 * for fopen, so it's safe to do this.
267 	 */
268 	if ((rc = getenv("MAILRC")) == 0)
269 		rc = "~/.mailrc";
270 	load(expand(rc));
271 	if (!rcvmode) {
272 		mail(to, cc, bcc, smopts, fromaddr, subject);
273 		/*
274 		 * why wait?
275 		 */
276 		exit(senderr);
277 	}
278 	/*
279 	 * Ok, we are reading mail.
280 	 * Decide whether we are editing a mailbox or reading
281 	 * the system mailbox, and open up the right stuff.
282 	 */
283 	if (ef == NULL)
284 		ef = "%";
285 	if (setfile(ef) < 0)
286 		exit(1);		/* error already reported */
287 
288 	if (value("quiet") == NULL)
289 		(void)printf("Mail version %s.  Type ? for help.\n",
290 			version);
291 	announce();
292 	(void)fflush(stdout);
293 	commands();
294 	(void)ignoresig(SIGHUP, NULL, NULL);
295 	(void)ignoresig(SIGINT, NULL, NULL);
296 	(void)ignoresig(SIGQUIT, NULL, NULL);
297 	quit();
298 	exit(0);
299 }
300 
301 /*
302  * Compute what the screen size for printing headers should be.
303  * We use the following algorithm for the height:
304  *	If baud rate < 1200, use  9
305  *	If baud rate = 1200, use 14
306  *	If baud rate > 1200, use 24 or ws_row
307  * Width is either 80 or ws_col;
308  */
309 void
310 setscreensize(void)
311 {
312 	struct termios tbuf;
313 	struct winsize ws;
314 	speed_t ospeed;
315 
316 	if (ioctl(1, TIOCGWINSZ, (char *) &ws) == -1)
317 		ws.ws_col = ws.ws_row = 0;
318 	if (tcgetattr(1, &tbuf) == -1)
319 		ospeed = 9600;
320 	else
321 		ospeed = cfgetospeed(&tbuf);
322 	if (ospeed < B1200)
323 		screenheight = 9;
324 	else if (ospeed == B1200)
325 		screenheight = 14;
326 	else if (ws.ws_row != 0)
327 		screenheight = ws.ws_row;
328 	else
329 		screenheight = 24;
330 	if ((realscreenheight = ws.ws_row) == 0)
331 		realscreenheight = 24;
332 	if ((screenwidth = ws.ws_col) == 0)
333 		screenwidth = 80;
334 }
335 
336 __dead void
337 usage(void)
338 {
339 
340 	fprintf(stderr, "usage: %s [-dEIinv] [-b list] [-c list] "
341 	    "[-r from-addr] [-s subject] to-addr ...\n", __progname);
342 	fprintf(stderr, "       %s [-dEIiNnv] -f [file]\n", __progname);
343 	fprintf(stderr, "       %s [-dEIiNnv] [-u user]\n", __progname);
344 	exit(1);
345 }
346