xref: /illumos-gate/usr/src/cmd/mailx/main.c (revision 30f1f754a88aa357235064d8c7ce78f2f546e0aa)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  * University Copyright- Copyright (c) 1982, 1986, 1988
33  * The Regents of the University of California
34  * All Rights Reserved
35  *
36  * University Acknowledgment- Portions of this document are derived from
37  * software developed by the University of California, Berkeley, and its
38  * contributors.
39  */
40 
41 #include "rcv.h"
42 #ifndef preSVr4
43 #include <locale.h>
44 #endif
45 
46 /*
47  * mailx -- a modified version of a University of California at Berkeley
48  *	mail program
49  *
50  * Startup -- interface with user.
51  */
52 
53 static void		hdrstop(int);
54 
55 static jmp_buf	hdrjmp;
56 
57 const char *const version = "mailx version 5.0";
58 
59 /*
60  * Find out who the user is, copy their mail file (if exists) into
61  * /tmp/Rxxxxx and set up the message pointers.  Then, print out the
62  * message headers and read user commands.
63  *
64  * Command line syntax:
65  *	mailx [ -i ] [ -r address ] [ -h number ] [ -f [ name ] ]
66  * or:
67  *	mailx [ -i ] [ -r address ] [ -h number ] people ...
68  *
69  * and a bunch of other options.
70  */
71 
72 int
73 main(int argc, char **argv)
74 {
75 	register char *ef;
76 	register int argp;
77 	int mustsend, f, goerr = 0;
78 	void (*prevint)(int);
79 	int loaded = 0;
80 	struct termio tbuf;
81 	struct termios tbufs;
82 	int c;
83 	char *cwd, *mf;
84 
85 	/*
86 	 * Set up a reasonable environment.
87 	 * Figure out whether we are being run interactively, set up
88 	 * all the temporary files, buffer standard output, and so forth.
89 	 */
90 
91 #ifndef preSVr4
92 	(void)setlocale(LC_ALL, "");
93 #endif
94 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
95 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
96 #endif
97 	(void) textdomain(TEXT_DOMAIN);
98 
99 #ifdef SIGCONT
100 	sigset(SIGCONT, SIG_DFL);
101 #endif
102 	rpterr = 0;	/* initialize; set when we output to stderr */
103 	progname = argv[0];
104 	if (progname[strlen(progname) - 1] != 'x') {
105 		assign("bsdcompat", "");
106 	}
107 	myegid = getegid();
108 	myrgid = getgid();
109 	myeuid = geteuid();
110 	myruid = getuid();
111 	mypid = getpid();
112 	setgid(myrgid);
113 	setuid(myruid);
114 	inithost();
115 	intty = isatty(0);
116 	if (ioctl(1, TCGETS, &tbufs) < 0) {
117 		if (ioctl(1, TCGETA, &tbuf)==0) {
118 			outtty = 1;
119 			baud = tbuf.c_cflag & CBAUD;
120 		} else
121 			baud = B9600;
122 	} else {
123 		outtty = 1;
124 		baud = cfgetospeed(&tbufs);
125 	}
126 	image = -1;
127 
128 	/*
129 	 * Now, determine how we are being used.
130 	 * We successively pick off instances of -r, -h, -f, and -i.
131 	 * If called as "rmail" we note this fact for letter sending.
132 	 * If there is anything left, it is the base of the list
133 	 * of users to mail to.  Argp will be set to point to the
134 	 * first of these users.
135 	 */
136 
137 	ef = NOSTR;
138 	argp = -1;
139 	mustsend = 0;
140 	if (argc > 0 && **argv == 'r')
141 		rmail++;
142 	while ((c = getopt(argc, argv, "b:Bc:defFh:HiInNr:s:u:UtT:vV~")) != EOF)
143 		switch (c) {
144 		case 'e':
145 			/*
146 			 * exit status only
147 			 */
148 			exitflg++;
149 			break;
150 
151 		case 'r':
152 			/*
153 			 * Next argument is address to be sent along
154 			 * to the mailer.
155 			 */
156 			mustsend++;
157 			rflag = optarg;
158 			break;
159 
160 		case 'T':
161 			/*
162 			 * Next argument is temp file to write which
163 			 * articles have been read/deleted for netnews.
164 			 */
165 			Tflag = optarg;
166 			if ((f = creat(Tflag, TEMPPERM)) < 0) {
167 				perror(Tflag);
168 				exit(1);
169 			}
170 			close(f);
171 			/* fall through for -I too */
172 			/* FALLTHROUGH */
173 
174 		case 'I':
175 			/*
176 			 * print newsgroup in header summary
177 			 */
178 			newsflg++;
179 			break;
180 
181 		case 'u':
182 			/*
183 			 * Next argument is person's mailbox to use.
184 			 * Treated the same as "-f /var/mail/user".
185 			 */
186 			{
187 			static char u[PATHSIZE];
188 			snprintf(u, sizeof (u), "%s%s", maildir, optarg);
189 			ef = u;
190 			break;
191 			}
192 
193 		case 'i':
194 			/*
195 			 * User wants to ignore interrupts.
196 			 * Set the variable "ignore"
197 			 */
198 			assign("ignore", "");
199 			break;
200 
201 		case 'U':
202 			UnUUCP++;
203 			break;
204 
205 		case 'd':
206 			assign("debug", "");
207 			break;
208 
209 		case 'h':
210 			/*
211 			 * Specified sequence number for network.
212 			 * This is the number of "hops" made so
213 			 * far (count of times message has been
214 			 * forwarded) to help avoid infinite mail loops.
215 			 */
216 			mustsend++;
217 			hflag = atoi(optarg);
218 			if (hflag == 0) {
219 				fprintf(stderr,
220 				    gettext("-h needs non-zero number\n"));
221 				goerr++;
222 			}
223 			break;
224 
225 		case 's':
226 			/*
227 			 * Give a subject field for sending from
228 			 * non terminal
229 			 */
230 			mustsend++;
231 			sflag = optarg;
232 			break;
233 
234 		case 'c':	/* Cc: from command line */
235 			mustsend++;
236 			cflag = optarg;
237 			break;
238 
239 		case 'b':	/* Bcc: from command line */
240 			mustsend++;
241 			bflag = optarg;
242 			break;
243 
244 		case 'f':
245 			/*
246 			 * User is specifying file to "edit" with mailx,
247 			 * as opposed to reading system mailbox.
248 			 * If no argument is given after -f, we read their
249 			 * $MBOX file or mbox in their home directory.
250 			 */
251 			ef = (argc == optind || *argv[optind] == '-')
252 				? "" : argv[optind++];
253 			if (*ef && *ef != '/' && *ef != '+')
254 				cwd = getcwd(NOSTR, PATHSIZE);
255 			break;
256 
257 		case 'F':
258 			Fflag++;
259 			mustsend++;
260 			break;
261 
262 		case 'n':
263 			/*
264 			 * User doesn't want to source
265 			 *	/etc/mail/mailx.rc
266 			 */
267 			nosrc++;
268 			break;
269 
270 		case 'N':
271 			/*
272 			 * Avoid initial header printing.
273 			 */
274 			noheader++;
275 			break;
276 
277 		case 'H':
278 			/*
279 			 * Print headers and exit
280 			 */
281 			Hflag++;
282 			break;
283 
284 		case 'V':
285 			puts(version);
286 			return 0;
287 
288 		case '~':
289 			/*
290 			 * Permit tildas no matter where
291 			 * the input is coming from.
292 			 */
293 			assign("escapeok", "");
294 			break;
295 
296 		case 'v':
297 			/*
298 			 * Send mailer verbose flag
299 			 */
300 			assign("verbose", "");
301 			break;
302 
303 		case 'B':
304 			/*
305 			 * Don't buffer output
306 			 * (Line buffered is good enough)
307 			 */
308 			setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
309 			setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
310 			break;
311 
312 		case 't':
313 			/*
314 			 * Like sendmail -t, read headers from text
315 			 */
316 			tflag++;
317 			mustsend++;
318 			break;
319 
320 		case '?':
321 		default:
322 			goerr++;
323 			break;
324 		}
325 
326 	if (optind != argc)
327 		argp = optind;
328 
329 	/*
330 	 * Check for inconsistent arguments.
331 	 */
332 
333 	if (newsflg && ef==NOSTR) {
334 		fprintf(stderr, gettext("Need -f with -I flag\n"));
335 		goerr++;
336 	}
337 	if (ef != NOSTR && argp != -1) {
338 		fprintf(stderr,
339 		    gettext("Cannot give -f and people to send to.\n"));
340 		goerr++;
341 	}
342 	if (exitflg && (mustsend || argp != -1))
343 		exit(1);	/* nonsense flags involving -e simply exit */
344 	if (tflag && argp != -1) {
345 		fprintf(stderr,
346 		    gettext("Ignoring recipients on command line with -t\n"));
347 		argp = -1;
348 	} else if (!tflag && mustsend && argp == -1) {
349 		fprintf(stderr,
350 	    gettext("The flags you gave are used only when sending mail.\n"));
351 		goerr++;
352 	}
353 	if (goerr) {
354 		fprintf(stderr,
355 gettext("Usage: %s -eiIUdFntBNHvV~ -T FILE -u USER -h hops -r address\n"),
356 		    progname);
357 		fprintf(stderr,
358 		    gettext("\t\t-s SUBJECT -f FILE users\n"));
359 		exit(1);
360 	}
361 	tinit();
362 	input = stdin;
363 	rcvmode = !tflag && argp == -1;
364 	if (!nosrc)
365 		load(MASTER);
366 
367 	if (!rcvmode) {
368 		load(Getf("MAILRC"));
369 		if (tflag)
370 			tmail();
371 		else
372 			mail(&argv[argp]);
373 		exit(senderr ? senderr : rpterr);
374 	}
375 
376 	/*
377 	 * Ok, we are reading mail.
378 	 * Decide whether we are editing a mailbox or reading
379 	 * the system mailbox, and open up the right stuff.
380 	 *
381 	 * Do this before sourcing the MAILRC, because there might be
382 	 * a 'chdir' there that breaks the -f option.  But if the
383 	 * file specified with -f is a folder name, go ahead and
384 	 * source the MAILRC anyway so that "folder" will be defined.
385 	 */
386 
387 	nstrcpy(origname, PATHSIZE, mailname);
388 	editfile = mailname;
389 
390 	if (ef != NOSTR) {
391 		if (ef == NOSTR || *ef == '\0' || *ef == '+') {
392 			load(Getf("MAILRC"));
393 			loaded++;
394 		}
395 		ef = *ef ? safeexpand(ef) : Getf("MBOX");
396 		nstrcpy(origname, PATHSIZE, ef);
397 		if (ef[0] != '/') {
398 			if (cwd == NOSTR)
399 				cwd = getcwd(NOSTR, PATHSIZE);
400 			nstrcat(cwd, PATHSIZE, "/");
401 			nstrcat(cwd, PATHSIZE, ef);
402 			ef = cwd;
403 		}
404 		editfile = ef;
405 		edit++;
406 	}
407 
408 	if (setfile(editfile, edit) < 0)
409 		exit(1);
410 
411 	if (!loaded)
412 		load(Getf("MAILRC"));
413 	if (msgCount > 0 && !noheader && value("header") != NOSTR) {
414 		if (setjmp(hdrjmp) == 0) {
415 			if ((prevint = sigset(SIGINT, SIG_IGN)) != SIG_IGN)
416 				sigset(SIGINT, hdrstop);
417 			announce();
418 			fflush(stdout);
419 			sigset(SIGINT, prevint);
420 		}
421 	}
422 	if (Hflag || (!edit && msgCount == 0)) {
423 		if (!Hflag) {
424 			fprintf(stderr, gettext("No mail for %s\n"), myname);
425 			Verhogen();
426 		}
427 		fflush(stdout);
428 		exit(rpterr);
429 	}
430 	commands();
431 	sigset(SIGHUP, SIG_IGN);
432 	sigset(SIGINT, SIG_IGN);
433 	sigset(SIGQUIT, SIG_IGN);
434 	if (!outtty)
435 		sigset(SIGPIPE, SIG_IGN);
436 	if (edit)
437 		edstop(0);
438 	else {
439 		quit(0);
440 		Verhogen();
441 	}
442 	return (rpterr);
443 }
444 
445 /*
446  * Interrupt printing of the headers.
447  */
448 static void
449 #ifdef	__cplusplus
450 hdrstop(int)
451 #else
452 /* ARGSUSED */
453 hdrstop(int s)
454 #endif
455 {
456 
457 	fflush(stdout);
458 	fprintf(stderr, gettext("\nInterrupt\n"));
459 # ifdef OLD_BSD_SIGS
460 	sigrelse(SIGINT);
461 # endif
462 	longjmp(hdrjmp, 1);
463 }
464