1 1.49 christos /* $NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $ */ 2 1.6 christos 3 1.1 cgd /* 4 1.3 deraadt * Copyright (c) 1980, 1993 5 1.3 deraadt * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.30 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.11 lukem #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.6 christos #if 0 35 1.6 christos static char sccsid[] = "@(#)collect.c 8.2 (Berkeley) 4/19/94"; 36 1.6 christos #else 37 1.49 christos __RCSID("$NetBSD: collect.c,v 1.49 2017/11/09 20:27:50 christos Exp $"); 38 1.6 christos #endif 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd /* 42 1.1 cgd * Mail -- a mail program 43 1.1 cgd * 44 1.1 cgd * Collect input from standard input, handling 45 1.1 cgd * ~ escapes. 46 1.1 cgd */ 47 1.1 cgd 48 1.36 christos #include <assert.h> 49 1.36 christos #include <util.h> 50 1.36 christos 51 1.1 cgd #include "rcv.h" 52 1.3 deraadt #include "extern.h" 53 1.36 christos #include "format.h" 54 1.35 christos #ifdef MIME_SUPPORT 55 1.35 christos #include "mime.h" 56 1.35 christos #endif 57 1.43 christos #include "sig.h" 58 1.37 christos #include "thread.h" 59 1.1 cgd 60 1.21 christos 61 1.1 cgd /* 62 1.29 wiz * Read a message from standard input and return a read file to it 63 1.1 cgd * or NULL on error. 64 1.1 cgd */ 65 1.1 cgd 66 1.1 cgd /* 67 1.1 cgd * The following hokiness with global variables is so that on 68 1.1 cgd * receipt of an interrupt signal, the partial message can be salted 69 1.1 cgd * away on dead.letter. 70 1.1 cgd */ 71 1.1 cgd static FILE *collf; /* File for saving away */ 72 1.1 cgd static int hadintr; /* Have seen one SIGINT so far */ 73 1.1 cgd 74 1.43 christos static jmp_buf abort_jmpbuf; /* To end collection with error */ 75 1.43 christos static jmp_buf reset_jmpbuf; /* To get back to work */ 76 1.43 christos static int reset_on_stop; /* To do job control longjmp. */ 77 1.33 christos 78 1.37 christos /* 79 1.37 christos * Write a file, ex-like if f set. 80 1.37 christos */ 81 1.37 christos static int 82 1.37 christos exwrite(const char name[], FILE *fp, int f) 83 1.37 christos { 84 1.37 christos FILE *of; 85 1.37 christos int c; 86 1.37 christos long cc; 87 1.37 christos int lc; 88 1.37 christos struct stat junk; 89 1.37 christos 90 1.37 christos if (f) { 91 1.37 christos (void)printf("\"%s\" ", name); 92 1.37 christos (void)fflush(stdout); 93 1.37 christos } 94 1.37 christos if (stat(name, &junk) >= 0 && S_ISREG(junk.st_mode)) { 95 1.37 christos if (!f) 96 1.37 christos (void)fprintf(stderr, "%s: ", name); 97 1.37 christos (void)fprintf(stderr, "File exists\n"); 98 1.37 christos return -1; 99 1.37 christos } 100 1.45 christos if ((of = Fopen(name, "we")) == NULL) { 101 1.37 christos warn("%s", name); 102 1.37 christos return -1; 103 1.37 christos } 104 1.37 christos lc = 0; 105 1.37 christos cc = 0; 106 1.37 christos while ((c = getc(fp)) != EOF) { 107 1.37 christos cc++; 108 1.37 christos if (c == '\n') 109 1.37 christos lc++; 110 1.37 christos (void)putc(c, of); 111 1.37 christos if (ferror(of)) { 112 1.37 christos warn("%s", name); 113 1.37 christos (void)Fclose(of); 114 1.37 christos return -1; 115 1.37 christos } 116 1.37 christos } 117 1.37 christos (void)Fclose(of); 118 1.37 christos (void)printf("%d/%ld\n", lc, cc); 119 1.37 christos (void)fflush(stdout); 120 1.37 christos return 0; 121 1.37 christos } 122 1.37 christos 123 1.37 christos /* 124 1.37 christos * Edit the message being collected on fp. 125 1.37 christos * On return, make the edit file the new temp file. 126 1.37 christos */ 127 1.37 christos static void 128 1.37 christos mesedit(FILE *fp, int c) 129 1.37 christos { 130 1.43 christos struct sigaction osa; 131 1.43 christos sigset_t oset; 132 1.43 christos FILE *nf; 133 1.37 christos 134 1.43 christos sig_check(); 135 1.43 christos (void)sig_ignore(SIGINT, &osa, &oset); 136 1.43 christos nf = run_editor(fp, (off_t)-1, c, 0); 137 1.37 christos if (nf != NULL) { 138 1.37 christos (void)fseek(nf, 0L, 2); 139 1.37 christos collf = nf; 140 1.37 christos (void)Fclose(fp); 141 1.37 christos } 142 1.43 christos (void)sig_restore(SIGINT, &osa, &oset); 143 1.43 christos sig_check(); 144 1.37 christos } 145 1.37 christos 146 1.37 christos /* 147 1.37 christos * Pipe the message through the command. 148 1.37 christos * Old message is on stdin of command; 149 1.37 christos * New message collected from stdout. 150 1.37 christos * Sh -c must return 0 to accept the new message. 151 1.37 christos */ 152 1.37 christos static void 153 1.37 christos mespipe(FILE *fp, char cmd[]) 154 1.37 christos { 155 1.37 christos FILE *nf; 156 1.43 christos struct sigaction osa; 157 1.43 christos sigset_t oset; 158 1.37 christos const char *shellcmd; 159 1.37 christos int fd; 160 1.37 christos char tempname[PATHSIZE]; 161 1.37 christos 162 1.43 christos sig_check(); 163 1.43 christos (void)sig_ignore(SIGINT, &osa, &oset); 164 1.43 christos 165 1.37 christos (void)snprintf(tempname, sizeof(tempname), 166 1.37 christos "%s/mail.ReXXXXXXXXXX", tmpdir); 167 1.37 christos if ((fd = mkstemp(tempname)) == -1 || 168 1.49 christos (nf = Fdopen(fd, "wef+")) == NULL) { 169 1.37 christos if (fd != -1) 170 1.37 christos (void)close(fd); 171 1.37 christos warn("%s", tempname); 172 1.37 christos goto out; 173 1.37 christos } 174 1.37 christos (void)unlink(tempname); 175 1.37 christos /* 176 1.37 christos * stdin = current message. 177 1.37 christos * stdout = new message. 178 1.37 christos */ 179 1.37 christos if ((shellcmd = value(ENAME_SHELL)) == NULL) 180 1.37 christos shellcmd = _PATH_CSHELL; 181 1.37 christos if (run_command(shellcmd, 182 1.43 christos NULL, fileno(fp), fileno(nf), "-c", cmd, NULL) < 0) { 183 1.37 christos (void)Fclose(nf); 184 1.37 christos goto out; 185 1.37 christos } 186 1.37 christos if (fsize(nf) == 0) { 187 1.37 christos (void)fprintf(stderr, "No bytes from \"%s\" !?\n", cmd); 188 1.37 christos (void)Fclose(nf); 189 1.37 christos goto out; 190 1.37 christos } 191 1.37 christos /* 192 1.37 christos * Take new files. 193 1.37 christos */ 194 1.37 christos (void)fseek(nf, 0L, 2); 195 1.37 christos collf = nf; 196 1.37 christos (void)Fclose(fp); 197 1.43 christos out: 198 1.43 christos (void)sig_restore(SIGINT, &osa, &oset); 199 1.43 christos sig_check(); 200 1.37 christos } 201 1.37 christos 202 1.37 christos /* 203 1.37 christos * Interpolate the named messages into the current 204 1.37 christos * message, preceding each line with a tab. 205 1.37 christos * Return a count of the number of characters now in 206 1.37 christos * the message, or -1 if an error is encountered writing 207 1.37 christos * the message temporary. The flag argument is 'm' if we 208 1.37 christos * should shift over and 'f' if not. 209 1.37 christos */ 210 1.37 christos static int 211 1.41 christos interpolate(char ms[], FILE *fp, char *fn, int f) 212 1.37 christos { 213 1.37 christos int *msgvec; 214 1.37 christos struct ignoretab *ig; 215 1.37 christos const char *tabst; 216 1.37 christos #ifdef MIME_SUPPORT 217 1.37 christos struct mime_info *mip; 218 1.37 christos int retval; 219 1.37 christos #endif 220 1.42 christos msgvec = salloc((get_msgCount() + 1) * sizeof(*msgvec)); 221 1.37 christos if (msgvec == NULL) 222 1.37 christos return 0; 223 1.37 christos if (getmsglist(ms, msgvec, 0) < 0) 224 1.37 christos return 0; 225 1.37 christos if (*msgvec == 0) { 226 1.37 christos *msgvec = first(0, MMNORM); 227 1.37 christos if (*msgvec == 0) { 228 1.37 christos (void)printf("No appropriate messages\n"); 229 1.37 christos return 0; 230 1.37 christos } 231 1.37 christos msgvec[1] = 0; 232 1.37 christos } 233 1.37 christos if (f == 'f' || f == 'F') 234 1.37 christos tabst = NULL; 235 1.37 christos else if ((tabst = value(ENAME_INDENTPREFIX)) == NULL) 236 1.37 christos tabst = "\t"; 237 1.37 christos ig = isupper(f) ? NULL : ignore; 238 1.37 christos (void)printf("Interpolating:"); 239 1.40 christos for (/*EMPTY*/; *msgvec != 0; msgvec++) { 240 1.37 christos struct message *mp; 241 1.37 christos char *fmtstr; 242 1.37 christos 243 1.37 christos mp = get_message(*msgvec); 244 1.37 christos touch(mp); 245 1.37 christos (void)printf(" %d", *msgvec); 246 1.37 christos (void)fflush(stdout); /* flush stdout and the above */ 247 1.37 christos 248 1.37 christos if (tabst && (fmtstr = value(ENAME_INDENT_PREAMBLE)) != NULL) 249 1.37 christos fmsgprintf(collf, fmtstr, mp); 250 1.37 christos #ifdef MIME_SUPPORT 251 1.37 christos mip = NULL; 252 1.37 christos if (value(ENAME_MIME_DECODE_MSG)) { 253 1.37 christos if ((tabst == NULL && value(ENAME_MIME_DECODE_INSERT)) || 254 1.37 christos (tabst != NULL && value(ENAME_MIME_DECODE_QUOTE))) 255 1.37 christos mip = mime_decode_open(mp); 256 1.37 christos } 257 1.37 christos retval = mime_sendmessage(mp, fp, ig, tabst, mip); 258 1.37 christos mime_decode_close(mip); 259 1.37 christos if (retval < 0) 260 1.37 christos #else 261 1.37 christos if (sendmessage(mp, fp, ig, tabst, NULL) < 0) 262 1.37 christos #endif 263 1.37 christos { 264 1.37 christos warn("%s", fn); 265 1.37 christos return -1; 266 1.37 christos } 267 1.37 christos if (tabst && (fmtstr = value(ENAME_INDENT_POSTSCRIPT)) != NULL) 268 1.37 christos fmsgprintf(collf, fmtstr, mp); 269 1.37 christos } 270 1.37 christos (void)printf("\n"); 271 1.37 christos return 0; 272 1.37 christos } 273 1.37 christos 274 1.37 christos /* 275 1.37 christos * Append the contents of the file to the end of the deadletter file. 276 1.37 christos */ 277 1.37 christos PUBLIC void 278 1.37 christos savedeadletter(FILE *fp) 279 1.37 christos { 280 1.37 christos FILE *dbuf; 281 1.37 christos mode_t m; 282 1.37 christos int c; 283 1.37 christos const char *cp; 284 1.37 christos 285 1.37 christos if (fsize(fp) == 0) 286 1.37 christos return; 287 1.37 christos cp = getdeadletter(); 288 1.37 christos m = umask(077); 289 1.45 christos dbuf = Fopen(cp, "ae"); 290 1.37 christos (void)umask(m); 291 1.37 christos if (dbuf == NULL) 292 1.37 christos return; 293 1.43 christos (void)printf("Saving message body to `%s'.\n", cp); 294 1.37 christos while ((c = getc(fp)) != EOF) 295 1.37 christos (void)putc(c, dbuf); 296 1.37 christos (void)Fclose(dbuf); 297 1.37 christos rewind(fp); 298 1.37 christos } 299 1.37 christos 300 1.37 christos /* 301 1.37 christos * On interrupt, come here to save the partial message in ~/dead.letter. 302 1.37 christos * Then jump out of the collection loop. 303 1.37 christos */ 304 1.37 christos static void 305 1.43 christos coll_int(int signo) 306 1.37 christos { 307 1.47 christos sig_t o = signal(SIGINT, SIG_IGN); 308 1.43 christos 309 1.37 christos /* 310 1.37 christos * the control flow is subtle, because we can be called from ~q. 311 1.37 christos */ 312 1.37 christos if (!hadintr) { 313 1.37 christos if (value(ENAME_IGNORE) != NULL) { 314 1.37 christos (void)puts("@"); 315 1.37 christos (void)fflush(stdout); 316 1.37 christos clearerr(stdin); 317 1.48 christos signal(SIGINT, o); 318 1.37 christos return; 319 1.37 christos } 320 1.37 christos hadintr = 1; 321 1.48 christos signal(SIGINT, o); 322 1.43 christos longjmp(reset_jmpbuf, signo); 323 1.37 christos } 324 1.47 christos if (collf) { 325 1.47 christos rewind(collf); 326 1.47 christos if (value(ENAME_NOSAVE) == NULL) 327 1.47 christos savedeadletter(collf); 328 1.47 christos } 329 1.47 christos signal(SIGINT, o); 330 1.43 christos longjmp(abort_jmpbuf, signo); 331 1.37 christos } 332 1.37 christos 333 1.37 christos /*ARGSUSED*/ 334 1.44 joerg __dead static void 335 1.43 christos coll_hup(int signo __unused) 336 1.37 christos { 337 1.43 christos 338 1.37 christos rewind(collf); 339 1.37 christos savedeadletter(collf); 340 1.37 christos /* 341 1.37 christos * Let's pretend nobody else wants to clean up, 342 1.37 christos * a true statement at this time. 343 1.37 christos */ 344 1.43 christos exit(EXIT_FAILURE); 345 1.43 christos } 346 1.43 christos 347 1.43 christos /* 348 1.43 christos * Print (continue) when continued after ^Z. 349 1.43 christos */ 350 1.43 christos static void 351 1.43 christos coll_stop(int signo) 352 1.43 christos { 353 1.43 christos 354 1.43 christos if (reset_on_stop) { 355 1.43 christos reset_on_stop = 0; 356 1.43 christos hadintr = 0; 357 1.43 christos longjmp(reset_jmpbuf, signo); 358 1.43 christos } 359 1.37 christos } 360 1.37 christos 361 1.37 christos PUBLIC FILE * 362 1.22 wiz collect(struct header *hp, int printheaders) 363 1.1 cgd { 364 1.46 christos sig_t volatile old_sigint = sig_current(SIGINT); 365 1.46 christos sig_t volatile old_sighup = sig_current(SIGHUP); 366 1.46 christos sig_t volatile old_sigtstp = sig_current(SIGTSTP); 367 1.46 christos sig_t volatile old_sigttin = sig_current(SIGTTIN); 368 1.46 christos sig_t volatile old_sigttou = sig_current(SIGTTOU); 369 1.1 cgd FILE *fbuf; 370 1.34 christos int lc, cc; 371 1.27 wiz int c, fd, t; 372 1.31 christos char linebuf[LINESIZE]; 373 1.31 christos const char *cp; 374 1.27 wiz char tempname[PATHSIZE]; 375 1.28 wiz char mailtempname[PATHSIZE]; 376 1.35 christos int eofcount; 377 1.35 christos int longline; 378 1.43 christos int lastlong, rc; /* So we don't make 2 or more lines 379 1.43 christos out of a long input line. */ 380 1.34 christos 381 1.34 christos /* The following are declared volatile to avoid longjmp clobbering. */ 382 1.35 christos char volatile getsub; 383 1.35 christos int volatile escape; 384 1.1 cgd 385 1.32 christos (void)memset(mailtempname, 0, sizeof(mailtempname)); 386 1.1 cgd collf = NULL; 387 1.43 christos 388 1.43 christos if (setjmp(abort_jmpbuf) || setjmp(reset_jmpbuf)) { 389 1.28 wiz (void)rm(mailtempname); 390 1.1 cgd goto err; 391 1.1 cgd } 392 1.43 christos sig_check(); 393 1.43 christos 394 1.43 christos sig_hold(); 395 1.43 christos old_sigint = sig_signal(SIGINT, coll_int); 396 1.43 christos old_sighup = sig_signal(SIGHUP, coll_hup); 397 1.43 christos old_sigtstp = sig_signal(SIGTSTP, coll_stop); 398 1.43 christos old_sigttin = sig_signal(SIGTTIN, coll_stop); 399 1.43 christos old_sigttou = sig_signal(SIGTTOU, coll_stop); 400 1.43 christos sig_release(); 401 1.1 cgd 402 1.1 cgd noreset++; 403 1.28 wiz (void)snprintf(mailtempname, sizeof(mailtempname), 404 1.28 wiz "%s/mail.RsXXXXXXXXXX", tmpdir); 405 1.28 wiz if ((fd = mkstemp(mailtempname)) == -1 || 406 1.49 christos (collf = Fdopen(fd, "wef+")) == NULL) { 407 1.28 wiz if (fd != -1) 408 1.32 christos (void)close(fd); 409 1.28 wiz warn("%s", mailtempname); 410 1.1 cgd goto err; 411 1.1 cgd } 412 1.28 wiz (void)rm(mailtempname); 413 1.1 cgd 414 1.1 cgd /* 415 1.1 cgd * If we are going to prompt for a subject, 416 1.1 cgd * refrain from printing a newline after 417 1.1 cgd * the headers (since some people mind). 418 1.1 cgd */ 419 1.43 christos t = GTO | GSUBJECT | GCC | GNL | GSMOPTS; 420 1.1 cgd getsub = 0; 421 1.37 christos if (hp->h_subject == NULL && value(ENAME_INTERACTIVE) != NULL && 422 1.43 christos (value(ENAME_ASK) != NULL || value(ENAME_ASKSUB) != NULL)) { 423 1.43 christos t &= ~GNL; 424 1.43 christos getsub++; 425 1.43 christos } 426 1.1 cgd if (printheaders) { 427 1.32 christos (void)puthead(hp, stdout, t); 428 1.32 christos (void)fflush(stdout); 429 1.1 cgd } 430 1.37 christos if ((cp = value(ENAME_ESCAPE)) != NULL) 431 1.1 cgd escape = *cp; 432 1.1 cgd else 433 1.1 cgd escape = ESCAPE; 434 1.43 christos hadintr = 0; 435 1.43 christos if (setjmp(reset_jmpbuf) == 0) { 436 1.1 cgd if (getsub) 437 1.32 christos (void)grabh(hp, GSUBJECT); 438 1.1 cgd } else { 439 1.1 cgd /* 440 1.1 cgd * Come here for printing the after-signal message. 441 1.1 cgd * Duplicate messages won't be printed because 442 1.1 cgd * the write is aborted if we get a SIGTTOU. 443 1.1 cgd */ 444 1.43 christos cont: 445 1.1 cgd if (hadintr) { 446 1.32 christos (void)fflush(stdout); 447 1.32 christos (void)fprintf(stderr, 448 1.1 cgd "\n(Interrupt -- one more to kill letter)\n"); 449 1.1 cgd } else { 450 1.32 christos (void)printf("(continue)\n"); 451 1.32 christos (void)fflush(stdout); 452 1.1 cgd } 453 1.1 cgd } 454 1.35 christos eofcount = 0; /* reset after possible longjmp */ 455 1.35 christos longline = 0; /* reset after possible longjmp */ 456 1.1 cgd for (;;) { 457 1.43 christos reset_on_stop = 1; 458 1.43 christos c = readline(stdin, linebuf, LINESIZE, reset_on_stop); 459 1.43 christos reset_on_stop = 0; 460 1.43 christos 461 1.33 christos if (c < 0) { 462 1.33 christos char *p; 463 1.43 christos 464 1.37 christos if (value(ENAME_INTERACTIVE) != NULL && 465 1.37 christos (p = value(ENAME_IGNOREEOF)) != NULL && 466 1.33 christos ++eofcount < (*p == 0 ? 25 : atoi(p))) { 467 1.33 christos (void)printf("Use \".\" to terminate letter\n"); 468 1.33 christos continue; 469 1.33 christos } 470 1.33 christos break; 471 1.33 christos } 472 1.8 phil lastlong = longline; 473 1.43 christos longline = c == LINESIZE - 1; 474 1.1 cgd eofcount = 0; 475 1.1 cgd hadintr = 0; 476 1.1 cgd if (linebuf[0] == '.' && linebuf[1] == '\0' && 477 1.37 christos value(ENAME_INTERACTIVE) != NULL && !lastlong && 478 1.37 christos (value(ENAME_DOT) != NULL || value(ENAME_IGNOREEOF) != NULL)) 479 1.1 cgd break; 480 1.37 christos if (linebuf[0] != escape || value(ENAME_INTERACTIVE) == NULL || 481 1.8 phil lastlong) { 482 1.8 phil if (putline(collf, linebuf, !longline) < 0) 483 1.1 cgd goto err; 484 1.1 cgd continue; 485 1.1 cgd } 486 1.1 cgd c = linebuf[1]; 487 1.1 cgd switch (c) { 488 1.1 cgd default: 489 1.1 cgd /* 490 1.1 cgd * On double escape, just send the single one. 491 1.1 cgd * Otherwise, it's an error. 492 1.1 cgd */ 493 1.1 cgd if (c == escape) { 494 1.8 phil if (putline(collf, &linebuf[1], !longline) < 0) 495 1.1 cgd goto err; 496 1.1 cgd else 497 1.1 cgd break; 498 1.1 cgd } 499 1.32 christos (void)printf("Unknown tilde escape.\n"); 500 1.1 cgd break; 501 1.35 christos #ifdef MIME_SUPPORT 502 1.35 christos case '@': 503 1.36 christos hp->h_attach = mime_attach_files(hp->h_attach, &linebuf[2]); 504 1.35 christos break; 505 1.35 christos #endif 506 1.1 cgd case 'C': 507 1.1 cgd /* 508 1.1 cgd * Dump core. 509 1.1 cgd */ 510 1.32 christos (void)core(NULL); 511 1.1 cgd break; 512 1.1 cgd case '!': 513 1.1 cgd /* 514 1.1 cgd * Shell escape, send the balance of the 515 1.1 cgd * line to sh -c. 516 1.1 cgd */ 517 1.32 christos (void)shell(&linebuf[2]); 518 1.1 cgd break; 519 1.1 cgd case ':': 520 1.5 jtc case '_': 521 1.1 cgd /* 522 1.1 cgd * Escape to command mode, but be nice! 523 1.1 cgd */ 524 1.38 christos (void)execute(&linebuf[2], ec_composing); 525 1.1 cgd goto cont; 526 1.1 cgd case '.': 527 1.1 cgd /* 528 1.1 cgd * Simulate end of file on input. 529 1.1 cgd */ 530 1.1 cgd goto out; 531 1.1 cgd case 'q': 532 1.1 cgd /* 533 1.1 cgd * Force a quit of sending mail. 534 1.1 cgd * Act like an interrupt happened. 535 1.1 cgd */ 536 1.1 cgd hadintr++; 537 1.43 christos coll_int(SIGINT); 538 1.1 cgd exit(1); 539 1.32 christos /*NOTREACHED*/ 540 1.19 mjl 541 1.19 mjl case 'x': /* exit, do not save in dead.letter */ 542 1.19 mjl goto err; 543 1.19 mjl 544 1.1 cgd case 'h': 545 1.1 cgd /* 546 1.1 cgd * Grab a bunch of headers. 547 1.1 cgd */ 548 1.43 christos (void)grabh(hp, GTO | GSUBJECT | GCC | GBCC | GSMOPTS); 549 1.1 cgd goto cont; 550 1.1 cgd case 't': 551 1.1 cgd /* 552 1.1 cgd * Add to the To list. 553 1.1 cgd */ 554 1.1 cgd hp->h_to = cat(hp->h_to, extract(&linebuf[2], GTO)); 555 1.1 cgd break; 556 1.1 cgd case 's': 557 1.1 cgd /* 558 1.1 cgd * Set the Subject list. 559 1.1 cgd */ 560 1.40 christos cp = skip_WSP(&linebuf[2]); 561 1.1 cgd hp->h_subject = savestr(cp); 562 1.1 cgd break; 563 1.1 cgd case 'c': 564 1.1 cgd /* 565 1.1 cgd * Add to the CC list. 566 1.1 cgd */ 567 1.1 cgd hp->h_cc = cat(hp->h_cc, extract(&linebuf[2], GCC)); 568 1.1 cgd break; 569 1.1 cgd case 'b': 570 1.1 cgd /* 571 1.1 cgd * Add stuff to blind carbon copies list. 572 1.1 cgd */ 573 1.1 cgd hp->h_bcc = cat(hp->h_bcc, extract(&linebuf[2], GBCC)); 574 1.1 cgd break; 575 1.19 mjl case 'i': 576 1.19 mjl case 'A': 577 1.19 mjl case 'a': 578 1.19 mjl /* 579 1.19 mjl * Insert named variable in message 580 1.19 mjl */ 581 1.19 mjl 582 1.19 mjl switch(c) { 583 1.40 christos case 'i': 584 1.40 christos cp = skip_WSP(&linebuf[2]); 585 1.19 mjl break; 586 1.19 mjl case 'a': 587 1.19 mjl cp = "sign"; 588 1.19 mjl break; 589 1.19 mjl case 'A': 590 1.19 mjl cp = "Sign"; 591 1.19 mjl break; 592 1.19 mjl default: 593 1.19 mjl goto err; 594 1.19 mjl } 595 1.19 mjl 596 1.37 christos if (*cp && (cp = value(cp)) != NULL) { 597 1.40 christos (void)printf("%s\n", cp); 598 1.37 christos if (putline(collf, cp, 1) < 0) 599 1.19 mjl goto err; 600 1.19 mjl } 601 1.19 mjl 602 1.19 mjl break; 603 1.19 mjl 604 1.1 cgd case 'd': 605 1.32 christos (void)strcpy(linebuf + 2, getdeadletter()); 606 1.32 christos /* FALLTHROUGH */ 607 1.1 cgd case 'r': 608 1.5 jtc case '<': 609 1.1 cgd /* 610 1.1 cgd * Invoke a file: 611 1.1 cgd * Search for the file name, 612 1.1 cgd * then open it and copy the contents to collf. 613 1.1 cgd */ 614 1.40 christos cp = skip_WSP(&linebuf[2]); 615 1.1 cgd if (*cp == '\0') { 616 1.32 christos (void)printf("Interpolate what file?\n"); 617 1.1 cgd break; 618 1.1 cgd } 619 1.19 mjl 620 1.1 cgd cp = expand(cp); 621 1.24 wiz if (cp == NULL) 622 1.1 cgd break; 623 1.19 mjl 624 1.19 mjl if (*cp == '!') { /* insert stdout of command */ 625 1.31 christos const char *shellcmd; 626 1.19 mjl int nullfd; 627 1.23 wiz int rc2; 628 1.19 mjl 629 1.37 christos if ((nullfd = open("/dev/null", O_RDONLY, 0)) == -1) { 630 1.26 wiz warn("/dev/null"); 631 1.19 mjl break; 632 1.19 mjl } 633 1.19 mjl 634 1.27 wiz (void)snprintf(tempname, sizeof(tempname), 635 1.28 wiz "%s/mail.ReXXXXXXXXXX", tmpdir); 636 1.27 wiz if ((fd = mkstemp(tempname)) == -1 || 637 1.49 christos (fbuf = Fdopen(fd, "wef+")) == NULL) { 638 1.27 wiz if (fd != -1) 639 1.32 christos (void)close(fd); 640 1.27 wiz warn("%s", tempname); 641 1.19 mjl break; 642 1.19 mjl } 643 1.27 wiz (void)unlink(tempname); 644 1.19 mjl 645 1.37 christos if ((shellcmd = value(ENAME_SHELL)) == NULL) 646 1.23 wiz shellcmd = _PATH_CSHELL; 647 1.19 mjl 648 1.43 christos rc2 = run_command(shellcmd, NULL, nullfd, fileno(fbuf), "-c", cp + 1, NULL); 649 1.19 mjl 650 1.32 christos (void)close(nullfd); 651 1.19 mjl 652 1.23 wiz if (rc2 < 0) { 653 1.25 wiz (void)Fclose(fbuf); 654 1.19 mjl break; 655 1.19 mjl } 656 1.19 mjl 657 1.19 mjl if (fsize(fbuf) == 0) { 658 1.35 christos (void)fprintf(stderr, "No bytes from command \"%s\"\n", cp + 1); 659 1.25 wiz (void)Fclose(fbuf); 660 1.19 mjl break; 661 1.19 mjl } 662 1.19 mjl 663 1.19 mjl rewind(fbuf); 664 1.19 mjl } 665 1.19 mjl else if (isdir(cp)) { 666 1.32 christos (void)printf("%s: Directory\n", cp); 667 1.1 cgd break; 668 1.1 cgd } 669 1.45 christos else if ((fbuf = Fopen(cp, "re")) == NULL) { 670 1.26 wiz warn("%s", cp); 671 1.1 cgd break; 672 1.1 cgd } 673 1.32 christos (void)printf("\"%s\" ", cp); 674 1.32 christos (void)fflush(stdout); 675 1.1 cgd lc = 0; 676 1.1 cgd cc = 0; 677 1.43 christos while ((rc = readline(fbuf, linebuf, LINESIZE, 0)) >= 0) { 678 1.8 phil if (rc != LINESIZE-1) lc++; 679 1.8 phil if ((t = putline(collf, linebuf, 680 1.8 phil rc != LINESIZE-1)) < 0) { 681 1.32 christos (void)Fclose(fbuf); 682 1.1 cgd goto err; 683 1.1 cgd } 684 1.1 cgd cc += t; 685 1.1 cgd } 686 1.32 christos (void)Fclose(fbuf); 687 1.32 christos (void)printf("%d/%d\n", lc, cc); 688 1.1 cgd break; 689 1.1 cgd case 'w': 690 1.1 cgd /* 691 1.1 cgd * Write the message on a file. 692 1.1 cgd */ 693 1.40 christos cp = skip_WSP(&linebuf[2]); 694 1.1 cgd if (*cp == '\0') { 695 1.32 christos (void)fprintf(stderr, "Write what file!?\n"); 696 1.1 cgd break; 697 1.1 cgd } 698 1.24 wiz if ((cp = expand(cp)) == NULL) 699 1.1 cgd break; 700 1.1 cgd rewind(collf); 701 1.32 christos (void)exwrite(cp, collf, 1); 702 1.1 cgd break; 703 1.1 cgd case 'm': 704 1.1 cgd case 'M': 705 1.1 cgd case 'f': 706 1.1 cgd case 'F': 707 1.1 cgd /* 708 1.1 cgd * Interpolate the named messages, if we 709 1.1 cgd * are in receiving mail mode. Does the 710 1.1 cgd * standard list processing garbage. 711 1.1 cgd * If ~f is given, we don't shift over. 712 1.1 cgd */ 713 1.41 christos if (interpolate(linebuf + 2, collf, mailtempname, c) < 0) 714 1.1 cgd goto err; 715 1.1 cgd goto cont; 716 1.1 cgd case '?': 717 1.39 christos cathelp(_PATH_TILDE); 718 1.1 cgd break; 719 1.1 cgd case 'p': 720 1.1 cgd /* 721 1.1 cgd * Print out the current state of the 722 1.1 cgd * message without altering anything. 723 1.1 cgd */ 724 1.1 cgd rewind(collf); 725 1.32 christos (void)printf("-------\nMessage contains:\n"); 726 1.43 christos (void)puthead(hp, stdout, 727 1.43 christos GTO | GSUBJECT | GCC | GBCC | GSMOPTS | GNL); 728 1.1 cgd while ((t = getc(collf)) != EOF) 729 1.25 wiz (void)putchar(t); 730 1.1 cgd goto cont; 731 1.1 cgd case '|': 732 1.1 cgd /* 733 1.1 cgd * Pipe message through command. 734 1.1 cgd * Collect output as new message. 735 1.1 cgd */ 736 1.1 cgd rewind(collf); 737 1.1 cgd mespipe(collf, &linebuf[2]); 738 1.1 cgd goto cont; 739 1.1 cgd case 'v': 740 1.1 cgd case 'e': 741 1.1 cgd /* 742 1.1 cgd * Edit the current message. 743 1.1 cgd * 'e' means to use EDITOR 744 1.1 cgd * 'v' means to use VISUAL 745 1.1 cgd */ 746 1.1 cgd rewind(collf); 747 1.1 cgd mesedit(collf, c); 748 1.1 cgd goto cont; 749 1.1 cgd } 750 1.1 cgd } 751 1.1 cgd goto out; 752 1.43 christos err: 753 1.1 cgd if (collf != NULL) { 754 1.32 christos (void)Fclose(collf); 755 1.1 cgd collf = NULL; 756 1.1 cgd } 757 1.43 christos out: 758 1.1 cgd if (collf != NULL) 759 1.1 cgd rewind(collf); 760 1.1 cgd noreset--; 761 1.43 christos 762 1.43 christos sig_hold(); 763 1.43 christos (void)sig_signal(SIGINT, old_sigint); 764 1.43 christos (void)sig_signal(SIGHUP, old_sighup); 765 1.43 christos (void)sig_signal(SIGTSTP, old_sigtstp); 766 1.43 christos (void)sig_signal(SIGTTIN, old_sigttin); 767 1.43 christos (void)sig_signal(SIGTTOU, old_sigttou); 768 1.43 christos sig_release(); 769 1.43 christos 770 1.43 christos sig_check(); 771 1.1 cgd return collf; 772 1.1 cgd } 773