1 /*
2  * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3  *
4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5  */
6 /*
7  * Copyright (c) 1980, 1993
8  *	The Regents of the University of California.  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #ifdef	DOSCCS
41 static char copyright[]
42 = "@(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.\n";
43 static char sccsid[] = "@(#)main.c	2.51 (gritter) 10/1/07";
44 #endif	/* DOSCCS */
45 #endif /* not lint */
46 
47 /*
48  * Most strcpy/sprintf functions have been changed to strncpy/snprintf to
49  * correct several buffer overruns (at least one ot them was exploitable).
50  * Sat Jun 20 04:58:09 CEST 1998 Alvaro Martinez Echevarria <alvaro@lander.es>
51  * ---
52  * Note: We set egid to realgid ... and only if we need the egid we will
53  *       switch back temporary.  Nevertheless, I do not like seg faults.
54  *       Werner Fink, <werner@suse.de>
55  */
56 
57 
58 #include "config.h"
59 #ifdef	HAVE_NL_LANGINFO
60 #include <langinfo.h>
61 #endif	/* HAVE_NL_LANGINFO */
62 #define _MAIL_GLOBS_
63 #include "rcv.h"
64 #include "extern.h"
65 #include <sys/stat.h>
66 #include <sys/ioctl.h>
67 #include <termios.h>
68 #include <unistd.h>
69 #include <fcntl.h>
70 #ifdef	HAVE_SETLOCALE
71 #include <locale.h>
72 #endif	/* HAVE_SETLOCALE */
73 
74 /*
75  * Mail -- a mail program
76  *
77  * Startup -- interface with user.
78  */
79 
80 static sigjmp_buf	hdrjmp;
81 char	*progname;
82 sighandler_type	dflpipe = SIG_DFL;
83 
84 static void hdrstop(int signo);
85 static void setscreensize(int dummy);
86 
87 int
main(int argc,char * argv[])88 main(int argc, char *argv[])
89 {
90 	const char optstr[] = "A:BHEFINVT:RS:a:b:c:dDefh:inqr:s:tu:v~";
91 	int i, existonly = 0, headersonly = 0, sendflag = 0;
92 	struct name *to, *cc, *bcc, *smopts;
93 	struct attachment *attach;
94 	char *subject, *cp, *ef, *qf = NULL, *fromaddr = NULL, *Aflag = NULL;
95 	char nosrc = 0;
96 	int Eflag = 0, Fflag = 0, Nflag = 0, tflag = 0;
97 	sighandler_type prevint;
98 
99 	(void)&Nflag;
100 	/*
101 	 * Absolutely the first thing we do is save our egid
102 	 * and set it to the rgid, so that we can safely run
103 	 * setgid.  We use the sgid (saved set-gid) to allow ourselves
104 	 * to revert to the egid if we want (temporarily) to become
105 	 * priveliged.
106 	 */
107 	effectivegid = getegid();
108 	realgid = getgid();
109 	if (setgid(realgid) < 0) {
110 		perror("setgid");
111 		exit(1);
112 	}
113 
114 	starting = 1;
115 	progname = strrchr(argv[0], '/');
116 	if (progname != NULL)
117 		progname++;
118 	else
119 		progname = argv[0];
120 	/*
121 	 * Set up a reasonable environment.
122 	 * Figure out whether we are being run interactively,
123 	 * start the SIGCHLD catcher, and so forth.
124 	 */
125 	safe_signal(SIGCHLD, sigchild);
126 	is_a_tty[0] = isatty(0);
127 	is_a_tty[1] = isatty(1);
128 	if (is_a_tty[0]) {
129 		assign("interactive", "");
130 		if (is_a_tty[1])
131 			safe_signal(SIGPIPE, dflpipe = SIG_IGN);
132 	}
133 	assign("header", "");
134 	assign("save", "");
135 #ifdef	HAVE_SETLOCALE
136 	setlocale(LC_CTYPE, "");
137 	setlocale(LC_COLLATE, "");
138 	setlocale(LC_MESSAGES, "");
139 	mb_cur_max = MB_CUR_MAX;
140 #if defined (HAVE_NL_LANGINFO) && defined (CODESET)
141 	if (value("ttycharset") == NULL && (cp = nl_langinfo(CODESET)) != NULL)
142 		assign("ttycharset", cp);
143 #endif	/* HAVE_NL_LANGINFO && CODESET */
144 #if defined (HAVE_MBTOWC) && defined (HAVE_WCTYPE_H)
145 	if (mb_cur_max > 1) {
146 		wchar_t	wc;
147 		if (mbtowc(&wc, "\303\266", 2) == 2 && wc == 0xF6 &&
148 				mbtowc(&wc, "\342\202\254", 3) == 3 &&
149 				wc == 0x20AC)
150 			utf8 = 1;
151 	}
152 #endif	/* HAVE_MBTOWC && HAVE_WCTYPE_H */
153 #else	/* !HAVE_SETLOCALE */
154 	mb_cur_max = 1;
155 #endif	/* !HAVE_SETLOCALE */
156 #ifdef	HAVE_CATGETS
157 #ifdef	NL_CAT_LOCALE
158 	i = NL_CAT_LOCALE;
159 #else
160 	i = 0;
161 #endif
162 	catd = catopen(CATNAME, i);
163 #endif	/* HAVE_CATGETS */
164 #ifdef	HAVE_ICONV
165 	iconvd = (iconv_t) -1;
166 #endif
167 	image = -1;
168 	/*
169 	 * Now, determine how we are being used.
170 	 * We successively pick off - flags.
171 	 * If there is anything left, it is the base of the list
172 	 * of users to mail to.  Argp will be set to point to the
173 	 * first of these users.
174 	 */
175 	ef = NULL;
176 	to = NULL;
177 	cc = NULL;
178 	bcc = NULL;
179 	attach = NULL;
180 	smopts = NULL;
181 	subject = NULL;
182 	while ((i = getopt(argc, argv, optstr)) != EOF) {
183 		switch (i) {
184 		case 'V':
185 			puts(version);
186 			exit(0);
187 			/*NOTREACHED*/
188 		case 'B':
189 			setvbuf(stdin, NULL, _IOLBF, 0);
190 			setvbuf(stdout, NULL, _IOLBF, 0);
191 			break;
192 		case 'H':
193 			headersonly = 1;
194 			break;
195 		case 'E':
196 			Eflag = 1;
197 			break;
198 		case 'F':
199 			Fflag = 1;
200 			sendflag++;
201 			break;
202 		case 'S': {
203 				char *args[] = { NULL, NULL };
204 				args[0] = optarg;
205 				set(args);
206 			}
207 			break;
208 		case 'T':
209 			/*
210 			 * Next argument is temp file to write which
211 			 * articles have been read/deleted for netnews.
212 			 */
213 			Tflag = optarg;
214 			if ((i = creat(Tflag, 0600)) < 0) {
215 				perror(Tflag);
216 				exit(1);
217 			}
218 			close(i);
219 			/*FALLTHRU*/
220 		case 'I':
221 			/*
222 			 * Show Newsgroups: field in header summary
223 			 */
224 			Iflag = 1;
225 			break;
226 		case 'u':
227 			/*
228 			 * Next argument is person to pretend to be.
229 			 */
230 			uflag = myname = optarg;
231 			break;
232 		case 'i':
233 			/*
234 			 * User wants to ignore interrupts.
235 			 * Set the variable "ignore"
236 			 */
237 			assign("ignore", "");
238 			break;
239 		case 'd':
240 			debug++;
241 			break;
242 		case 'D':
243 			assign("disconnected", "");
244 			break;
245 		case 'e':
246 			existonly++;
247 			break;
248 		case 's':
249 			/*
250 			 * Give a subject field for sending from
251 			 * non terminal
252 			 */
253 			subject = optarg;
254 			sendflag++;
255 			break;
256 		case 'f':
257 			/*
258 			 * User is specifying file to "edit" with Mail,
259 			 * as opposed to reading system mailbox.
260 			 * If no argument is given, we read his
261 			 * mbox file.
262 			 *
263 			 * Check for remaining arguments later.
264 			 */
265 			ef = "&";
266 			break;
267 		case 'q':
268 			/*
269 			 * User is specifying file to quote in front of
270 			 * the mail to be collected.
271 			 */
272 			if ((argv[optind]) && (argv[optind][0] != '-'))
273 				qf = argv[optind++];
274 			else
275 				qf = NULL;
276 			sendflag++;
277 			break;
278 		case 'n':
279 			/*
280 			 * User doesn't want to source /usr/lib/Mail.rc
281 			 */
282 			nosrc++;
283 			break;
284 		case 'N':
285 			/*
286 			 * Avoid initial header printing.
287 			 */
288 			Nflag = 1;
289 			unset_internal("header");
290 			break;
291 		case 'v':
292 			/*
293 			 * Send mailer verbose flag
294 			 */
295 			assign("verbose", "");
296 			break;
297 		case 'r':
298 			/*
299 			 * Set From address.
300 			 */
301 			fromaddr = optarg;
302 			smopts = cat(smopts, nalloc("-r", 0));
303 			smopts = cat(smopts, nalloc(optarg, 0));
304 			tildeflag = -1;
305 			sendflag++;
306 			break;
307 		case 'a':
308 			/*
309 			 * Get attachment filenames
310 			 */
311 			if ((attach = add_attachment(attach, optarg)) == NULL) {
312 				perror(optarg);
313 				exit(1);
314 			}
315 			sendflag++;
316 			break;
317 		case 'c':
318 			/*
319 			 * Get Carbon Copy Recipient list
320 			 */
321 			cc = checkaddrs(cat(cc, extract(optarg, GCC|GFULL)));
322 			sendflag++;
323 			break;
324 		case 'b':
325 			/*
326 			 * Get Blind Carbon Copy Recipient list
327 			 */
328 			bcc = checkaddrs(cat(bcc, extract(optarg, GBCC|GFULL)));
329 			sendflag++;
330 			break;
331 		case 'h':
332 			/*
333 			 * Hop count for sendmail
334 			 */
335 			smopts = cat(smopts, nalloc("-h", 0));
336 			smopts = cat(smopts, nalloc(optarg, 0));
337 			sendflag++;
338 			break;
339 		case '~':
340 			if (tildeflag == 0)
341 				tildeflag = 1;
342 			break;
343 		case 't':
344 			sendflag = 1;
345 			tflag = 1;
346 			break;
347 		case 'A':
348 			Aflag = optarg;
349 			break;
350 		case 'R':
351 			Rflag = 1;
352 			break;
353 		case '?':
354 usage:
355 			fprintf(stderr, catgets(catd, CATSET, 135,
356 "Usage: %s -eiIUdEFntBDNHRV~ -T FILE -u USER -h hops -r address -s SUBJECT -a FILE -q FILE -f FILE -A ACCOUNT -b USERS -c USERS -S OPTION users\n"), progname);
357 			exit(2);
358 		}
359 	}
360 	if (ef != NULL) {
361 		if (optind < argc) {
362 			if (optind + 1 < argc) {
363 				fprintf(stderr, catgets(catd, CATSET, 205,
364 					"More than one file given with -f\n"));
365 				goto usage;
366 			}
367 			ef = argv[optind];
368 		}
369 	} else {
370 		for (i = optind; argv[i]; i++)
371 			to = checkaddrs(cat(to, extract(argv[i], GTO|GFULL)));
372 	}
373 	/*
374 	 * Check for inconsistent arguments.
375 	 */
376 	if (ef != NULL && to != NULL) {
377 		fprintf(stderr, catgets(catd, CATSET, 137,
378 			"Cannot give -f and people to send to.\n"));
379 		goto usage;
380 	}
381 	if (sendflag && !tflag && to == NULL) {
382 		fprintf(stderr, catgets(catd, CATSET, 138,
383 			"Send options without primary recipient specified.\n"));
384 		goto usage;
385 	}
386 	if (Rflag && to != NULL) {
387 		fprintf(stderr, "The -R option is meaningless in send mode.\n");
388 		goto usage;
389 	}
390 	if (Iflag && ef == NULL) {
391 		fprintf(stderr, catgets(catd, CATSET, 204,
392 					"Need -f with -I.\n"));
393 		goto usage;
394 	}
395 	tinit();
396 	setscreensize(0);
397 #ifdef	SIGWINCH
398 	if (value("interactive"))
399 		if (safe_signal(SIGWINCH, SIG_IGN) != SIG_IGN)
400 			safe_signal(SIGWINCH, setscreensize);
401 #endif	/* SIGWINCH */
402 	input = stdin;
403 	rcvmode = !to && !tflag;
404 	spreserve();
405 	if (!nosrc)
406 		load(MAILRC);
407 	/*
408 	 * Expand returns a savestr, but load only uses the file name
409 	 * for fopen, so it's safe to do this.
410 	 */
411 	if ((cp = getenv("MAILRC")) != NULL)
412 		load(expand(cp));
413 	else if ((cp = getenv("NAILRC")) != NULL)
414 		load(expand(cp));
415 	else
416 		load(expand("~/.mailrc"));
417 	if (getenv("NAIL_EXTRA_RC") == NULL &&
418 			(cp = value("NAIL_EXTRA_RC")) != NULL)
419 		load(expand(cp));
420 	/*
421 	 * Now we can set the account.
422 	 */
423 	if (Aflag) {
424 		char	*a[2];
425 		a[0] = Aflag;
426 		a[1] = NULL;
427 		account(a);
428 	}
429 
430 	/*
431 	 * Override 'skipemptybody' if '-E' flag was given.
432 	 */
433 	if (Eflag)
434 		assign("skipemptybody", "");
435 
436 	starting = 0;
437 
438 	/*
439 	 * From address from command line overrides rc files.
440 	 */
441 	if (fromaddr)
442 		assign("from", fromaddr);
443 	if (!rcvmode) {
444 		mail(to, cc, bcc, smopts, subject, attach, qf, Fflag, tflag,
445 		    Eflag);
446 		/*
447 		 * why wait?
448 		 */
449 		exit(senderr ? 1 : 0);
450 	}
451 	/*
452 	 * Ok, we are reading mail.
453 	 * Decide whether we are editing a mailbox or reading
454 	 * the system mailbox, and open up the right stuff.
455 	 */
456 	if (ef == NULL)
457 		ef = "%";
458 	else if (*ef == '@') {
459 		/*
460 		 * This must be treated specially to make invocation like
461 		 * -A imap -f @mailbox work.
462 		 */
463 		if ((cp = value("folder")) != NULL &&
464 				which_protocol(cp) == PROTO_IMAP)
465 			strncpy(mailname, cp, PATHSIZE)[PATHSIZE-1] = '\0';
466 	}
467 	i = setfile(ef, 0);
468 	if (i < 0)
469 		exit(1);		/* error already reported */
470 	if (existonly)
471 		exit(i);
472 	if (headersonly) {
473 		if (mb.mb_type == MB_IMAP)
474 			imap_getheaders(1, msgCount);
475 		for (i = 1; i <= msgCount; i++)
476 			printhead(i, stdout, 0);
477 		exit(exit_status);
478 	}
479 	callhook(mailname, 0);
480 	if (i > 0 && value("emptystart") == NULL)
481 		exit(1);
482 	if (sigsetjmp(hdrjmp, 1) == 0) {
483 		if ((prevint = safe_signal(SIGINT, SIG_IGN)) != SIG_IGN)
484 			safe_signal(SIGINT, hdrstop);
485 		if (Nflag == 0) {
486 			if (value("quiet") == NULL)
487 				printf(catgets(catd, CATSET, 140,
488 					"Heirloom %s version %s.  "
489 					"Type ? for help.\n"),
490 					value("bsdcompat") ? "Mail" : "mailx",
491 					version);
492 			announce(1);
493 			fflush(stdout);
494 		}
495 		safe_signal(SIGINT, prevint);
496 	}
497 	commands();
498 	if (mb.mb_type == MB_FILE || mb.mb_type == MB_MAILDIR) {
499 		safe_signal(SIGHUP, SIG_IGN);
500 		safe_signal(SIGINT, SIG_IGN);
501 		safe_signal(SIGQUIT, SIG_IGN);
502 	}
503 	strncpy(mboxname, expand("&"), sizeof mboxname)[sizeof mboxname-1]='\0';
504 	quit();
505 	return exit_status;
506 }
507 
508 /*
509  * Interrupt printing of the headers.
510  */
511 /*ARGSUSED*/
512 static void
hdrstop(int signo)513 hdrstop(int signo)
514 {
515 
516 	fflush(stdout);
517 	fprintf(stderr, catgets(catd, CATSET, 141, "\nInterrupt\n"));
518 	siglongjmp(hdrjmp, 1);
519 }
520 
521 /*
522  * Compute what the screen size for printing headers should be.
523  * We use the following algorithm for the height:
524  *	If baud rate < 1200, use  9
525  *	If baud rate = 1200, use 14
526  *	If baud rate > 1200, use 24 or ws_row
527  * Width is either 80 or ws_col;
528  */
529 /*ARGSUSED*/
530 static void
setscreensize(int dummy)531 setscreensize(int dummy)
532 {
533 	struct termios tbuf;
534 #ifdef	TIOCGWINSZ
535 	struct winsize ws;
536 #endif
537 	speed_t ospeed;
538 
539 #ifdef	TIOCGWINSZ
540 	if (ioctl(1, TIOCGWINSZ, &ws) < 0)
541 		ws.ws_col = ws.ws_row = 0;
542 #endif
543 	if (tcgetattr(1, &tbuf) < 0)
544 		ospeed = B9600;
545 	else
546 		ospeed = cfgetospeed(&tbuf);
547 	if (ospeed < B1200)
548 		scrnheight = 9;
549 	else if (ospeed == B1200)
550 		scrnheight = 14;
551 #ifdef	TIOCGWINSZ
552 	else if (ws.ws_row != 0)
553 		scrnheight = ws.ws_row;
554 #endif
555 	else
556 		scrnheight = 24;
557 #ifdef	TIOCGWINSZ
558 	if ((realscreenheight = ws.ws_row) == 0)
559 		realscreenheight = 24;
560 #endif
561 #ifdef	TIOCGWINSZ
562 	if ((scrnwidth = ws.ws_col) == 0)
563 #endif
564 		scrnwidth = 80;
565 }
566