Home | History | Annotate | Line # | Download | only in mail
main.c revision 1.25
      1 /*	$NetBSD: main.c,v 1.25 2006/11/28 18:45:32 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.25 2006/11/28 18:45:32 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 		while (isblank((unsigned char)*word))
    155 			continue;
    156 		for (p = word;
    157 		     *p && !isblank((unsigned char)*p) && *p != ',';
    158 		     p++)
    159 			continue;
    160 		if (*p)
    161 			*p++ = '\0';
    162 		np = cat(np, nalloc(word, ntype));
    163 	}
    164 
    165 	free(list);
    166 	return np;
    167 }
    168 
    169 PUBLIC int
    170 main(int argc, char *argv[])
    171 {
    172 	int i;
    173 	struct name *to, *cc, *bcc, *smopts;
    174 #ifdef MIME_SUPPORT
    175 	struct name *attach_optargs;
    176 	struct name *attach_end;
    177 #endif
    178 	char *subject;
    179 	const char *ef;
    180 	char nosrc = 0;
    181 	sig_t prevint;
    182 	const char *rc;
    183 	int volatile Hflag;
    184 
    185 	/*
    186 	 * Set up a reasonable environment.
    187 	 * Figure out whether we are being run interactively,
    188 	 * start the SIGCHLD catcher, and so forth.
    189 	 */
    190 	(void)signal(SIGCHLD, sigchild);
    191 	if (isatty(0))
    192 		assign(ENAME_INTERACTIVE, "");
    193 	image = -1;
    194 	/*
    195 	 * Now, determine how we are being used.
    196 	 * We successively pick off - flags.
    197 	 * If there is anything left, it is the base of the list
    198 	 * of users to mail to.  Argp will be set to point to the
    199 	 * first of these users.
    200 	 */
    201 	ef = NULL;
    202 	to = NULL;
    203 	cc = NULL;
    204 	bcc = NULL;
    205 	smopts = NULL;
    206 	subject = NULL;
    207 	Hflag = 0;
    208 #ifdef MIME_SUPPORT
    209 	attach_optargs = NULL;
    210 	attach_end = NULL;
    211 	while ((i = getopt(argc, argv, ":~EH:INT:a:b:c:dfins:u:v")) != -1)
    212 #else
    213 	while ((i = getopt(argc, argv, ":~EH:INT:b:c:dfins:u:v")) != -1)
    214 #endif
    215 	{
    216 		switch (i) {
    217 		case 'T':
    218 			/*
    219 			 * Next argument is temp file to write which
    220 			 * articles have been read/deleted for netnews.
    221 			 */
    222 			Tflag = optarg;
    223 			if ((i = creat(Tflag, 0600)) < 0) {
    224 				warn("%s", Tflag);
    225 				exit(1);
    226 			}
    227 			(void)close(i);
    228 			break;
    229 #ifdef MIME_SUPPORT
    230 		case 'a': {
    231 			struct name *np;
    232 			np = nalloc(optarg, 0);
    233 			if (attach_end == NULL)
    234 				attach_optargs = np;
    235 			else {
    236 				np->n_blink = attach_end;
    237 				attach_end->n_flink = np;
    238 			}
    239 			attach_end = np;
    240 			break;
    241 		}
    242 #endif
    243 		case 'u':
    244 			/*
    245 			 * Next argument is person to pretend to be.
    246 			 */
    247 			myname = optarg;
    248 			(void)unsetenv("MAIL");
    249 			break;
    250 		case 'i':
    251 			/*
    252 			 * User wants to ignore interrupts.
    253 			 * Set the variable "ignore"
    254 			 */
    255 			assign(ENAME_IGNORE, "");
    256 			break;
    257 		case 'd':
    258 			debug++;
    259 			break;
    260 		case 's':
    261 			/*
    262 			 * Give a subject field for sending from
    263 			 * non terminal
    264 			 */
    265 			subject = optarg;
    266 			break;
    267 		case 'f':
    268 			/*
    269 			 * User is specifying file to "edit" with Mail,
    270 			 * as opposed to reading system mailbox.
    271 			 * If no argument is given after -f, we read his
    272 			 * mbox file.
    273 			 *
    274 			 * getopt() can't handle optional arguments, so here
    275 			 * is an ugly hack to get around it.
    276 			 */
    277 			if ((argv[optind]) && (argv[optind][0] != '-'))
    278 				ef = argv[optind++];
    279 			else
    280 				ef = "&";
    281 			break;
    282 		case 'H':
    283 			/*
    284 			 * Print out the headers and quit.
    285 			 */
    286 			Hflag = get_Hflag(argv);
    287 			break;
    288 		case 'n':
    289 			/*
    290 			 * User doesn't want to source /usr/lib/Mail.rc
    291 			 */
    292 			nosrc++;
    293 			break;
    294 		case 'N':
    295 			/*
    296 			 * Avoid initial header printing.
    297 			 */
    298 			assign(ENAME_NOHEADER, "");
    299 			break;
    300 		case 'v':
    301 			/*
    302 			 * Send mailer verbose flag
    303 			 */
    304 			assign(ENAME_VERBOSE, "");
    305 			break;
    306 		case 'I':
    307 		case '~':
    308 			/*
    309 			 * We're interactive
    310 			 */
    311 			assign(ENAME_INTERACTIVE, "");
    312 			break;
    313 		case 'c':
    314 			/*
    315 			 * Get Carbon Copy Recipient list
    316 			 */
    317 			cc = cat(cc, lexpand(optarg, GCC));
    318 			break;
    319 		case 'b':
    320 			/*
    321 			 * Get Blind Carbon Copy Recipient list
    322 			 */
    323 			bcc = cat(bcc, lexpand(optarg, GBCC));
    324 
    325 			break;
    326 		case 'E':
    327 			/*
    328 			 * Don't send empty files.
    329 			 */
    330 			assign(ENAME_DONTSENDEMPTY, "");
    331 			break;
    332 		case ':':
    333 			/*
    334 			 * An optarg was expected but not found.
    335 			 */
    336 			if (optopt == 'H') {
    337 				Hflag = get_Hflag(NULL);
    338 				break;
    339 			}
    340 			(void)fprintf(stderr,
    341 			    "%s: option requires an argument -- %c\n",
    342 			    getprogname(), optopt);
    343 
    344 			/* FALLTHROUGH */
    345 		case '?':
    346 			/*
    347 			 * An unknown option flag.  We need to do the
    348 			 * error message.
    349 			 */
    350 			if (optopt != '?')
    351 				(void)fprintf(stderr,
    352 				    "%s: unknown option -- %c\n", getprogname(),
    353 				    optopt);
    354 #ifdef MIME_SUPPORT
    355 			(void)fputs("\
    356 Usage: mail [-EiInv] [-s subject] [-a file] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
    357             [- sendmail-options ...]\n\
    358        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
    359        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
    360 				stderr);
    361 #else /* MIME_SUPPORT */
    362 			(void)fputs("\
    363 Usage: mail [-EiInv] [-s subject] [-c cc-addr] [-b bcc-addr] to-addr ...\n\
    364             [- sendmail-options ...]\n\
    365        mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\
    366        mail [-EiInNv] [-H[colon-modifier]] [-u user]\n",
    367 				stderr);
    368 #endif /* MIME_SUPPORT */
    369 
    370 			exit(1);
    371 		}
    372 	}
    373 	for (i = optind; (argv[i]) && (*argv[i] != '-'); i++)
    374 		to = cat(to, nalloc(argv[i], GTO));
    375 	for (; argv[i]; i++)
    376 		smopts = cat(smopts, nalloc(argv[i], GSMOPTS));
    377 	/*
    378 	 * Check for inconsistent arguments.
    379 	 */
    380 	if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL))
    381 		errx(1, "You must specify direct recipients with -s, -c, or -b.");
    382 	if (ef != NULL && to != NULL) {
    383 		errx(1, "Cannot give -f and people to send to.");
    384 	}
    385 	if (Hflag != 0 && to != NULL)
    386 		errx(EXIT_FAILURE, "Cannot give -H and people to send to.");
    387 #ifdef MIME_SUPPORT
    388 	if (attach_optargs != NULL && to == NULL)
    389 		errx(EXIT_FAILURE, "Cannot give -a without people to send to.");
    390 #endif
    391 	tinit();	/* must be done before loading the rcfile */
    392 	input = stdin;
    393 	mailmode = Hflag ? mm_hdrsonly :
    394 	    to ? mm_sending : mm_receiving;
    395 
    396 	spreserve();
    397 	if (!nosrc)
    398 		load(_PATH_MASTER_RC);
    399 	/*
    400 	 * Expand returns a savestr, but load only uses the file name
    401 	 * for fopen, so it's safe to do this.
    402 	 */
    403 	if ((rc = getenv("MAILRC")) == 0)
    404 		rc = "~/.mailrc";
    405 	load(expand(rc));
    406 	setscreensize();	/* do this after loading the rcfile */
    407 
    408 #ifdef USE_EDITLINE
    409 	/* this is after loading the MAILRC so we can use value() */
    410 	init_editline();
    411 #endif
    412 
    413 	switch (mailmode) {
    414 	case mm_sending:
    415 		(void)mail(to, cc, bcc, smopts, subject,
    416 		    mime_attach_optargs(attach_optargs));
    417 		/*
    418 		 * why wait?
    419 		 */
    420 		exit(senderr);
    421 		break;	/* XXX - keep lint happy */
    422 
    423 	case mm_receiving:
    424 	case mm_hdrsonly:
    425 		/*
    426 		 * Ok, we are reading mail.
    427 		 * Decide whether we are editing a mailbox or reading
    428 		 * the system mailbox, and open up the right stuff.
    429 		 */
    430 		if (ef == NULL)
    431 			ef = "%";
    432 		if (setfile(ef) < 0)
    433 			exit(1);		/* error already reported */
    434 		if (setjmp(hdrjmp) == 0) {
    435 			if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
    436 				(void)signal(SIGINT, hdrstop);
    437 			if (value(ENAME_QUIET) == NULL)
    438 				(void)printf("Mail version %s.  Type ? for help.\n",
    439 				    version);
    440 			if (mailmode == mm_hdrsonly)
    441 				show_headers_and_exit(Hflag);	/* NORETURN */
    442 			announce();
    443 			(void)fflush(stdout);
    444 			(void)signal(SIGINT, prevint);
    445 		}
    446 		commands();
    447 		(void)signal(SIGHUP, SIG_IGN);
    448 		(void)signal(SIGINT, SIG_IGN);
    449 		(void)signal(SIGQUIT, SIG_IGN);
    450 		quit();
    451 		break;
    452 
    453 	default:
    454 		assert(/*CONSTCOND*/0);
    455 		break;
    456 	}
    457 
    458 	return 0;
    459 }
    460