1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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 #ifndef lint 33 static const char copyright[] = 34 "@(#) 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 #endif /* not lint */ 40 #include <sys/cdefs.h> 41 #include "rcv.h" 42 #include <fcntl.h> 43 #include "extern.h" 44 45 /* 46 * Mail -- a mail program 47 * 48 * Startup -- interface with user. 49 */ 50 int msgCount; 51 int rcvmode; 52 int sawcom; 53 char *Tflag; 54 int senderr; 55 int edit; 56 int readonly; 57 int noreset; 58 int sourcing; 59 int loading; 60 int cond; 61 FILE *itf; 62 FILE *otf; 63 int image; 64 FILE *input; 65 char mailname[PATHSIZE]; 66 char prevfile[PATHSIZE]; 67 char *homedir; 68 char *myname; 69 off_t mailsize; 70 int lexnumber; 71 char lexstring[STRINGLEN]; 72 int regretp; 73 int regretstack[REGDEP]; 74 char *string_stack[REGDEP]; 75 int numberstack[REGDEP]; 76 struct message *dot; 77 struct message *message; 78 struct var *variables[HSHSIZE]; 79 struct grouphead *groups[HSHSIZE]; 80 struct ignoretab ignore[2]; 81 82 struct ignoretab saveignore[2]; 83 84 struct ignoretab ignoreall[2]; 85 char **altnames; 86 int debug; 87 int screenwidth; 88 int screenheight; 89 90 int realscreenheight; 91 92 jmp_buf srbuf; 93 94 static jmp_buf hdrjmp; 95 96 extern const char *version; 97 98 int 99 main(int argc, char *argv[]) 100 { 101 int i; 102 struct name *to, *cc, *bcc, *smopts; 103 char *subject, *replyto; 104 char *ef, *rc; 105 char nosrc = 0; 106 sig_t prevint; 107 108 /* 109 * Set up a reasonable environment. 110 * Figure out whether we are being run interactively, 111 * start the SIGCHLD catcher, and so forth. 112 */ 113 (void)signal(SIGCHLD, sigchild); 114 if (isatty(0)) 115 assign("interactive", ""); 116 image = -1; 117 /* 118 * Now, determine how we are being used. 119 * We successively pick off - flags. 120 * If there is anything left, it is the base of the list 121 * of users to mail to. Argp will be set to point to the 122 * first of these users. 123 */ 124 ef = NULL; 125 to = NULL; 126 cc = NULL; 127 bcc = NULL; 128 smopts = NULL; 129 subject = NULL; 130 while ((i = getopt(argc, argv, "FEHINT:b:c:edfins:u:v")) != -1) { 131 switch (i) { 132 case 'T': 133 /* 134 * Next argument is temp file to write which 135 * articles have been read/deleted for netnews. 136 */ 137 Tflag = optarg; 138 if ((i = open(Tflag, O_CREAT | O_TRUNC | O_WRONLY, 139 0600)) < 0) 140 err(1, "%s", Tflag); 141 (void)close(i); 142 break; 143 case 'u': 144 /* 145 * Next argument is person to pretend to be. 146 */ 147 myname = optarg; 148 unsetenv("MAIL"); 149 break; 150 case 'i': 151 /* 152 * User wants to ignore interrupts. 153 * Set the variable "ignore" 154 */ 155 assign("ignore", ""); 156 break; 157 case 'd': 158 debug++; 159 break; 160 case 'e': 161 /* 162 * User wants to check mail and exit. 163 */ 164 assign("checkmode", ""); 165 break; 166 case 'H': 167 /* 168 * User wants a header summary only. 169 */ 170 assign("headersummary", ""); 171 break; 172 case 'F': 173 /* 174 * User wants to record messages to files 175 * named after first recipient username. 176 */ 177 assign("recordrecip", ""); 178 break; 179 case 's': 180 /* 181 * Give a subject field for sending from 182 * non terminal 183 */ 184 subject = optarg; 185 break; 186 case 'f': 187 /* 188 * User is specifying file to "edit" with Mail, 189 * as opposed to reading system mailbox. 190 * If no argument is given after -f, we read his 191 * mbox file. 192 * 193 * getopt() can't handle optional arguments, so here 194 * is an ugly hack to get around it. 195 */ 196 if ((argv[optind] != NULL) && (argv[optind][0] != '-')) 197 ef = argv[optind++]; 198 else 199 ef = "&"; 200 break; 201 case 'n': 202 /* 203 * User doesn't want to source /usr/lib/Mail.rc 204 */ 205 nosrc++; 206 break; 207 case 'N': 208 /* 209 * Avoid initial header printing. 210 */ 211 assign("noheader", ""); 212 break; 213 case 'v': 214 /* 215 * Send mailer verbose flag 216 */ 217 assign("verbose", ""); 218 break; 219 case 'I': 220 /* 221 * We're interactive 222 */ 223 assign("interactive", ""); 224 break; 225 case 'c': 226 /* 227 * Get Carbon Copy Recipient list 228 */ 229 cc = cat(cc, nalloc(optarg, GCC)); 230 break; 231 case 'b': 232 /* 233 * Get Blind Carbon Copy Recipient list 234 */ 235 bcc = cat(bcc, nalloc(optarg, GBCC)); 236 break; 237 case 'E': 238 /* 239 * Don't send empty files. 240 */ 241 assign("dontsendempty", ""); 242 break; 243 case '?': 244 fprintf(stderr, "\ 245 Usage: %s [-dEiInv] [-s subject] [-c cc-addr] [-b bcc-addr] [-F] to-addr ...\n\ 246 %*s [-sendmail-option ...]\n\ 247 %s [-dEHiInNv] [-F] -f [name]\n\ 248 %s [-dEHiInNv] [-F] [-u user]\n\ 249 %s [-d] -e [-f name]\n", __progname, (int)strlen(__progname), "", 250 __progname, __progname, __progname); 251 exit(1); 252 } 253 } 254 for (i = optind; (argv[i] != NULL) && (*argv[i] != '-'); i++) 255 to = cat(to, nalloc(argv[i], GTO)); 256 for (; argv[i] != NULL; i++) 257 smopts = cat(smopts, nalloc(argv[i], 0)); 258 /* 259 * Check for inconsistent arguments. 260 */ 261 if (to == NULL && (subject != NULL || cc != NULL || bcc != NULL)) 262 errx(1, "You must specify direct recipients with -s, -c, or -b."); 263 if (ef != NULL && to != NULL) 264 errx(1, "Cannot give -f and people to send to."); 265 tinit(); 266 setscreensize(); 267 input = stdin; 268 rcvmode = !to; 269 spreserve(); 270 if (!nosrc) { 271 char *s, *path_rc; 272 273 if ((path_rc = malloc(sizeof(_PATH_MASTER_RC))) == NULL) 274 err(1, "malloc(path_rc) failed"); 275 276 strcpy(path_rc, _PATH_MASTER_RC); 277 while ((s = strsep(&path_rc, ":")) != NULL) 278 if (*s != '\0') 279 load(s); 280 } 281 /* 282 * Expand returns a savestr, but load only uses the file name 283 * for fopen, so it's safe to do this. 284 */ 285 if ((rc = getenv("MAILRC")) == NULL) 286 rc = "~/.mailrc"; 287 load(expand(rc)); 288 289 replyto = value("REPLYTO"); 290 if (!rcvmode) { 291 mail(to, cc, bcc, smopts, subject, replyto); 292 /* 293 * why wait? 294 */ 295 exit(senderr); 296 } 297 298 if(value("checkmode") != NULL) { 299 if (ef == NULL) 300 ef = "%"; 301 if (setfile(ef) <= 0) 302 /* Either an error has occurred, or no mail */ 303 exit(1); 304 else 305 exit(0); 306 /* NOTREACHED */ 307 } 308 309 /* 310 * Ok, we are reading mail. 311 * Decide whether we are editing a mailbox or reading 312 * the system mailbox, and open up the right stuff. 313 */ 314 if (ef == NULL) 315 ef = "%"; 316 if (setfile(ef) < 0) 317 exit(1); /* error already reported */ 318 if (setjmp(hdrjmp) == 0) { 319 if ((prevint = signal(SIGINT, SIG_IGN)) != SIG_IGN) 320 (void)signal(SIGINT, hdrstop); 321 if (value("quiet") == NULL) 322 printf("Mail version %s. Type ? for help.\n", 323 version); 324 announce(); 325 (void)fflush(stdout); 326 (void)signal(SIGINT, prevint); 327 } 328 329 /* If we were in header summary mode, it's time to exit. */ 330 if (value("headersummary") != NULL) 331 exit(0); 332 333 commands(); 334 (void)signal(SIGHUP, SIG_IGN); 335 (void)signal(SIGINT, SIG_IGN); 336 (void)signal(SIGQUIT, SIG_IGN); 337 quit(); 338 exit(0); 339 } 340 341 /* 342 * Interrupt printing of the headers. 343 */ 344 /*ARGSUSED*/ 345 void 346 hdrstop(int signo __unused) 347 { 348 349 (void)fflush(stdout); 350 fprintf(stderr, "\nInterrupt\n"); 351 longjmp(hdrjmp, 1); 352 } 353 354 /* 355 * Compute what the screen size for printing headers should be. 356 * We use the following algorithm for the height: 357 * If baud rate < 1200, use 9 358 * If baud rate = 1200, use 14 359 * If baud rate > 1200, use 24 or ws_row 360 * Width is either 80 or ws_col; 361 */ 362 void 363 setscreensize(void) 364 { 365 struct termios tbuf; 366 struct winsize ws; 367 speed_t speed; 368 369 if (ioctl(1, TIOCGWINSZ, (char *)&ws) < 0) 370 ws.ws_col = ws.ws_row = 0; 371 if (tcgetattr(1, &tbuf) < 0) 372 speed = B9600; 373 else 374 speed = cfgetospeed(&tbuf); 375 if (speed < B1200) 376 screenheight = 9; 377 else if (speed == B1200) 378 screenheight = 14; 379 else if (ws.ws_row != 0) 380 screenheight = ws.ws_row; 381 else 382 screenheight = 24; 383 if ((realscreenheight = ws.ws_row) == 0) 384 realscreenheight = 24; 385 if ((screenwidth = ws.ws_col) == 0) 386 screenwidth = 80; 387 } 388