xref: /netbsd/usr.bin/mail/main.c (revision 6550d01e)
1 /*	$NetBSD: main.c,v 1.31 2010/01/12 14:45:31 christos Exp $	*/
2 
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 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 4/20/95";
41 #else
42 __RCSID("$NetBSD: main.c,v 1.31 2010/01/12 14:45:31 christos Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #define EXTERN
47 #include "rcv.h"
48 #undef EXTERN
49 #include <assert.h>
50 #include <util.h>
51 
52 #include "extern.h"
53 #include "sig.h"
54 
55 #ifdef USE_EDITLINE
56 #include "complete.h"
57 #endif
58 #include "format.h"
59 #ifdef MIME_SUPPORT
60 #include "mime.h"
61 #endif
62 #ifdef THREAD_SUPPORT
63 #include "thread.h"
64 #endif
65 
66 /*
67  * Mail -- a mail program
68  *
69  * Startup -- interface with user.
70  */
71 
72 __dead
73 static void
74 usage(void)
75 {
76 #ifdef MIME_SUPPORT
77 	(void)fputs("\
78 Usage: mail [-EiInv] [-r rcfile] [-s subject] [-a file] [-c cc-addr]\n\
79             [-b bcc-addr] to-addr ... [- sendmail-options ...]\n\
80        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
81        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
82 				stderr);
83 #else /* MIME_SUPPORT */
84 	(void)fputs("\
85 Usage: mail [-EiInv] [-r rcfile] [-s subject] [-c cc-addr] [-b bcc-addr]\n\
86             to-addr ... [- sendmail-options ...]\n\
87        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
88        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
89 				stderr);
90 #endif /* MIME_SUPPORT */
91 	exit(1);
92 }
93 
94 /*
95  * Compute what the screen size for printing headers should be.
96  * We use the following algorithm for the height:
97  *	If baud rate < 1200, use  9
98  *	If baud rate = 1200, use 14
99  *	If baud rate > 1200, use 24 or ws_row
100  * Width is either 80 or ws_col;
101  */
102 PUBLIC void
103 setscreensize(void)
104 {
105 	struct termios tbuf;
106 	struct winsize ws;
107 	speed_t ospeed;
108 	char *cp;
109 
110 	if (ioctl(1, TIOCGWINSZ, &ws) < 0)
111 		ws.ws_col = ws.ws_row = 0;
112 	if (tcgetattr(1, &tbuf) < 0)
113 		ospeed = 9600;
114 	else
115 		ospeed = cfgetospeed(&tbuf);
116 	if (ospeed < 1200)
117 		screenheight = 9;
118 	else if (ospeed == 1200)
119 		screenheight = 14;
120 	else if (ws.ws_row != 0)
121 		screenheight = ws.ws_row;
122 	else
123 		screenheight = 24;
124 	if ((realscreenheight = ws.ws_row) == 0)
125 		realscreenheight = 24;
126 	if ((screenwidth = ws.ws_col) == 0)
127 		screenwidth = 80;
128 	/*
129 	 * Possible overrides from the rcfile.
130 	 */
131 	if ((cp = value(ENAME_SCREENWIDTH)) != NULL) {
132 		int width;
133 		width = *cp ? atoi(cp) : 0;
134 		if (width >= 0)
135 			screenwidth = width;
136 	}
137 	if ((cp = value(ENAME_SCREENHEIGHT)) != NULL) {
138 		int height;
139 		height = *cp ? atoi(cp) : 0;
140 		if (height >= 0) {
141 			realscreenheight = height;
142 			screenheight = height;
143 		}
144 	}
145 }
146 
147 /*
148  * Break up a white-space or comma delimited name list so that aliases
149  * can get expanded.  Without this, the CC: or BCC: list is broken too
150  * late for alias expansion to occur.
151  */
152 PUBLIC struct name *
153 lexpand(char *str, int ntype)
154 {
155 	char *list;
156 	struct name *np = NULL;
157 	char *word, *p;
158 
159 	list = estrdup(str);
160 	word = list;
161 	for (word = list; *word; word = p) {
162 		word = skip_WSP(word);
163 		for (p = word;
164 		     *p && !is_WSP(*p) && *p != ',';
165 		     p++)
166 			continue;
167 		if (*p)
168 			*p++ = '\0';
169 		np = cat(np, nalloc(word, ntype));
170 	}
171 
172 	free(list);
173 	return np;
174 }
175 
176 PUBLIC int
177 main(int argc, char *argv[])
178 {
179 	jmp_buf jmpbuf;
180 	struct sigaction sa;
181 	struct name *to, *cc, *bcc, *smopts;
182 #ifdef MIME_SUPPORT
183 	struct name *attach_optargs;
184 	struct name *attach_end;
185 #endif
186 	char *subject;
187 	const char *ef;
188 	char nosrc = 0;
189 	const char *rc;
190 	int Hflag;
191 	int i;
192 
193 	/*
194 	 * For portability, call setprogname() early, before
195 	 * getprogname() is called.
196 	 */
197 	(void)setprogname(argv[0]);
198 
199 	/*
200 	 * Set up a reasonable environment.
201 	 * Figure out whether we are being run interactively,
202 	 * start the SIGCHLD catcher, and so forth.
203 	 * (Other signals are setup later by sig_setup().)
204 	 */
205 	(void)sigemptyset(&sa.sa_mask);
206 	sa.sa_flags = SA_RESTART;
207 	sa.sa_handler = sigchild;
208 	(void)sigaction(SIGCHLD, &sa, NULL);
209 
210 	if (isatty(0))
211 		assign(ENAME_INTERACTIVE, "");
212 	image = -1;
213 
214 	/*
215 	 * Now, determine how we are being used.
216 	 * We successively pick off - flags.
217 	 * If there is anything left, it is the base of the list
218 	 * of users to mail to.  Argp will be set to point to the
219 	 * first of these users.
220 	 */
221 	rc = NULL;
222 	ef = NULL;
223 	to = NULL;
224 	cc = NULL;
225 	bcc = NULL;
226 	smopts = NULL;
227 	subject = NULL;
228 	Hflag = 0;
229 #ifdef MIME_SUPPORT
230 	attach_optargs = NULL;
231 	attach_end = NULL;
232 	while ((i = getopt(argc, argv, ":~EH:INT:a:b:c:dfinr:s:u:v")) != -1)
233 #else
234 	while ((i = getopt(argc, argv, ":~EH:INT:b:c:dfinr:s:u:v")) != -1)
235 #endif
236 	{
237 		switch (i) {
238 		case 'T':
239 			/*
240 			 * Next argument is temp file to write which
241 			 * articles have been read/deleted for netnews.
242 			 */
243 			Tflag = optarg;
244 			if ((i = creat(Tflag, 0600)) < 0) {
245 				warn("%s", Tflag);
246 				exit(1);
247 			}
248 			(void)close(i);
249 			break;
250 #ifdef MIME_SUPPORT
251 		case 'a': {
252 			struct name *np;
253 			np = nalloc(optarg, 0);
254 			if (attach_end == NULL)
255 				attach_optargs = np;
256 			else {
257 				np->n_blink = attach_end;
258 				attach_end->n_flink = np;
259 			}
260 			attach_end = np;
261 			break;
262 		}
263 #endif
264 		case 'u':
265 			/*
266 			 * Next argument is person to pretend to be.
267 			 */
268 			myname = optarg;
269 			(void)unsetenv("MAIL");
270 			break;
271 		case 'i':
272 			/*
273 			 * User wants to ignore interrupts.
274 			 * Set the variable "ignore"
275 			 */
276 			assign(ENAME_IGNORE, "");
277 			break;
278 		case 'd':
279 			debug++;
280 			break;
281 		case 'r':
282 			rc = optarg;
283 			break;
284 		case 's':
285 			/*
286 			 * Give a subject field for sending from
287 			 * non terminal
288 			 */
289 			subject = optarg;
290 			break;
291 		case 'f':
292 			/*
293 			 * User is specifying file to "edit" with Mail,
294 			 * as opposed to reading system mailbox.
295 			 * If no argument is given after -f, we read his
296 			 * mbox file.
297 			 *
298 			 * getopt() can't handle optional arguments, so here
299 			 * is an ugly hack to get around it.
300 			 */
301 			if ((argv[optind]) && (argv[optind][0] != '-'))
302 				ef = argv[optind++];
303 			else
304 				ef = "&";
305 			break;
306 		case 'H':
307 			/*
308 			 * Print out the headers and quit.
309 			 */
310 			Hflag = get_Hflag(argv);
311 			break;
312 		case 'n':
313 			/*
314 			 * User doesn't want to source /usr/lib/Mail.rc
315 			 */
316 			nosrc++;
317 			break;
318 		case 'N':
319 			/*
320 			 * Avoid initial header printing.
321 			 */
322 			assign(ENAME_NOHEADER, "");
323 			break;
324 		case 'v':
325 			/*
326 			 * Send mailer verbose flag
327 			 */
328 			assign(ENAME_VERBOSE, "");
329 			break;
330 		case 'I':
331 		case '~':
332 			/*
333 			 * We're interactive
334 			 */
335 			assign(ENAME_INTERACTIVE, "");
336 			break;
337 		case 'c':
338 			/*
339 			 * Get Carbon Copy Recipient list
340 			 */
341 			cc = cat(cc, lexpand(optarg, GCC));
342 			break;
343 		case 'b':
344 			/*
345 			 * Get Blind Carbon Copy Recipient list
346 			 */
347 			bcc = cat(bcc, lexpand(optarg, GBCC));
348 
349 			break;
350 		case 'E':
351 			/*
352 			 * Don't send empty files.
353 			 */
354 			assign(ENAME_DONTSENDEMPTY, "");
355 			break;
356 		case ':':
357 			/*
358 			 * An optarg was expected but not found.
359 			 */
360 			if (optopt == 'H') {
361 				Hflag = get_Hflag(NULL);
362 				break;
363 			}
364 			(void)fprintf(stderr,
365 			    "%s: option requires an argument -- %c\n",
366 			    getprogname(), optopt);
367 
368 			/* FALLTHROUGH */
369 		case '?':
370 			/*
371 			 * An unknown option flag.  We need to do the
372 			 * error message.
373 			 */
374 			if (optopt != '?')
375 				(void)fprintf(stderr,
376 				    "%s: unknown option -- %c\n", getprogname(),
377 				    optopt);
378 			usage();	/* print usage message and die */
379 			/*NOTREACHED*/
380 		}
381 	}
382 	for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
383 		to = cat(to, nalloc(argv[i], GTO));
384 	for (/*EMPTY*/; argv[i]; i++)
385 		smopts = cat(smopts, nalloc(argv[i], GSMOPTS));
386 	/*
387 	 * Check for inconsistent arguments.
388 	 */
389 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
390 		errx(EXIT_FAILURE, "You must specify direct recipients with -s, -c, or -b.");
391 	if (ef != NULL && to != NULL) {
392 		errx(EXIT_FAILURE, "Cannot give -f and people to send to.");
393 	}
394 	if (Hflag != 0 && to != NULL)
395 		errx(EXIT_FAILURE, "Cannot give -H and people to send to.");
396 #ifdef MIME_SUPPORT
397 	if (attach_optargs != NULL && to == NULL)
398 		errx(EXIT_FAILURE, "Cannot give -a without people to send to.");
399 #endif
400 	tinit();	/* must be done before loading the rcfile */
401 	input = stdin;
402 	mailmode = Hflag ? mm_hdrsonly :
403 	    to ? mm_sending : mm_receiving;
404 
405 	spreserve();
406 	if (!nosrc)
407 		load(_PATH_MASTER_RC);
408 	/*
409 	 * Expand returns a savestr, but load only uses the file name
410 	 * for fopen, so it's safe to do this.
411 	 */
412 	if (rc == NULL && (rc = getenv("MAILRC")) == NULL)
413 		rc = "~/.mailrc";
414 	load(expand(rc));
415 	setscreensize();	/* do this after loading the rcfile */
416 
417 #ifdef USE_EDITLINE
418 	/*
419 	 * This is after loading the MAILRC so we can use value().
420 	 * Avoid editline in mm_hdrsonly mode or pipelines will screw
421 	 * up.  XXX - there must be a better way!
422 	 */
423 	if (mailmode != mm_hdrsonly)
424 		init_editline();
425 #endif
426 
427 	sig_setup();
428 
429 	switch (mailmode) {
430 	case mm_sending:
431 		(void)mail(to, cc, bcc, smopts, subject,
432 		    mime_attach_optargs(attach_optargs));
433 		/*
434 		 * why wait?
435 		 */
436 		exit(senderr);
437 		break;	/* XXX - keep lint happy */
438 
439 	case mm_receiving:
440 	case mm_hdrsonly:
441 		/*
442 		 * Ok, we are reading mail.
443 		 * Decide whether we are editing a mailbox or reading
444 		 * the system mailbox, and open up the right stuff.
445 		 */
446 		if (ef == NULL)
447 			ef = "%";
448 		if (setfile(ef) < 0)
449 			exit(1);		/* error already reported */
450 		if (value(ENAME_QUIET) == NULL)
451 			(void)printf("Mail version %s.  Type ? for help.\n",
452 			    version);
453 		if (mailmode == mm_hdrsonly)
454 			show_headers_and_exit(Hflag);	/* NORETURN */
455 		announce();
456 		(void)fflush(stdout);
457 
458 		if (setjmp(jmpbuf) != 0) {
459 			/* Return here if quit() fails below. */
460 			(void)printf("Use 'exit' to quit without saving changes.\n");
461 		}
462 		commands();
463 
464 		/* Ignore these signals from now on! */
465 		(void)signal(SIGHUP, SIG_IGN);
466 		(void)signal(SIGINT, SIG_IGN);
467 		(void)signal(SIGQUIT, SIG_IGN);
468 		quit(jmpbuf);
469 		break;
470 
471 	default:
472 		assert(/*CONSTCOND*/0);
473 		break;
474 	}
475 
476 	return 0;
477 }
478