xref: /netbsd-src/usr.bin/mail/main.c (revision 0920b4f20b78ab1ccd9f2312fbe10deaf000cbf3)
1 /*	$NetBSD: main.c,v 1.27 2007/06/13 19:39:54 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.27 2007/06/13 19:39:54 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 		while (isblank((unsigned char)*word))
155 			continue;
156 		for (p = word;
157 		     *p && !isblank((unsigned char)*p) && *p != ',';
158 		     p++)
159 			continue;
160 		if (*p)
161 			*p++ = '\0';
162 		np = cat(np, nalloc(word, ntype));
163 	}
164 
165 	free(list);
166 	return np;
167 }
168 
169 PUBLIC int
170 main(int argc, char *argv[])
171 {
172 	int i;
173 	struct name *to, *cc, *bcc, *smopts;
174 #ifdef MIME_SUPPORT
175 	struct name *attach_optargs;
176 	struct name *attach_end;
177 #endif
178 	char *subject;
179 	const char *ef;
180 	char nosrc = 0;
181 	sig_t prevint;
182 	const char *rc;
183 	int volatile Hflag;
184 
185 	/*
186 	 * For portability, call setprogname() early, before
187 	 * getprogname() is called.
188 	 */
189 	(void)setprogname(argv[0]);
190 
191 	/*
192 	 * Set up a reasonable environment.
193 	 * Figure out whether we are being run interactively,
194 	 * start the SIGCHLD catcher, and so forth.
195 	 */
196 	(void)signal(SIGCHLD, sigchild);
197 	if (isatty(0))
198 		assign(ENAME_INTERACTIVE, "");
199 	image = -1;
200 	/*
201 	 * Now, determine how we are being used.
202 	 * We successively pick off - flags.
203 	 * If there is anything left, it is the base of the list
204 	 * of users to mail to.  Argp will be set to point to the
205 	 * first of these users.
206 	 */
207 	ef = NULL;
208 	to = NULL;
209 	cc = NULL;
210 	bcc = NULL;
211 	smopts = NULL;
212 	subject = NULL;
213 	Hflag = 0;
214 #ifdef MIME_SUPPORT
215 	attach_optargs = NULL;
216 	attach_end = NULL;
217 	while ((i = getopt(argc, argv, ":~EH:INT:a:b:c:dfins:u:v")) != -1)
218 #else
219 	while ((i = getopt(argc, argv, ":~EH:INT:b:c:dfins:u:v")) != -1)
220 #endif
221 	{
222 		switch (i) {
223 		case 'T':
224 			/*
225 			 * Next argument is temp file to write which
226 			 * articles have been read/deleted for netnews.
227 			 */
228 			Tflag = optarg;
229 			if ((i = creat(Tflag, 0600)) < 0) {
230 				warn("%s", Tflag);
231 				exit(1);
232 			}
233 			(void)close(i);
234 			break;
235 #ifdef MIME_SUPPORT
236 		case 'a': {
237 			struct name *np;
238 			np = nalloc(optarg, 0);
239 			if (attach_end == NULL)
240 				attach_optargs = np;
241 			else {
242 				np->n_blink = attach_end;
243 				attach_end->n_flink = np;
244 			}
245 			attach_end = np;
246 			break;
247 		}
248 #endif
249 		case 'u':
250 			/*
251 			 * Next argument is person to pretend to be.
252 			 */
253 			myname = optarg;
254 			(void)unsetenv("MAIL");
255 			break;
256 		case 'i':
257 			/*
258 			 * User wants to ignore interrupts.
259 			 * Set the variable "ignore"
260 			 */
261 			assign(ENAME_IGNORE, "");
262 			break;
263 		case 'd':
264 			debug++;
265 			break;
266 		case 's':
267 			/*
268 			 * Give a subject field for sending from
269 			 * non terminal
270 			 */
271 			subject = optarg;
272 			break;
273 		case 'f':
274 			/*
275 			 * User is specifying file to "edit" with Mail,
276 			 * as opposed to reading system mailbox.
277 			 * If no argument is given after -f, we read his
278 			 * mbox file.
279 			 *
280 			 * getopt() can't handle optional arguments, so here
281 			 * is an ugly hack to get around it.
282 			 */
283 			if ((argv[optind]) && (argv[optind][0] != '-'))
284 				ef = argv[optind++];
285 			else
286 				ef = "&";
287 			break;
288 		case 'H':
289 			/*
290 			 * Print out the headers and quit.
291 			 */
292 			Hflag = get_Hflag(argv);
293 			break;
294 		case 'n':
295 			/*
296 			 * User doesn't want to source /usr/lib/Mail.rc
297 			 */
298 			nosrc++;
299 			break;
300 		case 'N':
301 			/*
302 			 * Avoid initial header printing.
303 			 */
304 			assign(ENAME_NOHEADER, "");
305 			break;
306 		case 'v':
307 			/*
308 			 * Send mailer verbose flag
309 			 */
310 			assign(ENAME_VERBOSE, "");
311 			break;
312 		case 'I':
313 		case '~':
314 			/*
315 			 * We're interactive
316 			 */
317 			assign(ENAME_INTERACTIVE, "");
318 			break;
319 		case 'c':
320 			/*
321 			 * Get Carbon Copy Recipient list
322 			 */
323 			cc = cat(cc, lexpand(optarg, GCC));
324 			break;
325 		case 'b':
326 			/*
327 			 * Get Blind Carbon Copy Recipient list
328 			 */
329 			bcc = cat(bcc, lexpand(optarg, GBCC));
330 
331 			break;
332 		case 'E':
333 			/*
334 			 * Don't send empty files.
335 			 */
336 			assign(ENAME_DONTSENDEMPTY, "");
337 			break;
338 		case ':':
339 			/*
340 			 * An optarg was expected but not found.
341 			 */
342 			if (optopt == 'H') {
343 				Hflag = get_Hflag(NULL);
344 				break;
345 			}
346 			(void)fprintf(stderr,
347 			    "%s: option requires an argument -- %c\n",
348 			    getprogname(), optopt);
349 
350 			/* FALLTHROUGH */
351 		case '?':
352 			/*
353 			 * An unknown option flag.  We need to do the
354 			 * error message.
355 			 */
356 			if (optopt != '?')
357 				(void)fprintf(stderr,
358 				    "%s: unknown option -- %c\n", getprogname(),
359 				    optopt);
360 #ifdef MIME_SUPPORT
361 			(void)fputs("\
362 Usage: mail [-EiInv] [-s subject] [-a file] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
363             [- sendmail-options ...]\n\
364        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
365        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
366 				stderr);
367 #else /* MIME_SUPPORT */
368 			(void)fputs("\
369 Usage: mail [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
370             [- sendmail-options ...]\n\
371        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
372        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
373 				stderr);
374 #endif /* MIME_SUPPORT */
375 
376 			exit(1);
377 		}
378 	}
379 	for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
380 		to = cat(to, nalloc(argv[i], GTO));
381 	for (; argv[i]; i++)
382 		smopts = cat(smopts, nalloc(argv[i], GSMOPTS));
383 	/*
384 	 * Check for inconsistent arguments.
385 	 */
386 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
387 		errx(1, "You must specify direct recipients with -s, -c, or -b.");
388 	if (ef != NULL && to != NULL) {
389 		errx(1, "Cannot give -f and people to send to.");
390 	}
391 	if (Hflag != 0 && to != NULL)
392 		errx(EXIT_FAILURE, "Cannot give -H and people to send to.");
393 #ifdef MIME_SUPPORT
394 	if (attach_optargs != NULL && to == NULL)
395 		errx(EXIT_FAILURE, "Cannot give -a without people to send to.");
396 #endif
397 	tinit();	/* must be done before loading the rcfile */
398 	input = stdin;
399 	mailmode = Hflag ? mm_hdrsonly :
400 	    to ? mm_sending : mm_receiving;
401 
402 	spreserve();
403 	if (!nosrc)
404 		load(_PATH_MASTER_RC);
405 	/*
406 	 * Expand returns a savestr, but load only uses the file name
407 	 * for fopen, so it's safe to do this.
408 	 */
409 	if ((rc = getenv("MAILRC")) == 0)
410 		rc = "~/.mailrc";
411 	load(expand(rc));
412 	setscreensize();	/* do this after loading the rcfile */
413 
414 #ifdef USE_EDITLINE
415 	/*
416 	 * This is after loading the MAILRC so we can use value().
417 	 * Avoid editline in mm_hdrsonly mode or pipelines will screw
418 	 * up.  XXX - there must be a better way!
419 	 */
420 	if (mailmode != mm_hdrsonly)
421 		init_editline();
422 #endif
423 
424 	switch (mailmode) {
425 	case mm_sending:
426 		(void)mail(to, cc, bcc, smopts, subject,
427 		    mime_attach_optargs(attach_optargs));
428 		/*
429 		 * why wait?
430 		 */
431 		exit(senderr);
432 		break;	/* XXX - keep lint happy */
433 
434 	case mm_receiving:
435 	case mm_hdrsonly:
436 		/*
437 		 * Ok, we are reading mail.
438 		 * Decide whether we are editing a mailbox or reading
439 		 * the system mailbox, and open up the right stuff.
440 		 */
441 		if (ef == NULL)
442 			ef = "%";
443 		if (setfile(ef) < 0)
444 			exit(1);		/* error already reported */
445 		if (setjmp(hdrjmp) == 0) {
446 			if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
447 				(void)signal(SIGINT, hdrstop);
448 			if (value(ENAME_QUIET) == NULL)
449 				(void)printf("Mail version %s.  Type ? for help.\n",
450 				    version);
451 			if (mailmode == mm_hdrsonly)
452 				show_headers_and_exit(Hflag);	/* NORETURN */
453 			announce();
454 			(void)fflush(stdout);
455 			(void)signal(SIGINT, prevint);
456 		}
457 		commands();
458 		(void)signal(SIGHUP, SIG_IGN);
459 		(void)signal(SIGINT, SIG_IGN);
460 		(void)signal(SIGQUIT, SIG_IGN);
461 		quit();
462 		break;
463 
464 	default:
465 		assert(/*CONSTCOND*/0);
466 		break;
467 	}
468 
469 	return 0;
470 }
471