Home | History | Annotate | Line # | Download | only in mail
main.c revision 1.28
      1 /*	$NetBSD: main.c,v 1.28 2007/10/23 14:58:44 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1980, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. Neither the name of the University nor the names of its contributors
     16  *    may be used to endorse or promote products derived from this software
     17  *    without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
     35 	The Regents of the University of California.  All rights reserved.\n");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)main.c	8.2 (Berkeley) 4/20/95";
     41 #else
     42 __RCSID("$NetBSD: main.c,v 1.28 2007/10/23 14:58:44 christos Exp $");
     43 #endif
     44 #endif /* not lint */
     45 
     46 #define EXTERN
     47 #include "rcv.h"
     48 #undef EXTERN
     49 #include <assert.h>
     50 #include <util.h>
     51 
     52 #include "extern.h"
     53 
     54 #ifdef USE_EDITLINE
     55 #include "complete.h"
     56 #endif
     57 #include "format.h"
     58 #ifdef MIME_SUPPORT
     59 #include "mime.h"
     60 #endif
     61 #ifdef THREAD_SUPPORT
     62 #include "thread.h"
     63 #endif
     64 
     65 /*
     66  * Mail -- a mail program
     67  *
     68  * Startup -- interface with user.
     69  */
     70 
     71 static jmp_buf	hdrjmp;
     72 
     73 /*
     74  * Interrupt printing of the headers.
     75  */
     76 /*ARGSUSED*/
     77 static void
     78 hdrstop(int signo __unused)
     79 {
     80 
     81 	(void)fflush(stdout);
     82 	(void)fprintf(stderr, "\nInterrupt\n");
     83 	longjmp(hdrjmp, 1);
     84 }
     85 
     86 /*
     87  * Compute what the screen size for printing headers should be.
     88  * We use the following algorithm for the height:
     89  *	If baud rate < 1200, use  9
     90  *	If baud rate = 1200, use 14
     91  *	If baud rate > 1200, use 24 or ws_row
     92  * Width is either 80 or ws_col;
     93  */
     94 PUBLIC void
     95 setscreensize(void)
     96 {
     97 	struct termios tbuf;
     98 	struct winsize ws;
     99 	speed_t ospeed;
    100 	char *cp;
    101 
    102 	if (ioctl(1, TIOCGWINSZ, &ws) < 0)
    103 		ws.ws_col = ws.ws_row = 0;
    104 	if (tcgetattr(1, &tbuf) < 0)
    105 		ospeed = 9600;
    106 	else
    107 		ospeed = cfgetospeed(&tbuf);
    108 	if (ospeed < 1200)
    109 		screenheight = 9;
    110 	else if (ospeed == 1200)
    111 		screenheight = 14;
    112 	else if (ws.ws_row != 0)
    113 		screenheight = ws.ws_row;
    114 	else
    115 		screenheight = 24;
    116 	if ((realscreenheight = ws.ws_row) == 0)
    117 		realscreenheight = 24;
    118 	if ((screenwidth = ws.ws_col) == 0)
    119 		screenwidth = 80;
    120 	/*
    121 	 * Possible overrides from the rcfile.
    122 	 */
    123 	if ((cp = value(ENAME_SCREENWIDTH)) != NULL) {
    124 		int width;
    125 		width = *cp ? atoi(cp) : 0;
    126 		if (width >= 0)
    127 			screenwidth = width;
    128 	}
    129 	if ((cp = value(ENAME_SCREENHEIGHT)) != NULL) {
    130 		int height;
    131 		height = *cp ? atoi(cp) : 0;
    132 		if (height >= 0) {
    133 			realscreenheight = height;
    134 			screenheight = height;
    135 		}
    136 	}
    137 }
    138 
    139 /*
    140  * Break up a white-space or comma delimited name list so that aliases
    141  * can get expanded.  Without this, the CC: or BCC: list is broken too
    142  * late for alias expansion to occur.
    143  */
    144 PUBLIC struct name *
    145 lexpand(char *str, int ntype)
    146 {
    147 	char *list;
    148 	struct name *np = NULL;
    149 	char *word, *p;
    150 
    151 	list = estrdup(str);
    152 	word = list;
    153 	for (word = list; *word; word = p) {
    154 		word = skip_WSP(word);
    155 		for (p = word;
    156 		     *p && !is_WSP(*p) && *p != ',';
    157 		     p++)
    158 			continue;
    159 		if (*p)
    160 			*p++ = '\0';
    161 		np = cat(np, nalloc(word, ntype));
    162 	}
    163 
    164 	free(list);
    165 	return np;
    166 }
    167 
    168 PUBLIC int
    169 main(int argc, char *argv[])
    170 {
    171 	int i;
    172 	struct name *to, *cc, *bcc, *smopts;
    173 #ifdef MIME_SUPPORT
    174 	struct name *attach_optargs;
    175 	struct name *attach_end;
    176 #endif
    177 	char *subject;
    178 	const char *ef;
    179 	char nosrc = 0;
    180 	sig_t prevint;
    181 	const char *rc;
    182 	int volatile Hflag;
    183 
    184 	/*
    185 	 * For portability, call setprogname() early, before
    186 	 * getprogname() is called.
    187 	 */
    188 	(void)setprogname(argv[0]);
    189 
    190 	/*
    191 	 * Set up a reasonable environment.
    192 	 * Figure out whether we are being run interactively,
    193 	 * start the SIGCHLD catcher, and so forth.
    194 	 */
    195 	(void)signal(SIGCHLD, sigchild);
    196 	if (isatty(0))
    197 		assign(ENAME_INTERACTIVE, "");
    198 	image = -1;
    199 	/*
    200 	 * Now, determine how we are being used.
    201 	 * We successively pick off - flags.
    202 	 * If there is anything left, it is the base of the list
    203 	 * of users to mail to.  Argp will be set to point to the
    204 	 * first of these users.
    205 	 */
    206 	ef = NULL;
    207 	to = NULL;
    208 	cc = NULL;
    209 	bcc = NULL;
    210 	smopts = NULL;
    211 	subject = NULL;
    212 	Hflag = 0;
    213 #ifdef MIME_SUPPORT
    214 	attach_optargs = NULL;
    215 	attach_end = NULL;
    216 	while ((i = getopt(argc, argv, ":~EH:INT:a:b:c:dfins:u:v")) != -1)
    217 #else
    218 	while ((i = getopt(argc, argv, ":~EH:INT:b:c:dfins:u:v")) != -1)
    219 #endif
    220 	{
    221 		switch (i) {
    222 		case 'T':
    223 			/*
    224 			 * Next argument is temp file to write which
    225 			 * articles have been read/deleted for netnews.
    226 			 */
    227 			Tflag = optarg;
    228 			if ((i = creat(Tflag, 0600)) < 0) {
    229 				warn("%s", Tflag);
    230 				exit(1);
    231 			}
    232 			(void)close(i);
    233 			break;
    234 #ifdef MIME_SUPPORT
    235 		case 'a': {
    236 			struct name *np;
    237 			np = nalloc(optarg, 0);
    238 			if (attach_end == NULL)
    239 				attach_optargs = np;
    240 			else {
    241 				np->n_blink = attach_end;
    242 				attach_end->n_flink = np;
    243 			}
    244 			attach_end = np;
    245 			break;
    246 		}
    247 #endif
    248 		case 'u':
    249 			/*
    250 			 * Next argument is person to pretend to be.
    251 			 */
    252 			myname = optarg;
    253 			(void)unsetenv("MAIL");
    254 			break;
    255 		case 'i':
    256 			/*
    257 			 * User wants to ignore interrupts.
    258 			 * Set the variable "ignore"
    259 			 */
    260 			assign(ENAME_IGNORE, "");
    261 			break;
    262 		case 'd':
    263 			debug++;
    264 			break;
    265 		case 's':
    266 			/*
    267 			 * Give a subject field for sending from
    268 			 * non terminal
    269 			 */
    270 			subject = optarg;
    271 			break;
    272 		case 'f':
    273 			/*
    274 			 * User is specifying file to "edit" with Mail,
    275 			 * as opposed to reading system mailbox.
    276 			 * If no argument is given after -f, we read his
    277 			 * mbox file.
    278 			 *
    279 			 * getopt() can't handle optional arguments, so here
    280 			 * is an ugly hack to get around it.
    281 			 */
    282 			if ((argv[optind]) && (argv[optind][0] != '-'))
    283 				ef = argv[optind++];
    284 			else
    285 				ef = "&";
    286 			break;
    287 		case 'H':
    288 			/*
    289 			 * Print out the headers and quit.
    290 			 */
    291 			Hflag = get_Hflag(argv);
    292 			break;
    293 		case 'n':
    294 			/*
    295 			 * User doesn't want to source /usr/lib/Mail.rc
    296 			 */
    297 			nosrc++;
    298 			break;
    299 		case 'N':
    300 			/*
    301 			 * Avoid initial header printing.
    302 			 */
    303 			assign(ENAME_NOHEADER, "");
    304 			break;
    305 		case 'v':
    306 			/*
    307 			 * Send mailer verbose flag
    308 			 */
    309 			assign(ENAME_VERBOSE, "");
    310 			break;
    311 		case 'I':
    312 		case '~':
    313 			/*
    314 			 * We're interactive
    315 			 */
    316 			assign(ENAME_INTERACTIVE, "");
    317 			break;
    318 		case 'c':
    319 			/*
    320 			 * Get Carbon Copy Recipient list
    321 			 */
    322 			cc = cat(cc, lexpand(optarg, GCC));
    323 			break;
    324 		case 'b':
    325 			/*
    326 			 * Get Blind Carbon Copy Recipient list
    327 			 */
    328 			bcc = cat(bcc, lexpand(optarg, GBCC));
    329 
    330 			break;
    331 		case 'E':
    332 			/*
    333 			 * Don't send empty files.
    334 			 */
    335 			assign(ENAME_DONTSENDEMPTY, "");
    336 			break;
    337 		case ':':
    338 			/*
    339 			 * An optarg was expected but not found.
    340 			 */
    341 			if (optopt == 'H') {
    342 				Hflag = get_Hflag(NULL);
    343 				break;
    344 			}
    345 			(void)fprintf(stderr,
    346 			    "%s: option requires an argument -- %c\n",
    347 			    getprogname(), optopt);
    348 
    349 			/* FALLTHROUGH */
    350 		case '?':
    351 			/*
    352 			 * An unknown option flag.  We need to do the
    353 			 * error message.
    354 			 */
    355 			if (optopt != '?')
    356 				(void)fprintf(stderr,
    357 				    "%s: unknown option -- %c\n", getprogname(),
    358 				    optopt);
    359 #ifdef MIME_SUPPORT
    360 			(void)fputs("\
    361 Usage: mail [-EiInv] [-s subject] [-a file] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
    362             [- sendmail-options ...]\n\
    363        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
    364        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
    365 				stderr);
    366 #else /* MIME_SUPPORT */
    367 			(void)fputs("\
    368 Usage: mail [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
    369             [- sendmail-options ...]\n\
    370        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
    371        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
    372 				stderr);
    373 #endif /* MIME_SUPPORT */
    374 
    375 			exit(1);
    376 		}
    377 	}
    378 	for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
    379 		to = cat(to, nalloc(argv[i], GTO));
    380 	for (/*EMPTY*/; argv[i]; i++)
    381 		smopts = cat(smopts, nalloc(argv[i], GSMOPTS));
    382 	/*
    383 	 * Check for inconsistent arguments.
    384 	 */
    385 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
    386 		errx(1, "You must specify direct recipients with -s, -c, or -b.");
    387 	if (ef != NULL && to != NULL) {
    388 		errx(1, "Cannot give -f and people to send to.");
    389 	}
    390 	if (Hflag != 0 && to != NULL)
    391 		errx(EXIT_FAILURE, "Cannot give -H and people to send to.");
    392 #ifdef MIME_SUPPORT
    393 	if (attach_optargs != NULL && to == NULL)
    394 		errx(EXIT_FAILURE, "Cannot give -a without people to send to.");
    395 #endif
    396 	tinit();	/* must be done before loading the rcfile */
    397 	input = stdin;
    398 	mailmode = Hflag ? mm_hdrsonly :
    399 	    to ? mm_sending : mm_receiving;
    400 
    401 	spreserve();
    402 	if (!nosrc)
    403 		load(_PATH_MASTER_RC);
    404 	/*
    405 	 * Expand returns a savestr, but load only uses the file name
    406 	 * for fopen, so it's safe to do this.
    407 	 */
    408 	if ((rc = getenv("MAILRC")) == 0)
    409 		rc = "~/.mailrc";
    410 	load(expand(rc));
    411 	setscreensize();	/* do this after loading the rcfile */
    412 
    413 #ifdef USE_EDITLINE
    414 	/*
    415 	 * This is after loading the MAILRC so we can use value().
    416 	 * Avoid editline in mm_hdrsonly mode or pipelines will screw
    417 	 * up.  XXX - there must be a better way!
    418 	 */
    419 	if (mailmode != mm_hdrsonly)
    420 		init_editline();
    421 #endif
    422 
    423 	switch (mailmode) {
    424 	case mm_sending:
    425 		(void)mail(to, cc, bcc, smopts, subject,
    426 		    mime_attach_optargs(attach_optargs));
    427 		/*
    428 		 * why wait?
    429 		 */
    430 		exit(senderr);
    431 		break;	/* XXX - keep lint happy */
    432 
    433 	case mm_receiving:
    434 	case mm_hdrsonly:
    435 		/*
    436 		 * Ok, we are reading mail.
    437 		 * Decide whether we are editing a mailbox or reading
    438 		 * the system mailbox, and open up the right stuff.
    439 		 */
    440 		if (ef == NULL)
    441 			ef = "%";
    442 		if (setfile(ef) < 0)
    443 			exit(1);		/* error already reported */
    444 		if (setjmp(hdrjmp) == 0) {
    445 			if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
    446 				(void)signal(SIGINT, hdrstop);
    447 			if (value(ENAME_QUIET) == NULL)
    448 				(void)printf("Mail version %s.  Type ? for help.\n",
    449 				    version);
    450 			if (mailmode == mm_hdrsonly)
    451 				show_headers_and_exit(Hflag);	/* NORETURN */
    452 			announce();
    453 			(void)fflush(stdout);
    454 			(void)signal(SIGINT, prevint);
    455 		}
    456 		commands();
    457 		(void)signal(SIGHUP, SIG_IGN);
    458 		(void)signal(SIGINT, SIG_IGN);
    459 		(void)signal(SIGQUIT, SIG_IGN);
    460 		quit();
    461 		break;
    462 
    463 	default:
    464 		assert(/*CONSTCOND*/0);
    465 		break;
    466 	}
    467 
    468 	return 0;
    469 }
    470