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