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