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