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