1*d449716aSchristos /* $NetBSD: main.c,v 1.31 2010/01/12 14:45:31 christos Exp $ */
288b833a7Schristos
361f28255Scgd /*
42cb5542fSderaadt * Copyright (c) 1980, 1993
52cb5542fSderaadt * The Regents of the University of California. All rights reserved.
661f28255Scgd *
761f28255Scgd * Redistribution and use in source and binary forms, with or without
861f28255Scgd * modification, are permitted provided that the following conditions
961f28255Scgd * are met:
1061f28255Scgd * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd * notice, this list of conditions and the following disclaimer.
1261f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd * notice, this list of conditions and the following disclaimer in the
1461f28255Scgd * documentation and/or other materials provided with the distribution.
1589aaa1bbSagc * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd * may be used to endorse or promote products derived from this software
1761f28255Scgd * without specific prior written permission.
1861f28255Scgd *
1961f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd * SUCH DAMAGE.
3061f28255Scgd */
3161f28255Scgd
327c81c8f3Slukem #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
3498e5374cSlukem __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
3598e5374cSlukem The Regents of the University of California. All rights reserved.");
3661f28255Scgd #endif /* not lint */
3761f28255Scgd
3861f28255Scgd #ifndef lint
3988b833a7Schristos #if 0
4019d35cbcStls static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 4/20/95";
4188b833a7Schristos #else
42*d449716aSchristos __RCSID("$NetBSD: main.c,v 1.31 2010/01/12 14:45:31 christos Exp $");
4388b833a7Schristos #endif
4461f28255Scgd #endif /* not lint */
4561f28255Scgd
4655ce51b2Swsanchez #define EXTERN
4761f28255Scgd #include "rcv.h"
4855ce51b2Swsanchez #undef EXTERN
49798fbc60Schristos #include <assert.h>
508207b28aSchristos #include <util.h>
5155ce51b2Swsanchez
522cb5542fSderaadt #include "extern.h"
53ca13337dSchristos #include "sig.h"
5461f28255Scgd
558207b28aSchristos #ifdef USE_EDITLINE
5685c81c58Schristos #include "complete.h"
5785c81c58Schristos #endif
58798fbc60Schristos #include "format.h"
598207b28aSchristos #ifdef MIME_SUPPORT
608207b28aSchristos #include "mime.h"
618207b28aSchristos #endif
62f3098750Schristos #ifdef THREAD_SUPPORT
63f3098750Schristos #include "thread.h"
64f3098750Schristos #endif
657c81c8f3Slukem
6661f28255Scgd /*
6761f28255Scgd * Mail -- a mail program
6861f28255Scgd *
6961f28255Scgd * Startup -- interface with user.
7061f28255Scgd */
7161f28255Scgd
72ca13337dSchristos __dead
73f3098750Schristos static void
usage(void)74ca13337dSchristos usage(void)
75f3098750Schristos {
76ca13337dSchristos #ifdef MIME_SUPPORT
77ca13337dSchristos (void)fputs("\
78ca13337dSchristos Usage: mail [-EiInv] [-r rcfile] [-s subject] [-a file] [-c cc-addr]\n\
79ca13337dSchristos [-b bcc-addr] to-addr ... [- sendmail-options ...]\n\
80ca13337dSchristos mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
81ca13337dSchristos mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
82ca13337dSchristos stderr);
83ca13337dSchristos #else /* MIME_SUPPORT */
84ca13337dSchristos (void)fputs("\
85ca13337dSchristos Usage: mail [-EiInv] [-r rcfile] [-s subject] [-c cc-addr] [-b bcc-addr]\n\
86ca13337dSchristos to-addr ... [- sendmail-options ...]\n\
87ca13337dSchristos mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
88ca13337dSchristos mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
89ca13337dSchristos stderr);
90ca13337dSchristos #endif /* MIME_SUPPORT */
91ca13337dSchristos exit(1);
92f3098750Schristos }
93f3098750Schristos
94f3098750Schristos /*
95f3098750Schristos * Compute what the screen size for printing headers should be.
96f3098750Schristos * We use the following algorithm for the height:
97f3098750Schristos * If baud rate < 1200, use 9
98f3098750Schristos * If baud rate = 1200, use 14
99f3098750Schristos * If baud rate > 1200, use 24 or ws_row
100f3098750Schristos * Width is either 80 or ws_col;
101f3098750Schristos */
102f3098750Schristos PUBLIC void
setscreensize(void)103f3098750Schristos setscreensize(void)
104f3098750Schristos {
105f3098750Schristos struct termios tbuf;
106f3098750Schristos struct winsize ws;
107f3098750Schristos speed_t ospeed;
108f3098750Schristos char *cp;
109f3098750Schristos
110f3098750Schristos if (ioctl(1, TIOCGWINSZ, &ws) < 0)
111f3098750Schristos ws.ws_col = ws.ws_row = 0;
112f3098750Schristos if (tcgetattr(1, &tbuf) < 0)
113f3098750Schristos ospeed = 9600;
114f3098750Schristos else
115f3098750Schristos ospeed = cfgetospeed(&tbuf);
116f3098750Schristos if (ospeed < 1200)
117f3098750Schristos screenheight = 9;
118f3098750Schristos else if (ospeed == 1200)
119f3098750Schristos screenheight = 14;
120f3098750Schristos else if (ws.ws_row != 0)
121f3098750Schristos screenheight = ws.ws_row;
122f3098750Schristos else
123f3098750Schristos screenheight = 24;
124f3098750Schristos if ((realscreenheight = ws.ws_row) == 0)
125f3098750Schristos realscreenheight = 24;
126f3098750Schristos if ((screenwidth = ws.ws_col) == 0)
127f3098750Schristos screenwidth = 80;
128f3098750Schristos /*
129f3098750Schristos * Possible overrides from the rcfile.
130f3098750Schristos */
131f3098750Schristos if ((cp = value(ENAME_SCREENWIDTH)) != NULL) {
132f3098750Schristos int width;
133f3098750Schristos width = *cp ? atoi(cp) : 0;
134f3098750Schristos if (width >= 0)
135f3098750Schristos screenwidth = width;
136f3098750Schristos }
137f3098750Schristos if ((cp = value(ENAME_SCREENHEIGHT)) != NULL) {
138f3098750Schristos int height;
139f3098750Schristos height = *cp ? atoi(cp) : 0;
140f3098750Schristos if (height >= 0) {
141f3098750Schristos realscreenheight = height;
142f3098750Schristos screenheight = height;
143f3098750Schristos }
144f3098750Schristos }
145f3098750Schristos }
14685c81c58Schristos
14785c81c58Schristos /*
14885c81c58Schristos * Break up a white-space or comma delimited name list so that aliases
14985c81c58Schristos * can get expanded. Without this, the CC: or BCC: list is broken too
15085c81c58Schristos * late for alias expansion to occur.
15185c81c58Schristos */
152f3098750Schristos PUBLIC struct name *
lexpand(char * str,int ntype)15385c81c58Schristos lexpand(char *str, int ntype)
15485c81c58Schristos {
15585c81c58Schristos char *list;
15685c81c58Schristos struct name *np = NULL;
15785c81c58Schristos char *word, *p;
15885c81c58Schristos
1598207b28aSchristos list = estrdup(str);
16085c81c58Schristos word = list;
16185c81c58Schristos for (word = list; *word; word = p) {
162d727506fSchristos word = skip_WSP(word);
16385c81c58Schristos for (p = word;
164d727506fSchristos *p && !is_WSP(*p) && *p != ',';
16585c81c58Schristos p++)
16685c81c58Schristos continue;
16785c81c58Schristos if (*p)
16885c81c58Schristos *p++ = '\0';
16985c81c58Schristos np = cat(np, nalloc(word, ntype));
17085c81c58Schristos }
17185c81c58Schristos
17285c81c58Schristos free(list);
17385c81c58Schristos return np;
17485c81c58Schristos }
17585c81c58Schristos
176f3098750Schristos PUBLIC int
main(int argc,char * argv[])177b127ccccSwiz main(int argc, char *argv[])
17861f28255Scgd {
179ca13337dSchristos jmp_buf jmpbuf;
180ca13337dSchristos struct sigaction sa;
18161f28255Scgd struct name *to, *cc, *bcc, *smopts;
1828207b28aSchristos #ifdef MIME_SUPPORT
183798fbc60Schristos struct name *attach_optargs;
184798fbc60Schristos struct name *attach_end;
1858207b28aSchristos #endif
18661f28255Scgd char *subject;
187ece0fd5cSchristos const char *ef;
18861f28255Scgd char nosrc = 0;
189ece0fd5cSchristos const char *rc;
190ca13337dSchristos int Hflag;
191ca13337dSchristos int i;
19261f28255Scgd
19361f28255Scgd /*
194c5d61cf8Schristos * For portability, call setprogname() early, before
195c5d61cf8Schristos * getprogname() is called.
196c5d61cf8Schristos */
197c5d61cf8Schristos (void)setprogname(argv[0]);
198c5d61cf8Schristos
199c5d61cf8Schristos /*
20061f28255Scgd * Set up a reasonable environment.
20161f28255Scgd * Figure out whether we are being run interactively,
20261f28255Scgd * start the SIGCHLD catcher, and so forth.
203ca13337dSchristos * (Other signals are setup later by sig_setup().)
20461f28255Scgd */
205ca13337dSchristos (void)sigemptyset(&sa.sa_mask);
206ca13337dSchristos sa.sa_flags = SA_RESTART;
207ca13337dSchristos sa.sa_handler = sigchild;
208ca13337dSchristos (void)sigaction(SIGCHLD, &sa, NULL);
209ca13337dSchristos
21061f28255Scgd if (isatty(0))
211f3098750Schristos assign(ENAME_INTERACTIVE, "");
21261f28255Scgd image = -1;
213ca13337dSchristos
21461f28255Scgd /*
21561f28255Scgd * Now, determine how we are being used.
21661f28255Scgd * We successively pick off - flags.
21761f28255Scgd * If there is anything left, it is the base of the list
21861f28255Scgd * of users to mail to. Argp will be set to point to the
21961f28255Scgd * first of these users.
22061f28255Scgd */
221ca13337dSchristos rc = NULL;
222ab850155Swiz ef = NULL;
223cb6786d4Swiz to = NULL;
224cb6786d4Swiz cc = NULL;
225cb6786d4Swiz bcc = NULL;
226cb6786d4Swiz smopts = NULL;
227ab850155Swiz subject = NULL;
228798fbc60Schristos Hflag = 0;
2298207b28aSchristos #ifdef MIME_SUPPORT
230798fbc60Schristos attach_optargs = NULL;
231798fbc60Schristos attach_end = NULL;
232ca13337dSchristos while ((i = getopt(argc, argv, ":~EH:INT:a:b:c:dfinr:s:u:v")) != -1)
2338207b28aSchristos #else
234ca13337dSchristos while ((i = getopt(argc, argv, ":~EH:INT:b:c:dfinr:s:u:v")) != -1)
2358207b28aSchristos #endif
23685c81c58Schristos {
23761f28255Scgd switch (i) {
23861f28255Scgd case 'T':
23961f28255Scgd /*
24061f28255Scgd * Next argument is temp file to write which
24161f28255Scgd * articles have been read/deleted for netnews.
24261f28255Scgd */
24361f28255Scgd Tflag = optarg;
24461f28255Scgd if ((i = creat(Tflag, 0600)) < 0) {
245ae38aa87Swiz warn("%s", Tflag);
24661f28255Scgd exit(1);
24761f28255Scgd }
248ca286310Schristos (void)close(i);
24961f28255Scgd break;
2508207b28aSchristos #ifdef MIME_SUPPORT
251798fbc60Schristos case 'a': {
252798fbc60Schristos struct name *np;
253798fbc60Schristos np = nalloc(optarg, 0);
254798fbc60Schristos if (attach_end == NULL)
255798fbc60Schristos attach_optargs = np;
256798fbc60Schristos else {
257798fbc60Schristos np->n_blink = attach_end;
258798fbc60Schristos attach_end->n_flink = np;
259798fbc60Schristos }
260798fbc60Schristos attach_end = np;
2618207b28aSchristos break;
262798fbc60Schristos }
2638207b28aSchristos #endif
26461f28255Scgd case 'u':
26561f28255Scgd /*
26661f28255Scgd * Next argument is person to pretend to be.
26761f28255Scgd */
26861f28255Scgd myname = optarg;
269ca286310Schristos (void)unsetenv("MAIL");
27061f28255Scgd break;
27161f28255Scgd case 'i':
27261f28255Scgd /*
27361f28255Scgd * User wants to ignore interrupts.
27461f28255Scgd * Set the variable "ignore"
27561f28255Scgd */
276f3098750Schristos assign(ENAME_IGNORE, "");
27761f28255Scgd break;
27861f28255Scgd case 'd':
27961f28255Scgd debug++;
28061f28255Scgd break;
281ca13337dSchristos case 'r':
282ca13337dSchristos rc = optarg;
283ca13337dSchristos break;
28461f28255Scgd case 's':
28561f28255Scgd /*
28661f28255Scgd * Give a subject field for sending from
28761f28255Scgd * non terminal
28861f28255Scgd */
28961f28255Scgd subject = optarg;
29061f28255Scgd break;
29161f28255Scgd case 'f':
29261f28255Scgd /*
29361f28255Scgd * User is specifying file to "edit" with Mail,
29461f28255Scgd * as opposed to reading system mailbox.
29561f28255Scgd * If no argument is given after -f, we read his
29661f28255Scgd * mbox file.
29761f28255Scgd *
29861f28255Scgd * getopt() can't handle optional arguments, so here
29961f28255Scgd * is an ugly hack to get around it.
30061f28255Scgd */
30161f28255Scgd if ((argv[optind]) && (argv[optind][0] != '-'))
30261f28255Scgd ef = argv[optind++];
30361f28255Scgd else
30461f28255Scgd ef = "&";
30561f28255Scgd break;
306798fbc60Schristos case 'H':
307798fbc60Schristos /*
308798fbc60Schristos * Print out the headers and quit.
309798fbc60Schristos */
310798fbc60Schristos Hflag = get_Hflag(argv);
311798fbc60Schristos break;
31261f28255Scgd case 'n':
31361f28255Scgd /*
31461f28255Scgd * User doesn't want to source /usr/lib/Mail.rc
31561f28255Scgd */
31661f28255Scgd nosrc++;
31761f28255Scgd break;
31861f28255Scgd case 'N':
31961f28255Scgd /*
32061f28255Scgd * Avoid initial header printing.
32161f28255Scgd */
322f3098750Schristos assign(ENAME_NOHEADER, "");
32361f28255Scgd break;
32461f28255Scgd case 'v':
32561f28255Scgd /*
32661f28255Scgd * Send mailer verbose flag
32761f28255Scgd */
328f3098750Schristos assign(ENAME_VERBOSE, "");
32961f28255Scgd break;
33061f28255Scgd case 'I':
331253750edSchristos case '~':
33261f28255Scgd /*
33361f28255Scgd * We're interactive
33461f28255Scgd */
335f3098750Schristos assign(ENAME_INTERACTIVE, "");
33661f28255Scgd break;
33761f28255Scgd case 'c':
33861f28255Scgd /*
33961f28255Scgd * Get Carbon Copy Recipient list
34061f28255Scgd */
34185c81c58Schristos cc = cat(cc, lexpand(optarg, GCC));
34261f28255Scgd break;
34361f28255Scgd case 'b':
34461f28255Scgd /*
34561f28255Scgd * Get Blind Carbon Copy Recipient list
34661f28255Scgd */
34785c81c58Schristos bcc = cat(bcc, lexpand(optarg, GBCC));
34885c81c58Schristos
34985c81c58Schristos break;
350253750edSchristos case 'E':
351253750edSchristos /*
352253750edSchristos * Don't send empty files.
353253750edSchristos */
354f3098750Schristos assign(ENAME_DONTSENDEMPTY, "");
355253750edSchristos break;
356798fbc60Schristos case ':':
357798fbc60Schristos /*
358798fbc60Schristos * An optarg was expected but not found.
359798fbc60Schristos */
360798fbc60Schristos if (optopt == 'H') {
361798fbc60Schristos Hflag = get_Hflag(NULL);
362798fbc60Schristos break;
363798fbc60Schristos }
364798fbc60Schristos (void)fprintf(stderr,
365798fbc60Schristos "%s: option requires an argument -- %c\n",
366798fbc60Schristos getprogname(), optopt);
367798fbc60Schristos
368798fbc60Schristos /* FALLTHROUGH */
36961f28255Scgd case '?':
370798fbc60Schristos /*
371798fbc60Schristos * An unknown option flag. We need to do the
372798fbc60Schristos * error message.
373798fbc60Schristos */
374798fbc60Schristos if (optopt != '?')
375798fbc60Schristos (void)fprintf(stderr,
376798fbc60Schristos "%s: unknown option -- %c\n", getprogname(),
377798fbc60Schristos optopt);
378ca13337dSchristos usage(); /* print usage message and die */
379ca13337dSchristos /*NOTREACHED*/
38061f28255Scgd }
38161f28255Scgd }
38261f28255Scgd for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
38361f28255Scgd to = cat(to, nalloc(argv[i], GTO));
384d727506fSchristos for (/*EMPTY*/; argv[i]; i++)
38585c81c58Schristos smopts = cat(smopts, nalloc(argv[i], GSMOPTS));
38661f28255Scgd /*
38761f28255Scgd * Check for inconsistent arguments.
38861f28255Scgd */
389ed5c178aSchristos if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
390*d449716aSchristos errx(EXIT_FAILURE, "You must specify direct recipients with -s, -c, or -b.");
391cb6786d4Swiz if (ef != NULL && to != NULL) {
392*d449716aSchristos errx(EXIT_FAILURE, "Cannot give -f and people to send to.");
39361f28255Scgd }
394798fbc60Schristos if (Hflag != 0 && to != NULL)
395798fbc60Schristos errx(EXIT_FAILURE, "Cannot give -H and people to send to.");
3968207b28aSchristos #ifdef MIME_SUPPORT
397798fbc60Schristos if (attach_optargs != NULL && to == NULL)
398798fbc60Schristos errx(EXIT_FAILURE, "Cannot give -a without people to send to.");
3998207b28aSchristos #endif
400798fbc60Schristos tinit(); /* must be done before loading the rcfile */
40161f28255Scgd input = stdin;
402f3098750Schristos mailmode = Hflag ? mm_hdrsonly :
403f3098750Schristos to ? mm_sending : mm_receiving;
404f3098750Schristos
40561f28255Scgd spreserve();
40661f28255Scgd if (!nosrc)
40761f28255Scgd load(_PATH_MASTER_RC);
40861f28255Scgd /*
40961f28255Scgd * Expand returns a savestr, but load only uses the file name
41061f28255Scgd * for fopen, so it's safe to do this.
41161f28255Scgd */
412ca13337dSchristos if (rc == NULL && (rc = getenv("MAILRC")) == NULL)
41319d35cbcStls rc = "~/.mailrc";
41419d35cbcStls load(expand(rc));
415f3098750Schristos setscreensize(); /* do this after loading the rcfile */
41685c81c58Schristos
4178207b28aSchristos #ifdef USE_EDITLINE
418b01c8fbcSchristos /*
419b01c8fbcSchristos * This is after loading the MAILRC so we can use value().
420b01c8fbcSchristos * Avoid editline in mm_hdrsonly mode or pipelines will screw
421b01c8fbcSchristos * up. XXX - there must be a better way!
422b01c8fbcSchristos */
423b01c8fbcSchristos if (mailmode != mm_hdrsonly)
4248207b28aSchristos init_editline();
42585c81c58Schristos #endif
42685c81c58Schristos
427ca13337dSchristos sig_setup();
428ca13337dSchristos
429f3098750Schristos switch (mailmode) {
430f3098750Schristos case mm_sending:
431f3098750Schristos (void)mail(to, cc, bcc, smopts, subject,
432f3098750Schristos mime_attach_optargs(attach_optargs));
43361f28255Scgd /*
43461f28255Scgd * why wait?
43561f28255Scgd */
43661f28255Scgd exit(senderr);
437f3098750Schristos break; /* XXX - keep lint happy */
438f3098750Schristos
439f3098750Schristos case mm_receiving:
440f3098750Schristos case mm_hdrsonly:
44161f28255Scgd /*
44261f28255Scgd * Ok, we are reading mail.
44361f28255Scgd * Decide whether we are editing a mailbox or reading
44461f28255Scgd * the system mailbox, and open up the right stuff.
44561f28255Scgd */
446ab850155Swiz if (ef == NULL)
44761f28255Scgd ef = "%";
44861f28255Scgd if (setfile(ef) < 0)
44961f28255Scgd exit(1); /* error already reported */
450f3098750Schristos if (value(ENAME_QUIET) == NULL)
451ca286310Schristos (void)printf("Mail version %s. Type ? for help.\n",
45261f28255Scgd version);
453f3098750Schristos if (mailmode == mm_hdrsonly)
454f3098750Schristos show_headers_and_exit(Hflag); /* NORETURN */
45561f28255Scgd announce();
456ca286310Schristos (void)fflush(stdout);
457ca13337dSchristos
458ca13337dSchristos if (setjmp(jmpbuf) != 0) {
459ca13337dSchristos /* Return here if quit() fails below. */
460ca13337dSchristos (void)printf("Use 'exit' to quit without saving changes.\n");
46161f28255Scgd }
46261f28255Scgd commands();
463ca13337dSchristos
464ca13337dSchristos /* Ignore these signals from now on! */
465ca286310Schristos (void)signal(SIGHUP, SIG_IGN);
466ca286310Schristos (void)signal(SIGINT, SIG_IGN);
467ca286310Schristos (void)signal(SIGQUIT, SIG_IGN);
468ca13337dSchristos quit(jmpbuf);
469f3098750Schristos break;
470f3098750Schristos
471f3098750Schristos default:
472f3098750Schristos assert(/*CONSTCOND*/0);
473f3098750Schristos break;
474f3098750Schristos }
475f3098750Schristos
476ca286310Schristos return 0;
47761f28255Scgd }
478