xref: /freebsd/usr.bin/mail/main.c (revision 5b31cc94b10d4bb7109c6b27940a0fc76a44a331)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1980, 1993\n\
35 	The Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37 
38 #ifndef lint
39 #endif /* not lint */
40 #include <sys/cdefs.h>
41 #include "rcv.h"
42 #include <fcntl.h>
43 #include "extern.h"
44 
45 /*
46  * Mail -- a mail program
47  *
48  * Startup -- interface with user.
49  */
50 int	msgCount;
51 int	rcvmode;
52 int	sawcom;
53 char	*Tflag;
54 int	senderr;
55 int	edit;
56 int	readonly;
57 int	noreset;
58 int	sourcing;
59 int	loading;
60 int	cond;
61 FILE	*itf;
62 FILE	*otf;
63 int	image;
64 FILE	*input;
65 char	mailname[PATHSIZE];
66 char	prevfile[PATHSIZE];
67 char	*homedir;
68 char	*myname;
69 off_t	mailsize;
70 int	lexnumber;
71 char	lexstring[STRINGLEN];
72 int	regretp;
73 int	regretstack[REGDEP];
74 char	*string_stack[REGDEP];
75 int	numberstack[REGDEP];
76 struct	message	*dot;
77 struct	message	*message;
78 struct	var	*variables[HSHSIZE];
79 struct	grouphead	*groups[HSHSIZE];
80 struct	ignoretab	ignore[2];
81 
82 struct	ignoretab	saveignore[2];
83 
84 struct	ignoretab	ignoreall[2];
85 char	**altnames;
86 int	debug;
87 int	screenwidth;
88 int	screenheight;
89 
90 int	realscreenheight;
91 
92 jmp_buf	srbuf;
93 
94 static jmp_buf	hdrjmp;
95 
96 extern const char *version;
97 
98 int
99 main(int argc, char *argv[])
100 {
101 	int i;
102 	struct name *to, *cc, *bcc, *smopts;
103 	char *subject, *replyto;
104 	char *ef, *rc;
105 	char nosrc = 0;
106 	sig_t prevint;
107 
108 	/*
109 	 * Set up a reasonable environment.
110 	 * Figure out whether we are being run interactively,
111 	 * start the SIGCHLD catcher, and so forth.
112 	 */
113 	(void)signal(SIGCHLD, sigchild);
114 	if (isatty(0))
115 		assign("interactive", "");
116 	image = -1;
117 	/*
118 	 * Now, determine how we are being used.
119 	 * We successively pick off - flags.
120 	 * If there is anything left, it is the base of the list
121 	 * of users to mail to.  Argp will be set to point to the
122 	 * first of these users.
123 	 */
124 	ef = NULL;
125 	to = NULL;
126 	cc = NULL;
127 	bcc = NULL;
128 	smopts = NULL;
129 	subject = NULL;
130 	while ((i = getopt(argc, argv, "FEHINT:b:c:edfins:u:v")) != -1) {
131 		switch (i) {
132 		case 'T':
133 			/*
134 			 * Next argument is temp file to write which
135 			 * articles have been read/deleted for netnews.
136 			 */
137 			Tflag = optarg;
138 			if ((i = open(Tflag, O_CREAT | O_TRUNC | O_WRONLY,
139 			    0600)) < 0)
140 				err(1, "%s", Tflag);
141 			(void)close(i);
142 			break;
143 		case 'u':
144 			/*
145 			 * Next argument is person to pretend to be.
146 			 */
147 			myname = optarg;
148 			unsetenv("MAIL");
149 			break;
150 		case 'i':
151 			/*
152 			 * User wants to ignore interrupts.
153 			 * Set the variable "ignore"
154 			 */
155 			assign("ignore", "");
156 			break;
157 		case 'd':
158 			debug++;
159 			break;
160 		case 'e':
161 			/*
162 			 * User wants to check mail and exit.
163 			 */
164 			assign("checkmode", "");
165 			break;
166 		case 'H':
167 			/*
168 			 * User wants a header summary only.
169 			 */
170 			assign("headersummary", "");
171 			break;
172 		case 'F':
173 			/*
174 			 * User wants to record messages to files
175 			 * named after first recipient username.
176 			 */
177 			assign("recordrecip", "");
178 			break;
179 		case 's':
180 			/*
181 			 * Give a subject field for sending from
182 			 * non terminal
183 			 */
184 			subject = optarg;
185 			break;
186 		case 'f':
187 			/*
188 			 * User is specifying file to "edit" with Mail,
189 			 * as opposed to reading system mailbox.
190 			 * If no argument is given after -f, we read his
191 			 * mbox file.
192 			 *
193 			 * getopt() can't handle optional arguments, so here
194 			 * is an ugly hack to get around it.
195 			 */
196 			if ((argv[optind] != NULL) && (argv[optind][0] != '-'))
197 				ef = argv[optind++];
198 			else
199 				ef = "&";
200 			break;
201 		case 'n':
202 			/*
203 			 * User doesn't want to source /usr/lib/Mail.rc
204 			 */
205 			nosrc++;
206 			break;
207 		case 'N':
208 			/*
209 			 * Avoid initial header printing.
210 			 */
211 			assign("noheader", "");
212 			break;
213 		case 'v':
214 			/*
215 			 * Send mailer verbose flag
216 			 */
217 			assign("verbose", "");
218 			break;
219 		case 'I':
220 			/*
221 			 * We're interactive
222 			 */
223 			assign("interactive", "");
224 			break;
225 		case 'c':
226 			/*
227 			 * Get Carbon Copy Recipient list
228 			 */
229 			cc = cat(cc, nalloc(optarg, GCC));
230 			break;
231 		case 'b':
232 			/*
233 			 * Get Blind Carbon Copy Recipient list
234 			 */
235 			bcc = cat(bcc, nalloc(optarg, GBCC));
236 			break;
237 		case 'E':
238 			/*
239 			 * Don't send empty files.
240 			 */
241 			assign("dontsendempty", "");
242 			break;
243 		case '?':
244 			fprintf(stderr, "\
245 Usage: %s [-dEiInv] [-s subject] [-c cc-addr] [-b bcc-addr] [-F] to-addr ...\n\
246        %*s [-sendmail-option ...]\n\
247        %s [-dEHiInNv] [-F] -f [name]\n\
248        %s [-dEHiInNv] [-F] [-u user]\n\
249        %s [-d] -e [-f name]\n", __progname, (int)strlen(__progname), "",
250 				__progname, __progname, __progname);
251 			exit(1);
252 		}
253 	}
254 	for (i = optind; (argv[i] != NULL) && (*argv[i] != '-'); i++)
255 		to = cat(to, nalloc(argv[i], GTO));
256 	for (; argv[i] != NULL; i++)
257 		smopts = cat(smopts, nalloc(argv[i], 0));
258 	/*
259 	 * Check for inconsistent arguments.
260 	 */
261 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
262 		errx(1, "You must specify direct recipients with -s, -c, or -b.");
263 	if (ef != NULL && to != NULL)
264 		errx(1, "Cannot give -f and people to send to.");
265 	tinit();
266 	setscreensize();
267 	input = stdin;
268 	rcvmode = !to;
269 	spreserve();
270 	if (!nosrc) {
271 		char *s, *path_rc;
272 
273 		if ((path_rc = malloc(sizeof(_PATH_MASTER_RC))) == NULL)
274 			err(1, "malloc(path_rc) failed");
275 
276 		strcpy(path_rc, _PATH_MASTER_RC);
277 		while ((s = strsep(&path_rc, ":")) != NULL)
278 			if (*s != '\0')
279 				load(s);
280 	}
281 	/*
282 	 * Expand returns a savestr, but load only uses the file name
283 	 * for fopen, so it's safe to do this.
284 	 */
285 	if ((rc = getenv("MAILRC")) == NULL)
286 		rc = "~/.mailrc";
287 	load(expand(rc));
288 
289 	replyto = value("REPLYTO");
290 	if (!rcvmode) {
291 		mail(to, cc, bcc, smopts, subject, replyto);
292 		/*
293 		 * why wait?
294 		 */
295 		exit(senderr);
296 	}
297 
298 	if(value("checkmode") != NULL) {
299 		if (ef == NULL)
300 			ef = "%";
301 		if (setfile(ef) <= 0)
302 			/* Either an error has occurred, or no mail */
303 			exit(1);
304 		else
305 			exit(0);
306 		/* NOTREACHED */
307 	}
308 
309 	/*
310 	 * Ok, we are reading mail.
311 	 * Decide whether we are editing a mailbox or reading
312 	 * the system mailbox, and open up the right stuff.
313 	 */
314 	if (ef == NULL)
315 		ef = "%";
316 	if (setfile(ef) < 0)
317 		exit(1);		/* error already reported */
318 	if (setjmp(hdrjmp) == 0) {
319 		if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
320 			(void)signal(SIGINT, hdrstop);
321 		if (value("quiet") == NULL)
322 			printf("Mail version %s.  Type ? for help.\n",
323 				version);
324 		announce();
325 		(void)fflush(stdout);
326 		(void)signal(SIGINT, prevint);
327 	}
328 
329 	/* If we were in header summary mode, it's time to exit. */
330 	if (value("headersummary") != NULL)
331 		exit(0);
332 
333 	commands();
334 	(void)signal(SIGHUP, SIG_IGN);
335 	(void)signal(SIGINT, SIG_IGN);
336 	(void)signal(SIGQUIT, SIG_IGN);
337 	quit();
338 	exit(0);
339 }
340 
341 /*
342  * Interrupt printing of the headers.
343  */
344 /*ARGSUSED*/
345 void
346 hdrstop(int signo __unused)
347 {
348 
349 	(void)fflush(stdout);
350 	fprintf(stderr, "\nInterrupt\n");
351 	longjmp(hdrjmp, 1);
352 }
353 
354 /*
355  * Compute what the screen size for printing headers should be.
356  * We use the following algorithm for the height:
357  *	If baud rate < 1200, use  9
358  *	If baud rate = 1200, use 14
359  *	If baud rate > 1200, use 24 or ws_row
360  * Width is either 80 or ws_col;
361  */
362 void
363 setscreensize(void)
364 {
365 	struct termios tbuf;
366 	struct winsize ws;
367 	speed_t speed;
368 
369 	if (ioctl(1, TIOCGWINSZ, (char *)&ws) < 0)
370 		ws.ws_col = ws.ws_row = 0;
371 	if (tcgetattr(1, &tbuf) < 0)
372 		speed = B9600;
373 	else
374 		speed = cfgetospeed(&tbuf);
375 	if (speed < B1200)
376 		screenheight = 9;
377 	else if (speed == B1200)
378 		screenheight = 14;
379 	else if (ws.ws_row != 0)
380 		screenheight = ws.ws_row;
381 	else
382 		screenheight = 24;
383 	if ((realscreenheight = ws.ws_row) == 0)
384 		realscreenheight = 24;
385 	if ((screenwidth = ws.ws_col) == 0)
386 		screenwidth = 80;
387 }
388