1 /* $NetBSD: main.c,v 1.31 2010/01/12 14:45:31 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\ 35 The Regents of the University of California. All rights reserved."); 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.31 2010/01/12 14:45:31 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 #include "sig.h" 54 55 #ifdef USE_EDITLINE 56 #include "complete.h" 57 #endif 58 #include "format.h" 59 #ifdef MIME_SUPPORT 60 #include "mime.h" 61 #endif 62 #ifdef THREAD_SUPPORT 63 #include "thread.h" 64 #endif 65 66 /* 67 * Mail -- a mail program 68 * 69 * Startup -- interface with user. 70 */ 71 72 __dead 73 static void 74 usage(void) 75 { 76 #ifdef MIME_SUPPORT 77 (void)fputs("\ 78 Usage: mail [-EiInv] [-r rcfile] [-s subject] [-a file] [-c cc-addr]\n\ 79 [-b bcc-addr] to-addr ... [- sendmail-options ...]\n\ 80 mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\ 81 mail [-EiInNv] [-H[colon-modifier]] [-u user]\n", 82 stderr); 83 #else /* MIME_SUPPORT */ 84 (void)fputs("\ 85 Usage: mail [-EiInv] [-r rcfile] [-s subject] [-c cc-addr] [-b bcc-addr]\n\ 86 to-addr ... [- sendmail-options ...]\n\ 87 mail [-EiInNv] [-H[colon-modifier]] -f [name]\n\ 88 mail [-EiInNv] [-H[colon-modifier]] [-u user]\n", 89 stderr); 90 #endif /* MIME_SUPPORT */ 91 exit(1); 92 } 93 94 /* 95 * Compute what the screen size for printing headers should be. 96 * We use the following algorithm for the height: 97 * If baud rate < 1200, use 9 98 * If baud rate = 1200, use 14 99 * If baud rate > 1200, use 24 or ws_row 100 * Width is either 80 or ws_col; 101 */ 102 PUBLIC void 103 setscreensize(void) 104 { 105 struct termios tbuf; 106 struct winsize ws; 107 speed_t ospeed; 108 char *cp; 109 110 if (ioctl(1, TIOCGWINSZ, &ws) < 0) 111 ws.ws_col = ws.ws_row = 0; 112 if (tcgetattr(1, &tbuf) < 0) 113 ospeed = 9600; 114 else 115 ospeed = cfgetospeed(&tbuf); 116 if (ospeed < 1200) 117 screenheight = 9; 118 else if (ospeed == 1200) 119 screenheight = 14; 120 else if (ws.ws_row != 0) 121 screenheight = ws.ws_row; 122 else 123 screenheight = 24; 124 if ((realscreenheight = ws.ws_row) == 0) 125 realscreenheight = 24; 126 if ((screenwidth = ws.ws_col) == 0) 127 screenwidth = 80; 128 /* 129 * Possible overrides from the rcfile. 130 */ 131 if ((cp = value(ENAME_SCREENWIDTH)) != NULL) { 132 int width; 133 width = *cp ? atoi(cp) : 0; 134 if (width >= 0) 135 screenwidth = width; 136 } 137 if ((cp = value(ENAME_SCREENHEIGHT)) != NULL) { 138 int height; 139 height = *cp ? atoi(cp) : 0; 140 if (height >= 0) { 141 realscreenheight = height; 142 screenheight = height; 143 } 144 } 145 } 146 147 /* 148 * Break up a white-space or comma delimited name list so that aliases 149 * can get expanded. Without this, the CC: or BCC: list is broken too 150 * late for alias expansion to occur. 151 */ 152 PUBLIC struct name * 153 lexpand(char *str, int ntype) 154 { 155 char *list; 156 struct name *np = NULL; 157 char *word, *p; 158 159 list = estrdup(str); 160 word = list; 161 for (word = list; *word; word = p) { 162 word = skip_WSP(word); 163 for (p = word; 164 *p && !is_WSP(*p) && *p != ','; 165 p++) 166 continue; 167 if (*p) 168 *p++ = '\0'; 169 np = cat(np, nalloc(word, ntype)); 170 } 171 172 free(list); 173 return np; 174 } 175 176 PUBLIC int 177 main(int argc, char *argv[]) 178 { 179 jmp_buf jmpbuf; 180 struct sigaction sa; 181 struct name *to, *cc, *bcc, *smopts; 182 #ifdef MIME_SUPPORT 183 struct name *attach_optargs; 184 struct name *attach_end; 185 #endif 186 char *subject; 187 const char *ef; 188 char nosrc = 0; 189 const char *rc; 190 int Hflag; 191 int i; 192 193 /* 194 * For portability, call setprogname() early, before 195 * getprogname() is called. 196 */ 197 (void)setprogname(argv[0]); 198 199 /* 200 * Set up a reasonable environment. 201 * Figure out whether we are being run interactively, 202 * start the SIGCHLD catcher, and so forth. 203 * (Other signals are setup later by sig_setup().) 204 */ 205 (void)sigemptyset(&sa.sa_mask); 206 sa.sa_flags = SA_RESTART; 207 sa.sa_handler = sigchild; 208 (void)sigaction(SIGCHLD, &sa, NULL); 209 210 if (isatty(0)) 211 assign(ENAME_INTERACTIVE, ""); 212 image = -1; 213 214 /* 215 * Now, determine how we are being used. 216 * We successively pick off - flags. 217 * If there is anything left, it is the base of the list 218 * of users to mail to. Argp will be set to point to the 219 * first of these users. 220 */ 221 rc = NULL; 222 ef = NULL; 223 to = NULL; 224 cc = NULL; 225 bcc = NULL; 226 smopts = NULL; 227 subject = NULL; 228 Hflag = 0; 229 #ifdef MIME_SUPPORT 230 attach_optargs = NULL; 231 attach_end = NULL; 232 while ((i = getopt(argc, argv, ":~EH:INT:a:b:c:dfinr:s:u:v")) != -1) 233 #else 234 while ((i = getopt(argc, argv, ":~EH:INT:b:c:dfinr:s:u:v")) != -1) 235 #endif 236 { 237 switch (i) { 238 case 'T': 239 /* 240 * Next argument is temp file to write which 241 * articles have been read/deleted for netnews. 242 */ 243 Tflag = optarg; 244 if ((i = creat(Tflag, 0600)) < 0) { 245 warn("%s", Tflag); 246 exit(1); 247 } 248 (void)close(i); 249 break; 250 #ifdef MIME_SUPPORT 251 case 'a': { 252 struct name *np; 253 np = nalloc(optarg, 0); 254 if (attach_end == NULL) 255 attach_optargs = np; 256 else { 257 np->n_blink = attach_end; 258 attach_end->n_flink = np; 259 } 260 attach_end = np; 261 break; 262 } 263 #endif 264 case 'u': 265 /* 266 * Next argument is person to pretend to be. 267 */ 268 myname = optarg; 269 (void)unsetenv("MAIL"); 270 break; 271 case 'i': 272 /* 273 * User wants to ignore interrupts. 274 * Set the variable "ignore" 275 */ 276 assign(ENAME_IGNORE, ""); 277 break; 278 case 'd': 279 debug++; 280 break; 281 case 'r': 282 rc = optarg; 283 break; 284 case 's': 285 /* 286 * Give a subject field for sending from 287 * non terminal 288 */ 289 subject = optarg; 290 break; 291 case 'f': 292 /* 293 * User is specifying file to "edit" with Mail, 294 * as opposed to reading system mailbox. 295 * If no argument is given after -f, we read his 296 * mbox file. 297 * 298 * getopt() can't handle optional arguments, so here 299 * is an ugly hack to get around it. 300 */ 301 if ((argv[optind]) && (argv[optind][0] != '-')) 302 ef = argv[optind++]; 303 else 304 ef = "&"; 305 break; 306 case 'H': 307 /* 308 * Print out the headers and quit. 309 */ 310 Hflag = get_Hflag(argv); 311 break; 312 case 'n': 313 /* 314 * User doesn't want to source /usr/lib/Mail.rc 315 */ 316 nosrc++; 317 break; 318 case 'N': 319 /* 320 * Avoid initial header printing. 321 */ 322 assign(ENAME_NOHEADER, ""); 323 break; 324 case 'v': 325 /* 326 * Send mailer verbose flag 327 */ 328 assign(ENAME_VERBOSE, ""); 329 break; 330 case 'I': 331 case '~': 332 /* 333 * We're interactive 334 */ 335 assign(ENAME_INTERACTIVE, ""); 336 break; 337 case 'c': 338 /* 339 * Get Carbon Copy Recipient list 340 */ 341 cc = cat(cc, lexpand(optarg, GCC)); 342 break; 343 case 'b': 344 /* 345 * Get Blind Carbon Copy Recipient list 346 */ 347 bcc = cat(bcc, lexpand(optarg, GBCC)); 348 349 break; 350 case 'E': 351 /* 352 * Don't send empty files. 353 */ 354 assign(ENAME_DONTSENDEMPTY, ""); 355 break; 356 case ':': 357 /* 358 * An optarg was expected but not found. 359 */ 360 if (optopt == 'H') { 361 Hflag = get_Hflag(NULL); 362 break; 363 } 364 (void)fprintf(stderr, 365 "%s: option requires an argument -- %c\n", 366 getprogname(), optopt); 367 368 /* FALLTHROUGH */ 369 case '?': 370 /* 371 * An unknown option flag. We need to do the 372 * error message. 373 */ 374 if (optopt != '?') 375 (void)fprintf(stderr, 376 "%s: unknown option -- %c\n", getprogname(), 377 optopt); 378 usage(); /* print usage message and die */ 379 /*NOTREACHED*/ 380 } 381 } 382 for (i = optind; (argv[i]) && (*argv[i] != '-'); i++) 383 to = cat(to, nalloc(argv[i], GTO)); 384 for (/*EMPTY*/; argv[i]; i++) 385 smopts = cat(smopts, nalloc(argv[i], GSMOPTS)); 386 /* 387 * Check for inconsistent arguments. 388 */ 389 if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL)) 390 errx(EXIT_FAILURE, "You must specify direct recipients with -s, -c, or -b."); 391 if (ef != NULL && to != NULL) { 392 errx(EXIT_FAILURE, "Cannot give -f and people to send to."); 393 } 394 if (Hflag != 0 && to != NULL) 395 errx(EXIT_FAILURE, "Cannot give -H and people to send to."); 396 #ifdef MIME_SUPPORT 397 if (attach_optargs != NULL && to == NULL) 398 errx(EXIT_FAILURE, "Cannot give -a without people to send to."); 399 #endif 400 tinit(); /* must be done before loading the rcfile */ 401 input = stdin; 402 mailmode = Hflag ? mm_hdrsonly : 403 to ? mm_sending : mm_receiving; 404 405 spreserve(); 406 if (!nosrc) 407 load(_PATH_MASTER_RC); 408 /* 409 * Expand returns a savestr, but load only uses the file name 410 * for fopen, so it's safe to do this. 411 */ 412 if (rc == NULL && (rc = getenv("MAILRC")) == NULL) 413 rc = "~/.mailrc"; 414 load(expand(rc)); 415 setscreensize(); /* do this after loading the rcfile */ 416 417 #ifdef USE_EDITLINE 418 /* 419 * This is after loading the MAILRC so we can use value(). 420 * Avoid editline in mm_hdrsonly mode or pipelines will screw 421 * up. XXX - there must be a better way! 422 */ 423 if (mailmode != mm_hdrsonly) 424 init_editline(); 425 #endif 426 427 sig_setup(); 428 429 switch (mailmode) { 430 case mm_sending: 431 (void)mail(to, cc, bcc, smopts, subject, 432 mime_attach_optargs(attach_optargs)); 433 /* 434 * why wait? 435 */ 436 exit(senderr); 437 break; /* XXX - keep lint happy */ 438 439 case mm_receiving: 440 case mm_hdrsonly: 441 /* 442 * Ok, we are reading mail. 443 * Decide whether we are editing a mailbox or reading 444 * the system mailbox, and open up the right stuff. 445 */ 446 if (ef == NULL) 447 ef = "%"; 448 if (setfile(ef) < 0) 449 exit(1); /* error already reported */ 450 if (value(ENAME_QUIET) == NULL) 451 (void)printf("Mail version %s. Type ? for help.\n", 452 version); 453 if (mailmode == mm_hdrsonly) 454 show_headers_and_exit(Hflag); /* NORETURN */ 455 announce(); 456 (void)fflush(stdout); 457 458 if (setjmp(jmpbuf) != 0) { 459 /* Return here if quit() fails below. */ 460 (void)printf("Use 'exit' to quit without saving changes.\n"); 461 } 462 commands(); 463 464 /* Ignore these signals from now on! */ 465 (void)signal(SIGHUP, SIG_IGN); 466 (void)signal(SIGINT, SIG_IGN); 467 (void)signal(SIGQUIT, SIG_IGN); 468 quit(jmpbuf); 469 break; 470 471 default: 472 assert(/*CONSTCOND*/0); 473 break; 474 } 475 476 return 0; 477 } 478