/usr.bin/mail/collect.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 732 lines · 513 code · 37 blank · 182 comment · 155 complexity · 87698cce28782495e8a4db786faa88c7 MD5 · raw file

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