PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/recvattach.c

https://bitbucket.org/mutt/mutt
C | 1294 lines | 1108 code | 136 blank | 50 comment | 322 complexity | 7efba9882b3eb93d7f7253315b5d8d52 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, LGPL-2.0, LGPL-2.1
  1. /*
  2. * Copyright (C) 1996-2000,2002,2007,2010 Michael R. Elkins <me@mutt.org>
  3. * Copyright (C) 1999-2006 Thomas Roessler <roessler@does-not-exist.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. */
  19. #if HAVE_CONFIG_H
  20. # include "config.h"
  21. #endif
  22. #include "mutt.h"
  23. #include "mutt_curses.h"
  24. #include "mutt_menu.h"
  25. #include "rfc1524.h"
  26. #include "mime.h"
  27. #include "mailbox.h"
  28. #include "attach.h"
  29. #include "mapping.h"
  30. #include "mx.h"
  31. #include "mutt_crypt.h"
  32. #include <ctype.h>
  33. #include <stdlib.h>
  34. #include <unistd.h>
  35. #include <sys/wait.h>
  36. #include <sys/stat.h>
  37. #include <string.h>
  38. #include <errno.h>
  39. static const char *Mailbox_is_read_only = N_("Mailbox is read-only.");
  40. #define CHECK_READONLY if (Context->readonly) \
  41. {\
  42. mutt_flushinp (); \
  43. mutt_error _(Mailbox_is_read_only); \
  44. break; \
  45. }
  46. static const struct mapping_t AttachHelp[] = {
  47. { N_("Exit"), OP_EXIT },
  48. { N_("Save"), OP_SAVE },
  49. { N_("Pipe"), OP_PIPE },
  50. { N_("Print"), OP_PRINT },
  51. { N_("Help"), OP_HELP },
  52. { NULL, 0 }
  53. };
  54. void mutt_update_tree (ATTACHPTR **idx, short idxlen)
  55. {
  56. char buf[STRING];
  57. char *s;
  58. int x;
  59. for (x = 0; x < idxlen; x++)
  60. {
  61. idx[x]->num = x;
  62. if (2 * (idx[x]->level + 2) < sizeof (buf))
  63. {
  64. if (idx[x]->level)
  65. {
  66. s = buf + 2 * (idx[x]->level - 1);
  67. *s++ = (idx[x]->content->next) ? MUTT_TREE_LTEE : MUTT_TREE_LLCORNER;
  68. *s++ = MUTT_TREE_HLINE;
  69. *s++ = MUTT_TREE_RARROW;
  70. }
  71. else
  72. s = buf;
  73. *s = 0;
  74. }
  75. if (idx[x]->tree)
  76. {
  77. if (mutt_strcmp (idx[x]->tree, buf) != 0)
  78. mutt_str_replace (&idx[x]->tree, buf);
  79. }
  80. else
  81. idx[x]->tree = safe_strdup (buf);
  82. if (2 * (idx[x]->level + 2) < sizeof (buf) && idx[x]->level)
  83. {
  84. s = buf + 2 * (idx[x]->level - 1);
  85. *s++ = (idx[x]->content->next) ? '\005' : '\006';
  86. *s++ = '\006';
  87. }
  88. }
  89. }
  90. ATTACHPTR **mutt_gen_attach_list (BODY *m,
  91. int parent_type,
  92. ATTACHPTR **idx,
  93. short *idxlen,
  94. short *idxmax,
  95. int level,
  96. int compose)
  97. {
  98. ATTACHPTR *new;
  99. int i;
  100. for (; m; m = m->next)
  101. {
  102. if (*idxlen == *idxmax)
  103. {
  104. safe_realloc (&idx, sizeof (ATTACHPTR *) * ((*idxmax) += 5));
  105. for (i = *idxlen; i < *idxmax; i++)
  106. idx[i] = NULL;
  107. }
  108. if (m->type == TYPEMULTIPART && m->parts
  109. && (compose || (parent_type == -1 && ascii_strcasecmp ("alternative", m->subtype)))
  110. && (!(WithCrypto & APPLICATION_PGP) || !mutt_is_multipart_encrypted(m))
  111. )
  112. {
  113. idx = mutt_gen_attach_list (m->parts, m->type, idx, idxlen, idxmax, level, compose);
  114. }
  115. else
  116. {
  117. if (!idx[*idxlen])
  118. idx[*idxlen] = (ATTACHPTR *) safe_calloc (1, sizeof (ATTACHPTR));
  119. new = idx[(*idxlen)++];
  120. new->content = m;
  121. m->aptr = new;
  122. new->parent_type = parent_type;
  123. new->level = level;
  124. /* We don't support multipart messages in the compose menu yet */
  125. if (!compose && !m->collapsed &&
  126. ((m->type == TYPEMULTIPART
  127. && (!(WithCrypto & APPLICATION_PGP)
  128. || !mutt_is_multipart_encrypted (m))
  129. )
  130. || mutt_is_message_type(m->type, m->subtype)))
  131. {
  132. idx = mutt_gen_attach_list (m->parts, m->type, idx, idxlen, idxmax, level + 1, compose);
  133. }
  134. }
  135. }
  136. if (level == 0)
  137. mutt_update_tree (idx, *idxlen);
  138. return (idx);
  139. }
  140. /* %c = character set: convert?
  141. * %C = character set
  142. * %D = deleted flag
  143. * %d = description
  144. * %e = MIME content-transfer-encoding
  145. * %F = filename for content-disposition header
  146. * %f = filename
  147. * %I = content-disposition, either I (inline) or A (attachment)
  148. * %t = tagged flag
  149. * %T = tree chars
  150. * %m = major MIME type
  151. * %M = MIME subtype
  152. * %n = attachment number
  153. * %s = size
  154. * %u = unlink
  155. */
  156. const char *mutt_attach_fmt (char *dest,
  157. size_t destlen,
  158. size_t col,
  159. int cols,
  160. char op,
  161. const char *src,
  162. const char *prefix,
  163. const char *ifstring,
  164. const char *elsestring,
  165. unsigned long data,
  166. format_flag flags)
  167. {
  168. char fmt[16];
  169. char tmp[SHORT_STRING];
  170. char charset[SHORT_STRING];
  171. ATTACHPTR *aptr = (ATTACHPTR *) data;
  172. int optional = (flags & MUTT_FORMAT_OPTIONAL);
  173. size_t l;
  174. switch (op)
  175. {
  176. case 'C':
  177. if (!optional)
  178. {
  179. if (mutt_is_text_part (aptr->content) &&
  180. mutt_get_body_charset (charset, sizeof (charset), aptr->content))
  181. mutt_format_s (dest, destlen, prefix, charset);
  182. else
  183. mutt_format_s (dest, destlen, prefix, "");
  184. }
  185. else if (!mutt_is_text_part (aptr->content) ||
  186. !mutt_get_body_charset (charset, sizeof (charset), aptr->content))
  187. optional = 0;
  188. break;
  189. case 'c':
  190. /* XXX */
  191. if (!optional)
  192. {
  193. snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
  194. snprintf (dest, destlen, fmt, aptr->content->type != TYPETEXT ||
  195. aptr->content->noconv ? 'n' : 'c');
  196. }
  197. else if (aptr->content->type != TYPETEXT || aptr->content->noconv)
  198. optional = 0;
  199. break;
  200. case 'd':
  201. if(!optional)
  202. {
  203. if (aptr->content->description)
  204. {
  205. mutt_format_s (dest, destlen, prefix, aptr->content->description);
  206. break;
  207. }
  208. if (mutt_is_message_type(aptr->content->type, aptr->content->subtype) &&
  209. MsgFmt && aptr->content->hdr)
  210. {
  211. char s[SHORT_STRING];
  212. _mutt_make_string (s, sizeof (s), MsgFmt, NULL, aptr->content->hdr,
  213. MUTT_FORMAT_FORCESUBJ | MUTT_FORMAT_MAKEPRINT | MUTT_FORMAT_ARROWCURSOR);
  214. if (*s)
  215. {
  216. mutt_format_s (dest, destlen, prefix, s);
  217. break;
  218. }
  219. }
  220. if (!aptr->content->d_filename && !aptr->content->filename)
  221. {
  222. mutt_format_s (dest, destlen, prefix, "<no description>");
  223. break;
  224. }
  225. }
  226. else if(aptr->content->description ||
  227. (mutt_is_message_type (aptr->content->type, aptr->content->subtype)
  228. && MsgFmt && aptr->content->hdr))
  229. break;
  230. /* FALLS THROUGH TO 'F' */
  231. case 'F':
  232. if (!optional)
  233. {
  234. if (aptr->content->d_filename)
  235. {
  236. mutt_format_s (dest, destlen, prefix, aptr->content->d_filename);
  237. break;
  238. }
  239. }
  240. else if (!aptr->content->d_filename && !aptr->content->filename)
  241. {
  242. optional = 0;
  243. break;
  244. }
  245. /* FALLS THROUGH TO 'f' */
  246. case 'f':
  247. if(!optional)
  248. {
  249. if (aptr->content->filename && *aptr->content->filename == '/')
  250. {
  251. char path[_POSIX_PATH_MAX];
  252. strfcpy (path, aptr->content->filename, sizeof (path));
  253. mutt_pretty_mailbox (path, sizeof (path));
  254. mutt_format_s (dest, destlen, prefix, path);
  255. }
  256. else
  257. mutt_format_s (dest, destlen, prefix, NONULL (aptr->content->filename));
  258. }
  259. else if(!aptr->content->filename)
  260. optional = 0;
  261. break;
  262. case 'D':
  263. if(!optional)
  264. snprintf (dest, destlen, "%c", aptr->content->deleted ? 'D' : ' ');
  265. else if(!aptr->content->deleted)
  266. optional = 0;
  267. break;
  268. case 'e':
  269. if(!optional)
  270. mutt_format_s (dest, destlen, prefix,
  271. ENCODING (aptr->content->encoding));
  272. break;
  273. case 'I':
  274. if (!optional)
  275. {
  276. const char dispchar[] = { 'I', 'A', 'F', '-' };
  277. char ch;
  278. if (aptr->content->disposition < sizeof(dispchar))
  279. ch = dispchar[aptr->content->disposition];
  280. else
  281. {
  282. dprint(1, (debugfile, "ERROR: invalid content-disposition %d\n", aptr->content->disposition));
  283. ch = '!';
  284. }
  285. snprintf (dest, destlen, "%c", ch);
  286. }
  287. break;
  288. case 'm':
  289. if(!optional)
  290. mutt_format_s (dest, destlen, prefix, TYPE (aptr->content));
  291. break;
  292. case 'M':
  293. if(!optional)
  294. mutt_format_s (dest, destlen, prefix, aptr->content->subtype);
  295. else if(!aptr->content->subtype)
  296. optional = 0;
  297. break;
  298. case 'n':
  299. if(!optional)
  300. {
  301. snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
  302. snprintf (dest, destlen, fmt, aptr->num + 1);
  303. }
  304. break;
  305. case 'Q':
  306. if (optional)
  307. optional = aptr->content->attach_qualifies;
  308. else {
  309. snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
  310. mutt_format_s (dest, destlen, fmt, "Q");
  311. }
  312. break;
  313. case 's':
  314. if (flags & MUTT_FORMAT_STAT_FILE)
  315. {
  316. struct stat st;
  317. stat (aptr->content->filename, &st);
  318. l = st.st_size;
  319. }
  320. else
  321. l = aptr->content->length;
  322. if(!optional)
  323. {
  324. mutt_pretty_size (tmp, sizeof(tmp), l);
  325. mutt_format_s (dest, destlen, prefix, tmp);
  326. }
  327. else if (l == 0)
  328. optional = 0;
  329. break;
  330. case 't':
  331. if(!optional)
  332. snprintf (dest, destlen, "%c", aptr->content->tagged ? '*' : ' ');
  333. else if(!aptr->content->tagged)
  334. optional = 0;
  335. break;
  336. case 'T':
  337. if(!optional)
  338. mutt_format_s_tree (dest, destlen, prefix, NONULL (aptr->tree));
  339. else if (!aptr->tree)
  340. optional = 0;
  341. break;
  342. case 'u':
  343. if(!optional)
  344. snprintf (dest, destlen, "%c", aptr->content->unlink ? '-' : ' ');
  345. else if (!aptr->content->unlink)
  346. optional = 0;
  347. break;
  348. case 'X':
  349. if (optional)
  350. optional = (aptr->content->attach_count + aptr->content->attach_qualifies) != 0;
  351. else
  352. {
  353. snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
  354. snprintf (dest, destlen, fmt, aptr->content->attach_count + aptr->content->attach_qualifies);
  355. }
  356. break;
  357. default:
  358. *dest = 0;
  359. }
  360. if (optional)
  361. mutt_FormatString (dest, destlen, col, cols, ifstring, mutt_attach_fmt, data, 0);
  362. else if (flags & MUTT_FORMAT_OPTIONAL)
  363. mutt_FormatString (dest, destlen, col, cols, elsestring, mutt_attach_fmt, data, 0);
  364. return (src);
  365. }
  366. static void attach_entry (char *b, size_t blen, MUTTMENU *menu, int num)
  367. {
  368. mutt_FormatString (b, blen, 0, MuttIndexWindow->cols, NONULL (AttachFormat), mutt_attach_fmt, (unsigned long) (((ATTACHPTR **)menu->data)[num]), MUTT_FORMAT_ARROWCURSOR);
  369. }
  370. int mutt_tag_attach (MUTTMENU *menu, int n, int m)
  371. {
  372. BODY *cur = ((ATTACHPTR **) menu->data)[n]->content;
  373. int ot = cur->tagged;
  374. cur->tagged = (m >= 0 ? m : !cur->tagged);
  375. return cur->tagged - ot;
  376. }
  377. int mutt_is_message_type (int type, const char *subtype)
  378. {
  379. if (type != TYPEMESSAGE)
  380. return 0;
  381. subtype = NONULL(subtype);
  382. return (ascii_strcasecmp (subtype, "rfc822") == 0 || ascii_strcasecmp (subtype, "news") == 0);
  383. }
  384. static void prepend_curdir (char *dst, size_t dstlen)
  385. {
  386. size_t l;
  387. if (!dst || !*dst || *dst == '/' || dstlen < 3 ||
  388. /* XXX bad modularization, these are special to mutt_expand_path() */
  389. !strchr ("~=+@<>!-^", *dst))
  390. return;
  391. dstlen -= 3;
  392. l = strlen (dst) + 2;
  393. l = (l > dstlen ? dstlen : l);
  394. memmove (dst + 2, dst, l);
  395. dst[0] = '.';
  396. dst[1] = '/';
  397. dst[l + 2] = 0;
  398. }
  399. static int mutt_query_save_attachment (FILE *fp, BODY *body, HEADER *hdr, char **directory)
  400. {
  401. char *prompt;
  402. char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
  403. int is_message;
  404. int append = 0;
  405. int rc;
  406. if (body->filename)
  407. {
  408. if (directory && *directory)
  409. mutt_concat_path (buf, *directory, mutt_basename (body->filename), sizeof (buf));
  410. else
  411. strfcpy (buf, body->filename, sizeof (buf));
  412. }
  413. else if(body->hdr &&
  414. body->encoding != ENCBASE64 &&
  415. body->encoding != ENCQUOTEDPRINTABLE &&
  416. mutt_is_message_type(body->type, body->subtype))
  417. mutt_default_save(buf, sizeof(buf), body->hdr);
  418. else
  419. buf[0] = 0;
  420. prepend_curdir (buf, sizeof (buf));
  421. prompt = _("Save to file: ");
  422. while (prompt)
  423. {
  424. if (mutt_get_field (prompt, buf, sizeof (buf), MUTT_FILE | MUTT_CLEAR) != 0
  425. || !buf[0])
  426. {
  427. mutt_clear_error ();
  428. return -1;
  429. }
  430. prompt = NULL;
  431. mutt_expand_path (buf, sizeof (buf));
  432. is_message = (fp &&
  433. body->hdr &&
  434. body->encoding != ENCBASE64 &&
  435. body->encoding != ENCQUOTEDPRINTABLE &&
  436. mutt_is_message_type (body->type, body->subtype));
  437. if (is_message)
  438. {
  439. struct stat st;
  440. /* check to make sure that this file is really the one the user wants */
  441. if ((rc = mutt_save_confirm (buf, &st)) == 1)
  442. {
  443. prompt = _("Save to file: ");
  444. continue;
  445. }
  446. else if (rc == -1)
  447. return -1;
  448. strfcpy(tfile, buf, sizeof(tfile));
  449. }
  450. else
  451. {
  452. if ((rc = mutt_check_overwrite (body->filename, buf, tfile, sizeof (tfile), &append, directory)) == -1)
  453. return -1;
  454. else if (rc == 1)
  455. {
  456. prompt = _("Save to file: ");
  457. continue;
  458. }
  459. }
  460. mutt_message _("Saving...");
  461. if (mutt_save_attachment (fp, body, tfile, append, (hdr || !is_message) ? hdr : body->hdr) == 0)
  462. {
  463. mutt_message _("Attachment saved.");
  464. return 0;
  465. }
  466. else
  467. {
  468. prompt = _("Save to file: ");
  469. continue;
  470. }
  471. }
  472. return 0;
  473. }
  474. void mutt_save_attachment_list (FILE *fp, int tag, BODY *top, HEADER *hdr, MUTTMENU *menu)
  475. {
  476. char buf[_POSIX_PATH_MAX], tfile[_POSIX_PATH_MAX];
  477. char *directory = NULL;
  478. int rc = 1;
  479. int last = menu ? menu->current : -1;
  480. FILE *fpout;
  481. buf[0] = 0;
  482. for (; top; top = top->next)
  483. {
  484. if (!tag || top->tagged)
  485. {
  486. if (!option (OPTATTACHSPLIT))
  487. {
  488. if (!buf[0])
  489. {
  490. int append = 0;
  491. strfcpy (buf, mutt_basename (NONULL (top->filename)), sizeof (buf));
  492. prepend_curdir (buf, sizeof (buf));
  493. if (mutt_get_field (_("Save to file: "), buf, sizeof (buf),
  494. MUTT_FILE | MUTT_CLEAR) != 0 || !buf[0])
  495. return;
  496. mutt_expand_path (buf, sizeof (buf));
  497. if (mutt_check_overwrite (top->filename, buf, tfile,
  498. sizeof (tfile), &append, NULL))
  499. return;
  500. rc = mutt_save_attachment (fp, top, tfile, append, hdr);
  501. if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL)
  502. {
  503. fprintf(fpout, "%s", AttachSep);
  504. safe_fclose (&fpout);
  505. }
  506. }
  507. else
  508. {
  509. rc = mutt_save_attachment (fp, top, tfile, MUTT_SAVE_APPEND, hdr);
  510. if (rc == 0 && AttachSep && (fpout = fopen (tfile,"a")) != NULL)
  511. {
  512. fprintf(fpout, "%s", AttachSep);
  513. safe_fclose (&fpout);
  514. }
  515. }
  516. }
  517. else
  518. {
  519. if (tag && menu && top->aptr)
  520. {
  521. menu->oldcurrent = menu->current;
  522. menu->current = top->aptr->num;
  523. menu_check_recenter (menu);
  524. menu->redraw |= REDRAW_MOTION;
  525. menu_redraw (menu);
  526. }
  527. if (mutt_query_save_attachment (fp, top, hdr, &directory) == -1)
  528. break;
  529. }
  530. }
  531. else if (top->parts)
  532. mutt_save_attachment_list (fp, 1, top->parts, hdr, menu);
  533. if (!tag)
  534. break;
  535. }
  536. FREE (&directory);
  537. if (tag && menu)
  538. {
  539. menu->oldcurrent = menu->current;
  540. menu->current = last;
  541. menu_check_recenter (menu);
  542. menu->redraw |= REDRAW_MOTION;
  543. }
  544. if (!option (OPTATTACHSPLIT) && (rc == 0))
  545. mutt_message _("Attachment saved.");
  546. }
  547. static void
  548. mutt_query_pipe_attachment (char *command, FILE *fp, BODY *body, int filter)
  549. {
  550. char tfile[_POSIX_PATH_MAX];
  551. char warning[STRING+_POSIX_PATH_MAX];
  552. if (filter)
  553. {
  554. snprintf (warning, sizeof (warning),
  555. _("WARNING! You are about to overwrite %s, continue?"),
  556. body->filename);
  557. if (mutt_yesorno (warning, MUTT_NO) != MUTT_YES) {
  558. mutt_window_clearline (MuttMessageWindow, 0);
  559. return;
  560. }
  561. mutt_mktemp (tfile, sizeof (tfile));
  562. }
  563. else
  564. tfile[0] = 0;
  565. if (mutt_pipe_attachment (fp, body, command, tfile))
  566. {
  567. if (filter)
  568. {
  569. mutt_unlink (body->filename);
  570. mutt_rename_file (tfile, body->filename);
  571. mutt_update_encoding (body);
  572. mutt_message _("Attachment filtered.");
  573. }
  574. }
  575. else
  576. {
  577. if (filter && tfile[0])
  578. mutt_unlink (tfile);
  579. }
  580. }
  581. static void pipe_attachment (FILE *fp, BODY *b, STATE *state)
  582. {
  583. FILE *ifp;
  584. if (fp)
  585. {
  586. state->fpin = fp;
  587. mutt_decode_attachment (b, state);
  588. if (AttachSep)
  589. state_puts (AttachSep, state);
  590. }
  591. else
  592. {
  593. if ((ifp = fopen (b->filename, "r")) == NULL)
  594. {
  595. mutt_perror ("fopen");
  596. return;
  597. }
  598. mutt_copy_stream (ifp, state->fpout);
  599. safe_fclose (&ifp);
  600. if (AttachSep)
  601. state_puts (AttachSep, state);
  602. }
  603. }
  604. static void
  605. pipe_attachment_list (char *command, FILE *fp, int tag, BODY *top, int filter,
  606. STATE *state)
  607. {
  608. for (; top; top = top->next)
  609. {
  610. if (!tag || top->tagged)
  611. {
  612. if (!filter && !option (OPTATTACHSPLIT))
  613. pipe_attachment (fp, top, state);
  614. else
  615. mutt_query_pipe_attachment (command, fp, top, filter);
  616. }
  617. else if (top->parts)
  618. pipe_attachment_list (command, fp, tag, top->parts, filter, state);
  619. if (!tag)
  620. break;
  621. }
  622. }
  623. void mutt_pipe_attachment_list (FILE *fp, int tag, BODY *top, int filter)
  624. {
  625. STATE state;
  626. char buf[SHORT_STRING];
  627. pid_t thepid;
  628. if (fp)
  629. filter = 0; /* sanity check: we can't filter in the recv case yet */
  630. buf[0] = 0;
  631. memset (&state, 0, sizeof (STATE));
  632. /* perform charset conversion on text attachments when piping */
  633. state.flags = MUTT_CHARCONV;
  634. if (mutt_get_field ((filter ? _("Filter through: ") : _("Pipe to: ")),
  635. buf, sizeof (buf), MUTT_CMD) != 0 || !buf[0])
  636. return;
  637. mutt_expand_path (buf, sizeof (buf));
  638. if (!filter && !option (OPTATTACHSPLIT))
  639. {
  640. mutt_endwin (NULL);
  641. thepid = mutt_create_filter (buf, &state.fpout, NULL, NULL);
  642. pipe_attachment_list (buf, fp, tag, top, filter, &state);
  643. safe_fclose (&state.fpout);
  644. if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
  645. mutt_any_key_to_continue (NULL);
  646. }
  647. else
  648. pipe_attachment_list (buf, fp, tag, top, filter, &state);
  649. }
  650. static int can_print (BODY *top, int tag)
  651. {
  652. char type [STRING];
  653. for (; top; top = top->next)
  654. {
  655. snprintf (type, sizeof (type), "%s/%s", TYPE (top), top->subtype);
  656. if (!tag || top->tagged)
  657. {
  658. if (!rfc1524_mailcap_lookup (top, type, NULL, MUTT_PRINT))
  659. {
  660. if (ascii_strcasecmp ("text/plain", top->subtype) &&
  661. ascii_strcasecmp ("application/postscript", top->subtype))
  662. {
  663. if (!mutt_can_decode (top))
  664. {
  665. mutt_error (_("I don't know how to print %s attachments!"), type);
  666. return (0);
  667. }
  668. }
  669. }
  670. }
  671. else if (top->parts)
  672. return (can_print (top->parts, tag));
  673. if (!tag)
  674. break;
  675. }
  676. return (1);
  677. }
  678. static void print_attachment_list (FILE *fp, int tag, BODY *top, STATE *state)
  679. {
  680. char type [STRING];
  681. for (; top; top = top->next)
  682. {
  683. if (!tag || top->tagged)
  684. {
  685. snprintf (type, sizeof (type), "%s/%s", TYPE (top), top->subtype);
  686. if (!option (OPTATTACHSPLIT) && !rfc1524_mailcap_lookup (top, type, NULL, MUTT_PRINT))
  687. {
  688. if (!ascii_strcasecmp ("text/plain", top->subtype) ||
  689. !ascii_strcasecmp ("application/postscript", top->subtype))
  690. pipe_attachment (fp, top, state);
  691. else if (mutt_can_decode (top))
  692. {
  693. /* decode and print */
  694. char newfile[_POSIX_PATH_MAX] = "";
  695. FILE *ifp;
  696. mutt_mktemp (newfile, sizeof (newfile));
  697. if (mutt_decode_save_attachment (fp, top, newfile, MUTT_PRINTING, 0) == 0)
  698. {
  699. if ((ifp = fopen (newfile, "r")) != NULL)
  700. {
  701. mutt_copy_stream (ifp, state->fpout);
  702. safe_fclose (&ifp);
  703. if (AttachSep)
  704. state_puts (AttachSep, state);
  705. }
  706. }
  707. mutt_unlink (newfile);
  708. }
  709. }
  710. else
  711. mutt_print_attachment (fp, top);
  712. }
  713. else if (top->parts)
  714. print_attachment_list (fp, tag, top->parts, state);
  715. if (!tag)
  716. return;
  717. }
  718. }
  719. void mutt_print_attachment_list (FILE *fp, int tag, BODY *top)
  720. {
  721. STATE state;
  722. pid_t thepid;
  723. if (query_quadoption (OPT_PRINT, tag ? _("Print tagged attachment(s)?") : _("Print attachment?")) != MUTT_YES)
  724. return;
  725. if (!option (OPTATTACHSPLIT))
  726. {
  727. if (!can_print (top, tag))
  728. return;
  729. mutt_endwin (NULL);
  730. memset (&state, 0, sizeof (STATE));
  731. thepid = mutt_create_filter (NONULL (PrintCmd), &state.fpout, NULL, NULL);
  732. print_attachment_list (fp, tag, top, &state);
  733. safe_fclose (&state.fpout);
  734. if (mutt_wait_filter (thepid) != 0 || option (OPTWAITKEY))
  735. mutt_any_key_to_continue (NULL);
  736. }
  737. else
  738. print_attachment_list (fp, tag, top, &state);
  739. }
  740. static void
  741. mutt_update_attach_index (BODY *cur, ATTACHPTR ***idxp,
  742. short *idxlen, short *idxmax,
  743. MUTTMENU *menu)
  744. {
  745. ATTACHPTR **idx = *idxp;
  746. while (--(*idxlen) >= 0)
  747. idx[(*idxlen)]->content = NULL;
  748. *idxlen = 0;
  749. idx = *idxp = mutt_gen_attach_list (cur, -1, idx, idxlen, idxmax, 0, 0);
  750. menu->max = *idxlen;
  751. menu->data = *idxp;
  752. if (menu->current >= menu->max)
  753. menu->current = menu->max - 1;
  754. menu_check_recenter (menu);
  755. menu->redraw |= REDRAW_INDEX;
  756. }
  757. int
  758. mutt_attach_display_loop (MUTTMENU *menu, int op, FILE *fp, HEADER *hdr,
  759. BODY *cur, ATTACHPTR ***idxp, short *idxlen, short *idxmax,
  760. int recv)
  761. {
  762. ATTACHPTR **idx = *idxp;
  763. #if 0
  764. int old_optweed = option (OPTWEED);
  765. set_option (OPTWEED);
  766. #endif
  767. do
  768. {
  769. switch (op)
  770. {
  771. case OP_DISPLAY_HEADERS:
  772. toggle_option (OPTWEED);
  773. /* fall through */
  774. case OP_VIEW_ATTACH:
  775. op = mutt_view_attachment (fp, idx[menu->current]->content, MUTT_REGULAR,
  776. hdr, idx, *idxlen);
  777. break;
  778. case OP_NEXT_ENTRY:
  779. case OP_MAIN_NEXT_UNDELETED: /* hack */
  780. if (menu->current < menu->max - 1)
  781. {
  782. menu->current++;
  783. op = OP_VIEW_ATTACH;
  784. }
  785. else
  786. op = OP_NULL;
  787. break;
  788. case OP_PREV_ENTRY:
  789. case OP_MAIN_PREV_UNDELETED: /* hack */
  790. if (menu->current > 0)
  791. {
  792. menu->current--;
  793. op = OP_VIEW_ATTACH;
  794. }
  795. else
  796. op = OP_NULL;
  797. break;
  798. case OP_EDIT_TYPE:
  799. /* when we edit the content-type, we should redisplay the attachment
  800. immediately */
  801. mutt_edit_content_type (hdr, idx[menu->current]->content, fp);
  802. if (idxmax)
  803. {
  804. mutt_update_attach_index (cur, idxp, idxlen, idxmax, menu);
  805. idx = *idxp;
  806. }
  807. op = OP_VIEW_ATTACH;
  808. break;
  809. /* functions which are passed through from the pager */
  810. case OP_CHECK_TRADITIONAL:
  811. if (!(WithCrypto & APPLICATION_PGP) || (hdr && hdr->security & PGP_TRADITIONAL_CHECKED))
  812. {
  813. op = OP_NULL;
  814. break;
  815. }
  816. /* fall through */
  817. case OP_ATTACH_COLLAPSE:
  818. if (recv)
  819. return op;
  820. default:
  821. op = OP_NULL;
  822. }
  823. }
  824. while (op != OP_NULL);
  825. #if 0
  826. if (option (OPTWEED) != old_optweed)
  827. toggle_option (OPTWEED);
  828. #endif
  829. return op;
  830. }
  831. static void attach_collapse (BODY *b, short collapse, short init, short just_one)
  832. {
  833. short i;
  834. for (; b; b = b->next)
  835. {
  836. i = init || b->collapsed;
  837. if (i && option (OPTDIGESTCOLLAPSE) && b->type == TYPEMULTIPART
  838. && !ascii_strcasecmp (b->subtype, "digest"))
  839. attach_collapse (b->parts, 1, 1, 0);
  840. else if (b->type == TYPEMULTIPART || mutt_is_message_type (b->type, b->subtype))
  841. attach_collapse (b->parts, collapse, i, 0);
  842. b->collapsed = collapse;
  843. if (just_one)
  844. return;
  845. }
  846. }
  847. void mutt_attach_init (BODY *b)
  848. {
  849. for (; b; b = b->next)
  850. {
  851. b->tagged = 0;
  852. b->collapsed = 0;
  853. if (b->parts)
  854. mutt_attach_init (b->parts);
  855. }
  856. }
  857. static const char *Function_not_permitted = N_("Function not permitted in attach-message mode.");
  858. #define CHECK_ATTACH if(option(OPTATTACHMSG)) \
  859. {\
  860. mutt_flushinp (); \
  861. mutt_error _(Function_not_permitted); \
  862. break; \
  863. }
  864. void mutt_view_attachments (HEADER *hdr)
  865. {
  866. int secured = 0;
  867. int need_secured = 0;
  868. char helpstr[LONG_STRING];
  869. MUTTMENU *menu;
  870. BODY *cur = NULL;
  871. MESSAGE *msg;
  872. FILE *fp;
  873. ATTACHPTR **idx = NULL;
  874. short idxlen = 0;
  875. short idxmax = 0;
  876. int flags = 0;
  877. int op = OP_NULL;
  878. /* make sure we have parsed this message */
  879. mutt_parse_mime_message (Context, hdr);
  880. mutt_message_hook (Context, hdr, MUTT_MESSAGEHOOK);
  881. if ((msg = mx_open_message (Context, hdr->msgno)) == NULL)
  882. return;
  883. if (WithCrypto && ((hdr->security & ENCRYPT) ||
  884. (mutt_is_application_smime(hdr->content) & SMIMEOPAQUE)))
  885. {
  886. need_secured = 1;
  887. if ((hdr->security & ENCRYPT) && !crypt_valid_passphrase(hdr->security))
  888. {
  889. mx_close_message (Context, &msg);
  890. return;
  891. }
  892. if ((WithCrypto & APPLICATION_SMIME) && (hdr->security & APPLICATION_SMIME))
  893. {
  894. if (hdr->env)
  895. crypt_smime_getkeys (hdr->env);
  896. if (mutt_is_application_smime(hdr->content))
  897. {
  898. secured = ! crypt_smime_decrypt_mime (msg->fp, &fp,
  899. hdr->content, &cur);
  900. /* S/MIME nesting */
  901. if ((mutt_is_application_smime (cur) & SMIMEOPAQUE))
  902. {
  903. BODY *_cur = cur;
  904. FILE *_fp = fp;
  905. fp = NULL; cur = NULL;
  906. secured = !crypt_smime_decrypt_mime (_fp, &fp, _cur, &cur);
  907. mutt_free_body (&_cur);
  908. safe_fclose (&_fp);
  909. }
  910. }
  911. else
  912. need_secured = 0;
  913. }
  914. if ((WithCrypto & APPLICATION_PGP) && (hdr->security & APPLICATION_PGP))
  915. {
  916. if (mutt_is_multipart_encrypted(hdr->content) ||
  917. mutt_is_malformed_multipart_pgp_encrypted(hdr->content))
  918. secured = !crypt_pgp_decrypt_mime (msg->fp, &fp, hdr->content, &cur);
  919. else
  920. need_secured = 0;
  921. }
  922. if (need_secured && !secured)
  923. {
  924. mx_close_message (Context, &msg);
  925. mutt_error _("Can't decrypt encrypted message!");
  926. return;
  927. }
  928. }
  929. if (!WithCrypto || !need_secured)
  930. {
  931. fp = msg->fp;
  932. cur = hdr->content;
  933. }
  934. menu = mutt_new_menu (MENU_ATTACH);
  935. menu->title = _("Attachments");
  936. menu->make_entry = attach_entry;
  937. menu->tag = mutt_tag_attach;
  938. menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_ATTACH, AttachHelp);
  939. mutt_push_current_menu (menu);
  940. mutt_attach_init (cur);
  941. attach_collapse (cur, 0, 1, 0);
  942. mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu);
  943. FOREVER
  944. {
  945. if (op == OP_NULL)
  946. op = mutt_menuLoop (menu);
  947. switch (op)
  948. {
  949. case OP_ATTACH_VIEW_MAILCAP:
  950. mutt_view_attachment (fp, idx[menu->current]->content, MUTT_MAILCAP,
  951. hdr, idx, idxlen);
  952. menu->redraw = REDRAW_FULL;
  953. break;
  954. case OP_ATTACH_VIEW_TEXT:
  955. mutt_view_attachment (fp, idx[menu->current]->content, MUTT_AS_TEXT,
  956. hdr, idx, idxlen);
  957. menu->redraw = REDRAW_FULL;
  958. break;
  959. case OP_DISPLAY_HEADERS:
  960. case OP_VIEW_ATTACH:
  961. op = mutt_attach_display_loop (menu, op, fp, hdr, cur, &idx, &idxlen, &idxmax, 1);
  962. menu->redraw = REDRAW_FULL;
  963. continue;
  964. case OP_ATTACH_COLLAPSE:
  965. if (!idx[menu->current]->content->parts)
  966. {
  967. mutt_error _("There are no subparts to show!");
  968. break;
  969. }
  970. if (!idx[menu->current]->content->collapsed)
  971. attach_collapse (idx[menu->current]->content, 1, 0, 1);
  972. else
  973. attach_collapse (idx[menu->current]->content, 0, 1, 1);
  974. mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu);
  975. break;
  976. case OP_FORGET_PASSPHRASE:
  977. crypt_forget_passphrase ();
  978. break;
  979. case OP_EXTRACT_KEYS:
  980. if ((WithCrypto & APPLICATION_PGP))
  981. {
  982. crypt_pgp_extract_keys_from_attachment_list (fp, menu->tagprefix,
  983. menu->tagprefix ? cur : idx[menu->current]->content);
  984. menu->redraw = REDRAW_FULL;
  985. }
  986. break;
  987. case OP_CHECK_TRADITIONAL:
  988. if ((WithCrypto & APPLICATION_PGP)
  989. && crypt_pgp_check_traditional (fp, menu->tagprefix ? cur
  990. : idx[menu->current]->content,
  991. menu->tagprefix))
  992. {
  993. hdr->security = crypt_query (cur);
  994. menu->redraw = REDRAW_FULL;
  995. }
  996. break;
  997. case OP_PRINT:
  998. mutt_print_attachment_list (fp, menu->tagprefix,
  999. menu->tagprefix ? cur : idx[menu->current]->content);
  1000. break;
  1001. case OP_PIPE:
  1002. mutt_pipe_attachment_list (fp, menu->tagprefix,
  1003. menu->tagprefix ? cur : idx[menu->current]->content, 0);
  1004. break;
  1005. case OP_SAVE:
  1006. mutt_save_attachment_list (fp, menu->tagprefix,
  1007. menu->tagprefix ? cur : idx[menu->current]->content, hdr, menu);
  1008. if (!menu->tagprefix && option (OPTRESOLVE) && menu->current < menu->max - 1)
  1009. menu->current++;
  1010. menu->redraw = REDRAW_MOTION_RESYNCH | REDRAW_FULL;
  1011. break;
  1012. case OP_DELETE:
  1013. CHECK_READONLY;
  1014. #ifdef USE_POP
  1015. if (Context->magic == MUTT_POP)
  1016. {
  1017. mutt_flushinp ();
  1018. mutt_error _("Can't delete attachment from POP server.");
  1019. break;
  1020. }
  1021. #endif
  1022. if (WithCrypto && (hdr->security & ENCRYPT))
  1023. {
  1024. mutt_message _(
  1025. "Deletion of attachments from encrypted messages is unsupported.");
  1026. break;
  1027. }
  1028. if (WithCrypto && (hdr->security & (SIGN | PARTSIGN)))
  1029. {
  1030. mutt_message _(
  1031. "Deletion of attachments from signed messages may invalidate the signature.");
  1032. }
  1033. if (!menu->tagprefix)
  1034. {
  1035. if (idx[menu->current]->parent_type == TYPEMULTIPART)
  1036. {
  1037. idx[menu->current]->content->deleted = 1;
  1038. if (option (OPTRESOLVE) && menu->current < menu->max - 1)
  1039. {
  1040. menu->current++;
  1041. menu->redraw = REDRAW_MOTION_RESYNCH;
  1042. }
  1043. else
  1044. menu->redraw = REDRAW_CURRENT;
  1045. }
  1046. else
  1047. mutt_message _(
  1048. "Only deletion of multipart attachments is supported.");
  1049. }
  1050. else
  1051. {
  1052. int x;
  1053. for (x = 0; x < menu->max; x++)
  1054. {
  1055. if (idx[x]->content->tagged)
  1056. {
  1057. if (idx[x]->parent_type == TYPEMULTIPART)
  1058. {
  1059. idx[x]->content->deleted = 1;
  1060. menu->redraw = REDRAW_INDEX;
  1061. }
  1062. else
  1063. mutt_message _(
  1064. "Only deletion of multipart attachments is supported.");
  1065. }
  1066. }
  1067. }
  1068. break;
  1069. case OP_UNDELETE:
  1070. CHECK_READONLY;
  1071. if (!menu->tagprefix)
  1072. {
  1073. idx[menu->current]->content->deleted = 0;
  1074. if (option (OPTRESOLVE) && menu->current < menu->max - 1)
  1075. {
  1076. menu->current++;
  1077. menu->redraw = REDRAW_MOTION_RESYNCH;
  1078. }
  1079. else
  1080. menu->redraw = REDRAW_CURRENT;
  1081. }
  1082. else
  1083. {
  1084. int x;
  1085. for (x = 0; x < menu->max; x++)
  1086. {
  1087. if (idx[x]->content->tagged)
  1088. {
  1089. idx[x]->content->deleted = 0;
  1090. menu->redraw = REDRAW_INDEX;
  1091. }
  1092. }
  1093. }
  1094. break;
  1095. case OP_RESEND:
  1096. CHECK_ATTACH;
  1097. mutt_attach_resend (fp, hdr, idx, idxlen,
  1098. menu->tagprefix ? NULL : idx[menu->current]->content);
  1099. menu->redraw = REDRAW_FULL;
  1100. break;
  1101. case OP_BOUNCE_MESSAGE:
  1102. CHECK_ATTACH;
  1103. mutt_attach_bounce (fp, hdr, idx, idxlen,
  1104. menu->tagprefix ? NULL : idx[menu->current]->content);
  1105. menu->redraw = REDRAW_FULL;
  1106. break;
  1107. case OP_FORWARD_MESSAGE:
  1108. CHECK_ATTACH;
  1109. mutt_attach_forward (fp, hdr, idx, idxlen,
  1110. menu->tagprefix ? NULL : idx[menu->current]->content);
  1111. menu->redraw = REDRAW_FULL;
  1112. break;
  1113. case OP_REPLY:
  1114. case OP_GROUP_REPLY:
  1115. case OP_LIST_REPLY:
  1116. CHECK_ATTACH;
  1117. flags = SENDREPLY |
  1118. (op == OP_GROUP_REPLY ? SENDGROUPREPLY : 0) |
  1119. (op == OP_LIST_REPLY ? SENDLISTREPLY : 0);
  1120. mutt_attach_reply (fp, hdr, idx, idxlen,
  1121. menu->tagprefix ? NULL : idx[menu->current]->content, flags);
  1122. menu->redraw = REDRAW_FULL;
  1123. break;
  1124. case OP_EDIT_TYPE:
  1125. mutt_edit_content_type (hdr, idx[menu->current]->content, fp);
  1126. mutt_update_attach_index (cur, &idx, &idxlen, &idxmax, menu);
  1127. break;
  1128. case OP_EXIT:
  1129. mx_close_message (Context, &msg);
  1130. hdr->attach_del = 0;
  1131. while (idxmax-- > 0)
  1132. {
  1133. if (!idx[idxmax])
  1134. continue;
  1135. if (idx[idxmax]->content && idx[idxmax]->content->deleted)
  1136. hdr->attach_del = 1;
  1137. if (idx[idxmax]->content)
  1138. idx[idxmax]->content->aptr = NULL;
  1139. FREE (&idx[idxmax]->tree);
  1140. FREE (&idx[idxmax]);
  1141. }
  1142. if (hdr->attach_del)
  1143. hdr->changed = 1;
  1144. FREE (&idx);
  1145. idxmax = 0;
  1146. if (WithCrypto && need_secured && secured)
  1147. {
  1148. safe_fclose (&fp);
  1149. mutt_free_body (&cur);
  1150. }
  1151. mutt_pop_current_menu (menu);
  1152. mutt_menuDestroy (&menu);
  1153. return;
  1154. }
  1155. op = OP_NULL;
  1156. }
  1157. /* not reached */
  1158. }