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