1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the BSD package               *
4 *Copyright (c) 1978-2013 The Regents of the University of California an*
5 *                                                                      *
6 * Redistribution and use in source and binary forms, with or           *
7 * without modification, are permitted provided that the following      *
8 * conditions are met:                                                  *
9 *                                                                      *
10 *    1. Redistributions of source code must retain the above           *
11 *       copyright notice, this list of conditions and the              *
12 *       following disclaimer.                                          *
13 *                                                                      *
14 *    2. Redistributions in binary form must reproduce the above        *
15 *       copyright notice, this list of conditions and the              *
16 *       following disclaimer in the documentation and/or other         *
17 *       materials provided with the distribution.                      *
18 *                                                                      *
19 *    3. Neither the name of The Regents of the University of California*
20 *       names of its contributors may be used to endorse or            *
21 *       promote products derived from this software without            *
22 *       specific prior written permission.                             *
23 *                                                                      *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND               *
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,          *
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF             *
27 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE             *
28 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS    *
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,             *
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED      *
31 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,        *
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    *
33 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,      *
34 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY       *
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE              *
36 * POSSIBILITY OF SUCH DAMAGE.                                          *
37 *                                                                      *
38 * Redistribution and use in source and binary forms, with or without   *
39 * modification, are permitted provided that the following conditions   *
40 * are met:                                                             *
41 * 1. Redistributions of source code must retain the above copyright    *
42 *    notice, this list of conditions and the following disclaimer.     *
43 * 2. Redistributions in binary form must reproduce the above copyright *
44 *    notice, this list of conditions and the following disclaimer in   *
45 *    the documentation and/or other materials provided with the        *
46 *    distribution.                                                     *
47 * 3. Neither the name of the University nor the names of its           *
48 *    contributors may be used to endorse or promote products derived   *
49 *    from this software without specific prior written permission.     *
50 *                                                                      *
51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS"    *
52 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED    *
53 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A      *
54 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS    *
55 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,      *
56 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT     *
57 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF     *
58 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND  *
59 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,   *
60 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT   *
61 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF   *
62 * SUCH DAMAGE.                                                         *
63 *                                                                      *
64 *                          Kurt Shoens (UCB)                           *
65 *                                 gsf                                  *
66 *                                                                      *
67 ***********************************************************************/
68 #pragma prototyped
69 /*
70  * Mail -- a mail program
71  *
72  * Startup -- interface with user.
73  */
74 
75 #include "mailx.h"
76 
77 #if _PACKAGE_ast
78 
79 #include "stamp.h"
80 
81 static const char usage[] =
82 "[-?" STAMP "]"
83 USAGE_LICENSE
84 "[+NAME?mailx - send and receive mail]"
85 "[+DESCRIPTION?\bmailx\b is a mail processing command. The options place"
86 "	\bmailx\b in either \asend\a or \areceive\a mode. Send mode composes"
87 "	and sends messages from text on the standard input. Receive mode"
88 "	provides both an interactive command language and non-interactive"
89 "	command line actions. Receive mode, on startup, reads commands and"
90 "	variable settings from the file \b$HOME/.mailrc\b, if it exists.]"
91 "[+?\bmailx\b provides commands for saving, deleting and responding to"
92 "	messages. Message composition supports editing, reviewing and"
93 "	other modifications as the message is entered.]"
94 "[+?Incoming mail is stored in one or more unspecified locations for each"
95 "	user, collectively called the system mailbox for that user. When"
96 "	\bmailx\b is invoked in \areceive\a mode, the system mailbox is"
97 "	searched by default.]"
98 "[+?For additional help run \bmailx\b with no options or operands,"
99 "	and then enter the \bhelp\b command at the interactive prompt.]"
100 "[A:articles?Treat mail folders as netnews article.]"
101 "[F:followup?Automatically include the previous message (followup) text"
102 "	when composing a reply.]"
103 "[H:headers?List all headers and exit.]"
104 "[I:interactive?Force interactive receive mode.]"
105 "[N!:list-headers?List a screen of headers on receive mode startup.]"
106 "[P:pipe?Coprocess receive mode from a pipe.]"
107 "[Q:query?List the status character and sender address for the \acount\a"
108 "	most recent messages, one line per message, and exit. See \b--status\b"
109 "	for message status characters details.]:[count]"
110 "[S:status?List the status character and sender address for all messages,"
111 "	one line per message, and exit. The message status characters are:]{"
112 "		[M?To be saved in \bmbox\b and marked for delete on exit.]"
113 "		[N?New message.]"
114 "		[P?Preserved and will not be deleted.]"
115 "		[R?Already read.]"
116 "		[U?Unread message from previous session.]"
117 "		[X?Possible spam.]"
118 "		[\b*\b?Saved to a folder and marked for delete on exit.]"
119 "}"
120 "[T:oldnews?Read/deleted netnews article names are appended to \afile\a]:[file]"
121 "[b:bcc?Prompt for the blind carbon copy recipient list when composing"
122 "	messages.]"
123 "[c:cc?Prompt for the carbon copy recipient list when composing messages.]"
124 "[d:debug?Enable implementation specific debug output.]"
125 "[e:check?Silently exit 0 if there is mail, 1 otherwise.]"
126 "[f:folder?Mail is read from \afolder\a instead of the default"
127 "	user mailbox.]:?[folder:=$HOME/mbox]"
128 "[i:ignore-interrupts?Ignore interrupts.]"
129 "[n!:master?Read commands from \b/etc/mailx.rc\b on receive mode startup.]"
130 "[o:set?Set \aname\a=\avalue\a options. The interactive mail command"
131 "	\bhelp set all\b lists details for each option.]:[name=value]"
132 "[r:address?Set the reply to header address to \aaddress\a.]:[address]"
133 "[s:subject?The non-interactive send mode subject text.]:[text]"
134 "[t:sendheaders?Check for headers in send mode message text.]"
135 "[u:user?Pretend to be this \buser\b in send mode. For debugging.]:[user]"
136 "[v:verbose?Enable implementation specific send mode verbose trace.]"
137 "\n"
138 "\n[ address ... ]\n"
139 "\n"
140 "[+SEE ALSO?\b/bin/mail\b(1), \bMail\b(1)]"
141 ;
142 
143 #undef	optarg
144 #define optarg		opt_info.arg
145 #undef	optnum
146 #define optnum		opt_info.num
147 #undef	optind
148 #define optind		opt_info.index
149 
150 #undef	getopt
151 #define getopt(c,v,u)	optget(v,u)
152 
153 #else
154 
155 static const char usage[] = "AFHINPQ:ST:b:c:defino:r:s:tu:v";
156 
157 #endif
158 
159 /*
160  * Interrupt printing of the headers.
161  */
162 static void
hdrstop(int sig)163 hdrstop(int sig)
164 {
165 	note(0, "\nInterrupt");
166 	longjmp(state.jump.header, sig);
167 }
168 
169 /*
170  * Set command line options and append to
171  * op list for resetopt() after the rc's.
172  */
173 static struct list*
setopt(register struct list * op,char * s,char * v)174 setopt(register struct list* op, char* s, char* v)
175 {
176 	int		n;
177 	struct argvec	vec;
178 
179 	n = strlen(s) + 1;
180 	if (v)
181 		n += strlen(v) + 1;
182 	if (!(op->next = newof(0, struct list, 1, n)))
183 		note(PANIC, "Out of space");
184 	op = op->next;
185 	s = strcopy(op->name, s);
186 	if (v) {
187 		*s++ = '=';
188 		strcpy(s, v);
189 	}
190 	state.onstack++;
191 	initargs(&vec);
192 	getargs(&vec, op->name);
193 	if (endargs(&vec) > 0) {
194 		state.cmdline = 1;
195 		set(vec.argv);
196 		state.cmdline = 0;
197 	}
198 	sreset();
199 	state.onstack--;
200 	return op;
201 }
202 
203 /*
204  * Reset the setopt() options after the rc's.
205  */
206 static void
resetopt(register struct list * op)207 resetopt(register struct list* op)
208 {
209 	register struct list*	np;
210 	struct argvec		vec;
211 
212 	np = op->next;
213 	while (op = np) {
214 		initargs(&vec);
215 		getargs(&vec, op->name);
216 		if (endargs(&vec) > 0) {
217 			state.cmdline = 1;
218 			set(vec.argv);
219 			state.cmdline = 0;
220 		}
221 		sreset();
222 		np = op->next;
223 		free(op);
224 	}
225 }
226 
227 int
main(int argc,char ** argv)228 main(int argc, char** argv)
229 {
230 	register int	i;
231 	int		sig;
232 	char*		ef;
233 	int		flags = SIGN;
234 	sig_t		prevint;
235 	struct header	head;
236 	struct list	options;
237 	struct list*	op;
238 #if _PACKAGE_ast
239 	int		fatal = 0;
240 #endif
241 
242 #if _PACKAGE_ast
243 	error_info.id = "mailx";
244 #endif
245 
246 	/*
247 	 * Set up a reasonable environment.
248 	 * Figure out whether we are being run interactively,
249 	 * and so forth.
250 	 */
251 	memset(&head, 0, sizeof(head));
252 	(op = &options)->next = 0;
253 	if (!(state.path.buf = sfstropen()) || !(state.path.move = sfstropen()) || !(state.path.part = sfstropen()) || !(state.path.temp = sfstropen()))
254 		note(FATAL, "out of space");
255 	varinit();
256 	/*
257 	 * Now, determine how we are being used.
258 	 * We successively pick off - flags.
259 	 * If there is anything left, it is the base of the list
260 	 * of users to mail to.  Argp will be set to point to the
261 	 * first of these users.
262 	 */
263 	ef = 0;
264 	opterr = 0;
265 	for (;;) {
266 		switch (getopt(argc, argv, usage)) {
267 		case 0:
268 		case EOF:
269 			break;
270 		case 'A':
271 			op = setopt(op, "news", NiL);
272 			continue;
273 		case 'F':
274 			flags |= FOLLOWUP;
275 			flags &= ~SIGN;
276 			continue;
277 		case 'H':
278 			/*
279 			 * List all headers and exit.
280 			 */
281 			op = setopt(op, "justheaders", NiL);
282 			state.var.quiet = state.on;
283 			continue;
284 		case 'I':
285 			/*
286 			 * We're interactive
287 			 */
288 			op = setopt(op, "interactive", NiL);
289 			continue;
290 		case 'N':
291 			/*
292 			 * Avoid initial header printing.
293 			 */
294 			op = setopt(op, "noheader", NiL);
295 			state.var.quiet = state.on;
296 			continue;
297 		case 'P':
298 			/*
299 			 * Coprocess on pipe.
300 			 */
301 			op = setopt(op, "coprocess", NiL);
302 			continue;
303 		case 'Q':
304 			/*
305 			 * List all n most recent status and senders and exit.
306 			 */
307 			op = setopt(op, "justfrom", optarg);
308 			state.var.quiet = state.on;
309 			continue;
310 		case 'S':
311 			/*
312 			 * List all status and senders and exit.
313 			 */
314 			op = setopt(op, "justfrom", "-1");
315 			state.var.quiet = state.on;
316 			continue;
317 		case 'T':
318 			/*
319 			 * Next argument is temp file to write which
320 			 * articles have been read/deleted for netnews.
321 			 */
322 			op = setopt(op, "news", optarg);
323 			continue;
324 		case 'b':
325 			/*
326 			 * Get Blind Carbon Copy Recipient list
327 			 */
328 			extract(&head, GBCC|GMETOO, optarg);
329 			continue;
330 		case 'c':
331 			/*
332 			 * Get Carbon Copy Recipient list
333 			 */
334 			extract(&head, GCC|GMETOO, optarg);
335 			continue;
336 		case 'd':
337 			/*
338 			 * Debug output.
339 			 */
340 			op = setopt(op, "debug", NiL);
341 			continue;
342 		case 'e':
343 			/*
344 			 * Silently exit 0 if mail, 1, otherwise.
345 			 */
346 			op = setopt(op, "justcheck", NiL);
347 			state.var.quiet = state.on;
348 			continue;
349 		case 'f':
350 #if _PACKAGE_ast
351 			if (!(ef = opt_info.arg))
352 				ef = "&";
353 #else
354 			/*
355 			 * User is specifying file to "edit" with Mail,
356 			 * as opposed to reading system mailbox.
357 			 * If no argument is given after -f, we read his
358 			 * mbox file.
359 			 *
360 			 * getopt() can't handle optional arguments, so here
361 			 * is an ugly hack to get around it.
362 			 */
363 			if (argv[optind] && argv[optind][0] != '-')
364 				ef = argv[optind++];
365 			else
366 				ef = "&";
367 #endif
368 			continue;
369 		case 'i':
370 			/*
371 			 * User wants to ignore interrupts.
372 			 * Set the variable "ignore"
373 			 */
374 			op = setopt(op, "ignore", NiL);
375 			continue;
376 		case 'n':
377 			/*
378 			 * User doesn't want to source state.var.master
379 			 */
380 			op = setopt(op, "nomaster", NiL);
381 			continue;
382 		case 'o':
383 			/*
384 			 * Set option(s) by name.
385 			 */
386 			op = setopt(op, optarg, NiL);
387 			continue;
388 		case 'r':
389 			/*
390 			 * Set replyto.
391 			 */
392 			{
393 				char*			s;
394 				int			n;
395 
396 				static const char	h[] = "fixedheaders=Reply-To:\" \"";
397 
398 				n = strlen(optarg);
399 				if (!(s = newof(0, char, n + sizeof(h) + 1, 0)))
400 					note(PANIC, "Out of space");
401 				memcpy(s, h, sizeof(h) - 1);
402 				memcpy(s + sizeof(h) - 1, optarg, n);
403 				op = setopt(op, s, NiL);
404 			}
405 			continue;
406 		case 's':
407 			/*
408 			 * Give a subject field for sending from
409 			 * non terminal
410 			 */
411 			if (head.h_subject = optarg)
412 				head.h_flags |= GSUB;
413 			continue;
414 		case 't':
415 			/*
416 			 * Check for headers in message text.
417 			 */
418 			op = setopt(op, "sendheaders", optarg);
419 			state.mode = SEND;
420 			continue;
421 		case 'u':
422 			/*
423 			 * Next argument is person to pretend to be.
424 			 */
425 			op = setopt(op, "user", optarg);
426 			continue;
427 		case 'v':
428 			/*
429 			 * Send mailer verbose flag
430 			 */
431 			op = setopt(op, "verbose", NiL);
432 			continue;
433 #if _PACKAGE_ast
434 		case '?':
435 			error(ERROR_USAGE|4, "%s", opt_info.arg);
436 			break;
437 		case ':':
438 			error(2, "%s", opt_info.arg);
439 			fatal = 1;
440 			break;
441 #else
442 		case '?':
443 			note(FATAL, "\
444 Usage: mail [-o [no]name[=value]] [-s subject] [-c cc] [-b bcc] to ...\n\
445        mail [-o [no]name[=value]] [-f [folder]]");
446 			break;
447 #endif
448 		}
449 		break;
450 	}
451 #if _PACKAGE_ast
452 	if (fatal)
453 		error(ERROR_USAGE|4, "%s", optusage(NiL));
454 #endif
455 	for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
456 		extract(&head, GTO|GMETOO, argv[i]);
457 	if (argv[i])
458 		head.h_options = argv;
459 	if (!state.mode)
460 		state.mode = (head.h_flags & GTO) ? SEND : RECEIVE;
461 	/*
462 	 * Check for inconsistent arguments.
463 	 */
464 	if (state.mode == RECEIVE && (head.h_flags & GSTD)) {
465 		if (!state.var.sendheaders)
466 			note(FATAL|IDENTIFY, "You must specify direct recipients with -s, -c, or -b");
467 		state.mode = SEND;
468 	}
469 	if (state.mode == RECEIVE)
470 		state.var.receive = state.on;
471 	if (state.mode == SEND && ef)
472 		note(FATAL|IDENTIFY, "Cannot give -f and people to send to");
473 	if (state.var.justcheck && state.mode == SEND)
474 		exit(1);
475 	tempinit();
476 	state.input = stdin;
477 	/*
478 	 * Up to this point salloc()==malloc() by default.
479 	 * From now on salloc() space cleared by sreset().
480 	 */
481 	state.onstack = 1;
482 	if (state.var.master)
483 		load(expand(state.var.master, 1));
484 	/*
485 	 * Expand returns a savestr, but load only uses the file name
486 	 * for fopen, so it's safe to do this.
487 	 */
488 	load(expand(state.var.mailrc, 1));
489 	/*
490 	 * Reset command line options so they take precedence over the rc's.
491 	 */
492 	resetopt(&options);
493 	if (state.mode == SEND) {
494 		sendmail(&head, flags);
495 		/*
496 		 * why wait?
497 		 */
498 		exit(state.senderr);
499 	}
500 	/*
501 	 * Ok, we are reading mail.
502 	 * Decide whether we are editing a mailbox or reading
503 	 * the system mailbox, and open up the right stuff.
504 	 */
505 	if (!ef)
506 		ef = "%";
507 	if (setfolder(ef) < 0)
508 		exit(1);
509 	if (sig = setjmp(state.jump.header))
510 		resume(sig);
511 	else {
512 		if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
513 			signal(SIGINT, hdrstop);
514 		if (!state.var.quiet)
515 			note(0, "Mail version %s.  Type ? for help", state.version);
516 		announce();
517 		fflush(stdout);
518 		signal(SIGINT, prevint);
519 	}
520 	if (!state.var.justheaders) {
521 		commands();
522 		signal(SIGHUP, SIG_IGN);
523 		signal(SIGINT, SIG_IGN);
524 		signal(SIGQUIT, SIG_IGN);
525 		quit();
526 	}
527 	exit(0);
528 }
529