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