NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
postpone.c
Go to the documentation of this file.
1 
72 #include "config.h"
73 #include <stdbool.h>
74 #include <stdio.h>
75 #include <string.h>
76 #include <sys/stat.h>
77 #include <time.h>
78 #include <unistd.h>
79 #include "mutt/lib.h"
80 #include "config/lib.h"
81 #include "email/lib.h"
82 #include "core/lib.h"
83 #include "gui/lib.h"
84 #include "mutt.h"
85 #include "menu/lib.h"
86 #include "ncrypt/lib.h"
87 #include "pattern/lib.h"
88 #include "send/lib.h"
89 #include "context.h"
90 #include "format_flags.h"
91 #include "handler.h"
92 #include "hdrline.h"
93 #include "mutt_logging.h"
94 #include "mutt_thread.h"
95 #include "muttlib.h"
96 #include "mx.h"
97 #include "opcodes.h"
98 #include "options.h"
99 #include "protos.h"
100 #include "rfc3676.h"
101 #ifdef USE_IMAP
102 #include "imap/lib.h"
103 #endif
104 
106 static const struct Mapping PostponeHelp[] = {
107  // clang-format off
108  { N_("Exit"), OP_EXIT },
109  { N_("Del"), OP_DELETE },
110  { N_("Undel"), OP_UNDELETE },
111  { N_("Help"), OP_HELP },
112  { NULL, 0 },
113  // clang-format on
114 };
115 
116 static short PostCount = 0;
117 static bool UpdateNumPostponed = false;
118 
127 int mutt_num_postponed(struct Mailbox *m, bool force)
128 {
129  struct stat st;
130 
131  static time_t LastModify = 0;
132  static char *OldPostponed = NULL;
133 
134  if (UpdateNumPostponed)
135  {
136  UpdateNumPostponed = false;
137  force = true;
138  }
139 
140  const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed");
141  if (!mutt_str_equal(c_postponed, OldPostponed))
142  {
143  FREE(&OldPostponed);
144  OldPostponed = mutt_str_dup(c_postponed);
145  LastModify = 0;
146  force = true;
147  }
148 
149  if (!c_postponed)
150  return 0;
151 
152  // We currently are in the `$postponed` mailbox so just pick the current status
153  if (m && mutt_str_equal(c_postponed, m->realpath))
154  {
155  PostCount = m->msg_count - m->msg_deleted;
156  return PostCount;
157  }
158 
159 #ifdef USE_IMAP
160  /* LastModify is useless for IMAP */
161  if (imap_path_probe(c_postponed, NULL) == MUTT_IMAP)
162  {
163  if (force)
164  {
165  short newpc;
166 
167  newpc = imap_path_status(c_postponed, false);
168  if (newpc >= 0)
169  {
170  PostCount = newpc;
171  mutt_debug(LL_DEBUG3, "%d postponed IMAP messages found\n", PostCount);
172  }
173  else
174  mutt_debug(LL_DEBUG3, "using old IMAP postponed count\n");
175  }
176  return PostCount;
177  }
178 #endif
179 
180  if (stat(c_postponed, &st) == -1)
181  {
182  PostCount = 0;
183  LastModify = 0;
184  return 0;
185  }
186 
187  if (S_ISDIR(st.st_mode))
188  {
189  /* if we have a maildir mailbox, we need to stat the "new" dir */
190  struct Buffer *buf = mutt_buffer_pool_get();
191 
192  mutt_buffer_printf(buf, "%s/new", c_postponed);
193  if ((access(mutt_buffer_string(buf), F_OK) == 0) &&
194  (stat(mutt_buffer_string(buf), &st) == -1))
195  {
196  PostCount = 0;
197  LastModify = 0;
199  return 0;
200  }
202  }
203 
204  if (LastModify < st.st_mtime)
205  {
206 #ifdef USE_NNTP
207  int optnews = OptNews;
208 #endif
209  LastModify = st.st_mtime;
210 
211  if (access(c_postponed, R_OK | F_OK) != 0)
212  return PostCount = 0;
213 #ifdef USE_NNTP
214  if (optnews)
215  OptNews = false;
216 #endif
217  struct Mailbox *m_post = mx_path_resolve(c_postponed);
218  if (mx_mbox_open(m_post, MUTT_NOSORT | MUTT_QUIET))
219  {
220  PostCount = m_post->msg_count;
221  }
222  else
223  {
224  mailbox_free(&m_post);
225  PostCount = 0;
226  }
227  mx_fastclose_mailbox(m_post);
228 #ifdef USE_NNTP
229  if (optnews)
230  OptNews = true;
231 #endif
232  }
233 
234  return PostCount;
235 }
236 
241 {
242  UpdateNumPostponed = true;
243 }
244 
248 static void post_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
249 {
250  struct Mailbox *m = menu->mdata;
251 
252  const char *const c_index_format =
253  cs_subset_string(NeoMutt->sub, "index_format");
254  mutt_make_string(buf, buflen, menu->win->state.cols, NONULL(c_index_format),
255  m, -1, m->emails[line], MUTT_FORMAT_ARROWCURSOR, NULL);
256 }
257 
264 {
265  if ((nc->event_type != NT_CONFIG) || !nc->global_data || !nc->event_data)
266  return -1;
267 
268  struct EventConfig *ev_c = nc->event_data;
269 
270  if (!mutt_str_equal(ev_c->name, "index_format") && !mutt_str_equal(ev_c->name, "sort"))
271  return 0;
272 
273  struct Menu *menu = nc->global_data;
275  mutt_debug(LL_DEBUG5, "config done, request WA_RECALC, MENU_REDRAW_FULL\n");
276 
277  return 0;
278 }
279 
288 {
289  if ((nc->event_type != NT_WINDOW) || !nc->global_data || !nc->event_data)
290  return -1;
291 
292  if (nc->event_subtype != NT_WINDOW_DELETE)
293  return 0;
294 
295  struct MuttWindow *win_menu = nc->global_data;
296  struct EventWindow *ev_w = nc->event_data;
297  if (ev_w->win != win_menu)
298  return 0;
299 
300  struct Menu *menu = win_menu->wdata;
301 
304 
305  mutt_debug(LL_DEBUG5, "window delete done\n");
306  return 0;
307 }
308 
314 static struct Email *dlg_select_postponed_email(struct Mailbox *m)
315 {
316  int r = -1;
317  bool done = false;
318 
319  struct MuttWindow *dlg = simple_dialog_new(MENU_POSTPONE, WT_DLG_POSTPONE, PostponeHelp);
320 
321  struct Menu *menu = dlg->wdata;
322  menu->make_entry = post_make_entry;
323  menu->max = m->msg_count;
324  menu->mdata = m;
325  menu->custom_search = true;
326 
327  struct MuttWindow *win_menu = menu->win;
328 
329  // NT_COLOR is handled by the SimpleDialog
332 
333  struct MuttWindow *sbar = window_find_child(dlg, WT_STATUS_BAR);
334  sbar_set_title(sbar, _("Postponed Messages"));
335 
336  /* The postponed mailbox is setup to have sorting disabled, but the global
337  * `$sort` variable may indicate something different. Sorting has to be
338  * disabled while the postpone menu is being displayed. */
339  const short c_sort = cs_subset_sort(NeoMutt->sub, "sort");
341 
342  while (!done)
343  {
344  const int op = menu_loop(menu);
345  switch (op)
346  {
347  case OP_DELETE:
348  case OP_UNDELETE:
349  {
350  const int index = menu_get_index(menu);
351  /* should deleted draft messages be saved in the trash folder? */
352  mutt_set_flag(m, m->emails[index], MUTT_DELETE, (op == OP_DELETE));
353  PostCount = m->msg_count - m->msg_deleted;
354  const bool c_resolve = cs_subset_bool(NeoMutt->sub, "resolve");
355  if (c_resolve && (index < (menu->max - 1)))
356  {
357  menu_set_index(menu, index + 1);
358  if (index >= (menu->top + menu->pagelen))
359  {
360  menu->top = index;
362  }
363  }
364  else
366  break;
367  }
368 
369  // All search operations must exist to show the menu
370  case OP_SEARCH_REVERSE:
371  case OP_SEARCH_NEXT:
372  case OP_SEARCH_OPPOSITE:
373  case OP_SEARCH:
374  {
375  int index = menu_get_index(menu);
376  index = mutt_search_command(m, menu, index, op);
377  if (index != -1)
378  menu_set_index(menu, index);
379  break;
380  }
381 
382  case OP_GENERIC_SELECT_ENTRY:
383  r = menu_get_index(menu);
384  done = true;
385  break;
386 
387  case OP_EXIT:
388  done = true;
389  break;
390  }
391  }
392 
393  cs_subset_str_native_set(NeoMutt->sub, "sort", c_sort, NULL);
394  simple_dialog_free(&dlg);
395 
396  return (r > -1) ? m->emails[r] : NULL;
397 }
398 
403 static void hardclose(struct Mailbox *m)
404 {
405  /* messages might have been marked for deletion.
406  * try once more on reopen before giving up. */
407  enum MxStatus rc = mx_mbox_close(m);
408  if (rc != MX_STATUS_ERROR && rc != MX_STATUS_OK)
409  rc = mx_mbox_close(m);
410  if (rc != MX_STATUS_OK)
412 }
413 
424 int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr,
425  struct Email **cur, struct Buffer *fcc)
426 {
427  const char *const c_postponed = cs_subset_string(NeoMutt->sub, "postponed");
428  if (!c_postponed)
429  return -1;
430 
431  struct Email *e = NULL;
432  int rc = SEND_POSTPONED;
433  const char *p = NULL;
434 
435  struct Mailbox *m = mx_path_resolve(c_postponed);
436  if (m_cur != m)
437  {
438  if (!mx_mbox_open(m, MUTT_NOSORT))
439  {
440  PostCount = 0;
441  mutt_error(_("No postponed messages"));
442  mailbox_free(&m);
443  return -1;
444  }
445  }
446 
447  mx_mbox_check(m);
448 
449  if (m->msg_count == 0)
450  {
451  PostCount = 0;
452  mutt_error(_("No postponed messages"));
453  if (m_cur != m)
454  {
456  }
457  return -1;
458  }
459 
460  /* avoid the "purge deleted messages" prompt */
461  const enum QuadOption c_delete = cs_subset_quad(NeoMutt->sub, "delete");
462  cs_subset_str_native_set(NeoMutt->sub, "delete", MUTT_YES, NULL);
463 
464  struct Context *ctx = (m_cur != m) ? ctx_new(m) : NULL;
465  if (m->msg_count == 1)
466  {
467  /* only one message, so just use that one. */
468  e = m->emails[0];
469  }
470  else if (!(e = dlg_select_postponed_email(m)))
471  {
472  rc = -1;
473  goto cleanup;
474  }
475 
476  if (mutt_prepare_template(NULL, m, hdr, e, false) < 0)
477  {
478  rc = -1;
479  goto cleanup;
480  }
481 
482  /* finished with this message, so delete it. */
483  mutt_set_flag(m, e, MUTT_DELETE, true);
484  mutt_set_flag(m, e, MUTT_PURGE, true);
485 
486  /* update the count for the status display */
487  PostCount = m->msg_count - m->msg_deleted;
488 
489  struct ListNode *np = NULL, *tmp = NULL;
490  STAILQ_FOREACH_SAFE(np, &hdr->env->userhdrs, entries, tmp)
491  {
492  size_t plen = mutt_istr_startswith(np->data, "X-Mutt-References:");
493  if (plen)
494  {
495  /* if a mailbox is currently open, look to see if the original message
496  * the user attempted to reply to is in this mailbox */
497  p = mutt_str_skip_email_wsp(np->data + plen);
498  if (!m_cur->id_hash)
499  m_cur->id_hash = mutt_make_id_hash(m_cur);
500  *cur = mutt_hash_find(m_cur->id_hash, p);
501 
502  if (*cur)
503  rc |= SEND_REPLY;
504  }
505  else if ((plen = mutt_istr_startswith(np->data, "X-Mutt-Fcc:")))
506  {
507  p = mutt_str_skip_email_wsp(np->data + plen);
508  mutt_buffer_strcpy(fcc, p);
510 
511  /* note that x-mutt-fcc was present. we do this because we want to add a
512  * default fcc if the header was missing, but preserve the request of the
513  * user to not make a copy if the header field is present, but empty.
514  * see http://dev.mutt.org/trac/ticket/3653 */
515  rc |= SEND_POSTPONED_FCC;
516  }
517  else if (((WithCrypto & APPLICATION_PGP) != 0) &&
518  /* this is generated by old neomutt versions */
519  (mutt_str_startswith(np->data, "Pgp:") ||
520  /* this is the new way */
521  mutt_str_startswith(np->data, "X-Mutt-PGP:")))
522  {
523  hdr->security = mutt_parse_crypt_hdr(strchr(np->data, ':') + 1, true, APPLICATION_PGP);
524  hdr->security |= APPLICATION_PGP;
525  }
526  else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
527  mutt_str_startswith(np->data, "X-Mutt-SMIME:"))
528  {
529  hdr->security = mutt_parse_crypt_hdr(strchr(np->data, ':') + 1, true, APPLICATION_SMIME);
530  hdr->security |= APPLICATION_SMIME;
531  }
532 #ifdef MIXMASTER
533  else if (mutt_str_startswith(np->data, "X-Mutt-Mix:"))
534  {
535  mutt_list_free(&hdr->chain);
536 
537  char *t = strtok(np->data + 11, " \t\n");
538  while (t)
539  {
541  t = strtok(NULL, " \t\n");
542  }
543  }
544 #endif
545  else
546  {
547  // skip header removal
548  continue;
549  }
550 
551  // remove the header
552  STAILQ_REMOVE(&hdr->env->userhdrs, np, ListNode, entries);
553  FREE(&np->data);
554  FREE(&np);
555  }
556 
557  const bool c_crypt_opportunistic_encrypt =
558  cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
559  if (c_crypt_opportunistic_encrypt)
560  crypt_opportunistic_encrypt(m_cur, hdr);
561 
562 cleanup:
563  if (m_cur != m)
564  {
565  hardclose(m);
566  ctx_free(&ctx);
567  }
568 
569  cs_subset_str_native_set(NeoMutt->sub, "delete", c_delete, NULL);
570  return rc;
571 }
572 
580 SecurityFlags mutt_parse_crypt_hdr(const char *p, bool set_empty_signas, SecurityFlags crypt_app)
581 {
582  char smime_cryptalg[1024] = { 0 };
583  char sign_as[1024] = { 0 };
584  char *q = NULL;
585  SecurityFlags flags = SEC_NO_FLAGS;
586 
587  if (!WithCrypto)
588  return SEC_NO_FLAGS;
589 
591  for (; p[0] != '\0'; p++)
592  {
593  switch (p[0])
594  {
595  case 'c':
596  case 'C':
597  q = smime_cryptalg;
598 
599  if (p[1] == '<')
600  {
601  for (p += 2; (p[0] != '\0') && (p[0] != '>') &&
602  (q < (smime_cryptalg + sizeof(smime_cryptalg) - 1));
603  *q++ = *p++)
604  {
605  }
606 
607  if (p[0] != '>')
608  {
609  mutt_error(_("Illegal S/MIME header"));
610  return SEC_NO_FLAGS;
611  }
612  }
613 
614  *q = '\0';
615  break;
616 
617  case 'e':
618  case 'E':
619  flags |= SEC_ENCRYPT;
620  break;
621 
622  case 'i':
623  case 'I':
624  flags |= SEC_INLINE;
625  break;
626 
627  /* This used to be the micalg parameter.
628  *
629  * It's no longer needed, so we just skip the parameter in order
630  * to be able to recall old messages. */
631  case 'm':
632  case 'M':
633  if (p[1] != '<')
634  break;
635 
636  for (p += 2; (p[0] != '\0') && (p[0] != '>'); p++)
637  ; // do nothing
638 
639  if (p[0] != '>')
640  {
641  mutt_error(_("Illegal crypto header"));
642  return SEC_NO_FLAGS;
643  }
644  break;
645 
646  case 'o':
647  case 'O':
648  flags |= SEC_OPPENCRYPT;
649  break;
650 
651  case 'a':
652  case 'A':
653 #ifdef USE_AUTOCRYPT
654  flags |= SEC_AUTOCRYPT;
655 #endif
656  break;
657 
658  case 'z':
659  case 'Z':
660 #ifdef USE_AUTOCRYPT
661  flags |= SEC_AUTOCRYPT_OVERRIDE;
662 #endif
663  break;
664 
665  case 's':
666  case 'S':
667  flags |= SEC_SIGN;
668  q = sign_as;
669 
670  if (p[1] == '<')
671  {
672  for (p += 2;
673  (p[0] != '\0') && (*p != '>') && (q < (sign_as + sizeof(sign_as) - 1));
674  *q++ = *p++)
675  {
676  }
677 
678  if (p[0] != '>')
679  {
680  mutt_error(_("Illegal crypto header"));
681  return SEC_NO_FLAGS;
682  }
683  }
684 
685  q[0] = '\0';
686  break;
687 
688  default:
689  mutt_error(_("Illegal crypto header"));
690  return SEC_NO_FLAGS;
691  }
692  }
693 
694  /* the cryptalg field must not be empty */
695  if (((WithCrypto & APPLICATION_SMIME) != 0) && *smime_cryptalg)
696  {
697  struct Buffer errmsg = mutt_buffer_make(0);
698  int rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
699  smime_cryptalg, &errmsg);
700 
701  if ((CSR_RESULT(rc) != CSR_SUCCESS) && !mutt_buffer_is_empty(&errmsg))
702  mutt_error("%s", mutt_buffer_string(&errmsg));
703 
704  mutt_buffer_dealloc(&errmsg);
705  }
706 
707  /* Set {Smime,Pgp}SignAs, if desired. */
708 
709  if (((WithCrypto & APPLICATION_PGP) != 0) && (crypt_app == APPLICATION_PGP) &&
710  (flags & SEC_SIGN) && (set_empty_signas || *sign_as))
711  {
712  cs_subset_str_string_set(NeoMutt->sub, "pgp_sign_as", sign_as, NULL);
713  }
714 
715  if (((WithCrypto & APPLICATION_SMIME) != 0) && (crypt_app == APPLICATION_SMIME) &&
716  (flags & SEC_SIGN) && (set_empty_signas || *sign_as))
717  {
718  cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", sign_as, NULL);
719  }
720 
721  return flags;
722 }
723 
736 int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new,
737  struct Email *e, bool resend)
738 {
739  struct Message *msg = NULL;
740  struct Body *b = NULL;
741  FILE *fp_body = NULL;
742  int rc = -1;
743  struct State s = { 0 };
744  SecurityFlags sec_type;
745  struct Envelope *protected_headers = NULL;
746  struct Buffer *file = NULL;
747 
748  if (!fp && !(msg = mx_msg_open(m, e->msgno)))
749  return -1;
750 
751  if (!fp)
752  fp = msg->fp;
753 
754  fp_body = fp;
755 
756  /* parse the message header and MIME structure */
757 
758  fseeko(fp, e->offset, SEEK_SET);
759  e_new->offset = e->offset;
760  /* enable header weeding for resent messages */
761  e_new->env = mutt_rfc822_read_header(fp, e_new, true, resend);
762  e_new->body->length = e->body->length;
763  mutt_parse_part(fp, e_new->body);
764 
765  /* If resending a message, don't keep message_id or mail_followup_to.
766  * Otherwise, we are resuming a postponed message, and want to keep those
767  * headers if they exist. */
768  if (resend)
769  {
770  FREE(&e_new->env->message_id);
771  FREE(&e_new->env->mail_followup_to);
772  }
773 
774  /* decrypt pgp/mime encoded messages */
775 
776  if (((WithCrypto & APPLICATION_PGP) != 0) &&
777  (sec_type = mutt_is_multipart_encrypted(e_new->body)))
778  {
779  e_new->security |= sec_type;
780  if (!crypt_valid_passphrase(sec_type))
781  goto bail;
782 
783  mutt_message(_("Decrypting message..."));
784  if ((crypt_pgp_decrypt_mime(fp, &fp_body, e_new->body, &b) == -1) || !b)
785  {
786  mutt_error(_("Could not decrypt PGP message"));
787  goto bail;
788  }
789 
790  mutt_body_free(&e_new->body);
791  e_new->body = b;
792 
793  if (b->mime_headers)
794  {
795  protected_headers = b->mime_headers;
796  b->mime_headers = NULL;
797  }
798 
800  }
801 
802  /* remove a potential multipart/signed layer - useful when
803  * resending messages */
804  if ((WithCrypto != 0) && mutt_is_multipart_signed(e_new->body))
805  {
806  e_new->security |= SEC_SIGN;
807  if (((WithCrypto & APPLICATION_PGP) != 0) &&
808  mutt_istr_equal(mutt_param_get(&e_new->body->parameter, "protocol"),
809  "application/pgp-signature"))
810  {
811  e_new->security |= APPLICATION_PGP;
812  }
813  else if (WithCrypto & APPLICATION_SMIME)
814  e_new->security |= APPLICATION_SMIME;
815 
816  /* destroy the signature */
817  mutt_body_free(&e_new->body->parts->next);
818  e_new->body = mutt_remove_multipart(e_new->body);
819 
820  if (e_new->body->mime_headers)
821  {
822  mutt_env_free(&protected_headers);
823  protected_headers = e_new->body->mime_headers;
824  e_new->body->mime_headers = NULL;
825  }
826  }
827 
828  /* We don't need no primary multipart.
829  * Note: We _do_ preserve messages!
830  *
831  * XXX - we don't handle multipart/alternative in any
832  * smart way when sending messages. However, one may
833  * consider this a feature. */
834  if (e_new->body->type == TYPE_MULTIPART)
835  e_new->body = mutt_remove_multipart(e_new->body);
836 
837  s.fp_in = fp_body;
838 
839  file = mutt_buffer_pool_get();
840 
841  /* create temporary files for all attachments */
842  for (b = e_new->body; b; b = b->next)
843  {
844  /* what follows is roughly a receive-mode variant of
845  * mutt_get_tmp_attachment () from muttlib.c */
846 
847  mutt_buffer_reset(file);
848  if (b->filename)
849  {
850  mutt_buffer_strcpy(file, b->filename);
852  }
853  else
854  {
855  /* avoid Content-Disposition: header with temporary filename */
856  b->use_disp = false;
857  }
858 
859  /* set up state flags */
860 
861  s.flags = 0;
862 
863  if (b->type == TYPE_TEXT)
864  {
865  if (mutt_istr_equal("yes",
866  mutt_param_get(&b->parameter, "x-mutt-noconv")))
867  {
868  b->noconv = true;
869  }
870  else
871  {
872  s.flags |= MUTT_CHARCONV;
873  b->noconv = false;
874  }
875 
876  mutt_param_delete(&b->parameter, "x-mutt-noconv");
877  }
878 
879  mutt_adv_mktemp(file);
880  s.fp_out = mutt_file_fopen(mutt_buffer_string(file), "w");
881  if (!s.fp_out)
882  goto bail;
883 
884  if (((WithCrypto & APPLICATION_PGP) != 0) &&
885  ((sec_type = mutt_is_application_pgp(b)) & (SEC_ENCRYPT | SEC_SIGN)))
886  {
887  if (sec_type & SEC_ENCRYPT)
888  {
889  if (!crypt_valid_passphrase(APPLICATION_PGP))
890  goto bail;
891  mutt_message(_("Decrypting message..."));
892  }
893 
894  if (mutt_body_handler(b, &s) < 0)
895  {
896  mutt_error(_("Decryption failed"));
897  goto bail;
898  }
899 
900  if ((b == e_new->body) && !protected_headers)
901  {
902  protected_headers = b->mime_headers;
903  b->mime_headers = NULL;
904  }
905 
906  e_new->security |= sec_type;
907  b->type = TYPE_TEXT;
908  mutt_str_replace(&b->subtype, "plain");
909  mutt_param_delete(&b->parameter, "x-action");
910  }
911  else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
912  ((sec_type = mutt_is_application_smime(b)) & (SEC_ENCRYPT | SEC_SIGN)))
913  {
914  if (sec_type & SEC_ENCRYPT)
915  {
916  if (!crypt_valid_passphrase(APPLICATION_SMIME))
917  goto bail;
918  crypt_smime_getkeys(e_new->env);
919  mutt_message(_("Decrypting message..."));
920  }
921 
922  if (mutt_body_handler(b, &s) < 0)
923  {
924  mutt_error(_("Decryption failed"));
925  goto bail;
926  }
927 
928  e_new->security |= sec_type;
929  b->type = TYPE_TEXT;
930  mutt_str_replace(&b->subtype, "plain");
931  }
932  else
933  mutt_decode_attachment(b, &s);
934 
935  if (mutt_file_fclose(&s.fp_out) != 0)
936  goto bail;
937 
939  b->unlink = true;
940 
942 
943  mutt_body_free(&b->parts);
944  if (b->email)
945  b->email->body = NULL; /* avoid dangling pointer */
946  }
947 
948  const bool c_crypt_protected_headers_read =
949  cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read");
950  if (c_crypt_protected_headers_read && protected_headers && protected_headers->subject &&
951  !mutt_str_equal(e_new->env->subject, protected_headers->subject))
952  {
953  mutt_str_replace(&e_new->env->subject, protected_headers->subject);
954  }
955  mutt_env_free(&protected_headers);
956 
957  /* Fix encryption flags. */
958 
959  /* No inline if multipart. */
960  if ((WithCrypto != 0) && (e_new->security & SEC_INLINE) && e_new->body->next)
961  e_new->security &= ~SEC_INLINE;
962 
963  /* Do we even support multiple mechanisms? */
964  e_new->security &= WithCrypto | ~(APPLICATION_PGP | APPLICATION_SMIME);
965 
966  /* Theoretically, both could be set. Take the one the user wants to set by default. */
967  if ((e_new->security & APPLICATION_PGP) && (e_new->security & APPLICATION_SMIME))
968  {
969  const bool c_smime_is_default =
970  cs_subset_bool(NeoMutt->sub, "smime_is_default");
971  if (c_smime_is_default)
972  e_new->security &= ~APPLICATION_PGP;
973  else
974  e_new->security &= ~APPLICATION_SMIME;
975  }
976 
978 
979  rc = 0;
980 
981 bail:
982 
983  /* that's it. */
985  if (fp_body != fp)
986  mutt_file_fclose(&fp_body);
987  if (msg)
988  mx_msg_close(m, &msg);
989 
990  if (rc == -1)
991  {
992  mutt_env_free(&e_new->env);
993  mutt_body_free(&e_new->body);
994  }
995 
996  return rc;
997 }
#define CSR_SUCCESS
Action completed successfully.
Definition: set.h:35
void * mutt_hash_find(const struct HashTable *table, const char *strkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:354
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
Convenience wrapper for the gui headers.
The "current" mailbox.
Definition: context.h:37
void mutt_decode_attachment(struct Body *b, struct State *s)
Decode an email&#39;s attachment.
Definition: handler.c:1861
struct MuttWindow * window_find_child(struct MuttWindow *win, enum WindowType type)
Recursively find a child Window of a given type.
Definition: mutt_window.c:550
void mutt_stamp_attachment(struct Body *a)
Timestamp an Attachment.
Definition: sendlib.c:892
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:46
#define NONULL(x)
Definition: string2.h:37
struct Envelope * mime_headers
Memory hole protected headers.
Definition: body.h:63
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
int msg_count
Total number of messages.
Definition: mailbox.h:91
void mutt_buffer_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:79
#define mutt_set_flag(m, e, flag, bf)
Definition: protos.h:66
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
#define WithCrypto
Definition: lib.h:113
bool mx_mbox_open(struct Mailbox *m, OpenMailboxFlags flags)
Open a mailbox and parse it.
Definition: mx.c:304
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:96
#define SEND_POSTPONED_FCC
Used by mutt_get_postponed() to signal that the x-mutt-fcc header field was present.
Definition: send.h:48
IMAP network mailbox.
The envelope/body of an email.
Definition: email.h:37
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
void simple_dialog_free(struct MuttWindow **ptr)
Destroy a simple index Dialog.
Definition: simple.c:165
int imap_path_status(const char *path, bool queue)
Refresh the number of total and new messages.
Definition: imap.c:1235
Definition: lib.h:67
Data passed to a notification function.
Definition: observer.h:39
struct AddressList mail_followup_to
Email&#39;s &#39;mail-followup-to&#39;.
Definition: envelope.h:63
struct MuttWindow * win
Window that changed.
Definition: mutt_window.h:217
struct Body * body
List of MIME parts.
Definition: email.h:91
int msg_deleted
Number of deleted messages.
Definition: mailbox.h:96
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
Structs that make up an email.
String processing routines to generate the mail index.
An Event that happened to a Window.
Definition: mutt_window.h:215
#define mutt_error(...)
Definition: logging.h:88
The "currently-open" mailbox.
#define SEC_NO_FLAGS
No flags are set.
Definition: lib.h:74
Convenience wrapper for the send headers.
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:75
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
struct HashTable * mutt_make_id_hash(struct Mailbox *m)
Create a Hash Table for message-ids.
Definition: mutt_thread.c:1658
char * realpath
Used for duplicate detection, context comparison, and the sidebar.
Definition: mailbox.h:84
NeoMutt Logging.
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
#define MUTT_FORMAT_ARROWCURSOR
Reserve space for arrow_cursor.
Definition: format_flags.h:35
bool noconv
Don&#39;t do character set conversion.
Definition: body.h:73
struct Buffer mutt_buffer_make(size_t size)
Make a new buffer on the stack.
Definition: buffer.c:61
SecurityFlags mutt_is_application_smime(struct Body *b)
Does the message use S/MIME?
Definition: crypt.c:623
A config-change event.
Definition: subset.h:69
String manipulation buffer.
Definition: buffer.h:33
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
struct ListHead userhdrs
user defined headers
Definition: envelope.h:83
#define _(a)
Definition: message.h:28
Window is about to be deleted.
Definition: mutt_window.h:206
struct Body * next
next attachment in the list
Definition: body.h:53
FILE * fp_out
File to write to.
Definition: state.h:47
Messages to be purged (bypass trash)
Definition: mutt.h:96
#define SEC_AUTOCRYPT
(Autocrypt) Message will be, or was Autocrypt encrypt+signed
Definition: lib.h:84
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:71
enum MxStatus mx_mbox_close(struct Mailbox *m)
Save changes and close mailbox.
Definition: mx.c:610
int mutt_buffer_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
Flags to control mutt_expando_format()
All user-callable functions.
static void hardclose(struct Mailbox *m)
try hard to close a mailbox
Definition: postpone.c:403
#define SEND_POSTPONED
Recall a postponed email.
Definition: send.h:44
static int postponed_window_observer(struct NotifyCallback *nc)
Notification that a Window has changed - Implements observer_t.
Definition: postpone.c:287
Container for Accounts, Notifications.
Definition: neomutt.h:36
FILE * fp_in
File to read from.
Definition: state.h:46
void mutt_buffer_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:603
MuttWindow has changed, NotifyWindow, EventWindow.
Definition: notify_type.h:53
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:122
The body of an email.
Definition: body.h:34
Status Bar containing extra info about the Index/Pager/etc.
Definition: mutt_window.h:102
int mutt_get_postponed(struct Mailbox *m_cur, struct Email *hdr, struct Email **cur, struct Buffer *fcc)
Recall a postponed message.
Definition: postpone.c:424
QuadOption
Possible values for a quad-option.
Definition: quad.h:35
Convenience wrapper for the config headers.
void(* make_entry)(struct Menu *menu, char *buf, size_t buflen, int line)
Definition: lib.h:105
#define CSR_RESULT(x)
Definition: set.h:52
#define SEC_INLINE
Email has an inline signature.
Definition: lib.h:82
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
int event_subtype
Send: Event subtype, e.g. NT_ACCOUNT_ADD.
Definition: observer.h:43
enum NotifyType event_type
Send: Event type, e.g. NT_ACCOUNT.
Definition: observer.h:42
Some miscellaneous functions.
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition: subset.c:305
enum MxStatus mx_mbox_check(struct Mailbox *m)
Check for new mail - Wrapper for MxOps::mbox_check()
Definition: mx.c:1119
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1186
bool crypt_valid_passphrase(SecurityFlags flags)
Check that we have a usable passphrase, ask if not.
Definition: crypt.c:135
int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name, const char *value, struct Buffer *err)
Set a config item by string.
Definition: subset.c:408
char * message_id
Message ID.
Definition: envelope.h:69
struct Notify * notify
Notifications: NotifyWindow, EventWindow.
Definition: mutt_window.h:138
struct HashTable * id_hash
Hash Table by msg id.
Definition: mailbox.h:127
static void post_make_entry(struct Menu *menu, char *buf, size_t buflen, int line)
Format a menu item for the email list - Implements Menu::make_entry() -.
Definition: postpone.c:248
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
Does the message have encrypted parts?
Definition: crypt.c:460
Many unsorted constants and some structs.
API for mailboxes.
void mailbox_free(struct Mailbox **ptr)
Free a Mailbox.
Definition: mailbox.c:87
void crypt_opportunistic_encrypt(struct Mailbox *m, struct Email *e)
Can all recipients be determined.
Definition: crypt.c:1054
struct MuttWindow * win
Window holding the Menu.
Definition: lib.h:76
Sort by the order the messages appear in the mailbox.
Definition: sort2.h:48
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1409
struct Envelope * env
Envelope information.
Definition: email.h:90
Convenience wrapper for the core headers.
void mx_fastclose_mailbox(struct Mailbox *m)
free up memory associated with the Mailbox
Definition: mx.c:429
void mutt_buffer_dealloc(struct Buffer *buf)
Release the memory allocated by a buffer.
Definition: buffer.c:294
enum MailboxType imap_path_probe(const char *path, const struct stat *st)
Is this an IMAP Mailbox? - Implements MxOps::path_probe() -.
Definition: imap.c:2402
SecurityFlags mutt_is_multipart_signed(struct Body *b)
Is a message signed?
Definition: crypt.c:420
void mutt_rfc3676_space_unstuff(struct Email *e)
Remove RFC3676 space stuffing.
Definition: rfc3676.c:496
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:112
bool notify_observer_add(struct Notify *notify, enum NotifyType type, observer_t callback, void *global_data)
Add an observer to an object.
Definition: notify.c:189
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
void * global_data
Data from notify_observer_add()
Definition: observer.h:45
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:916
char * subtype
content-type subtype
Definition: body.h:37
struct ListNode * mutt_list_insert_tail(struct ListHead *h, char *s)
Append a string to the end of a List.
Definition: list.c:64
short cs_subset_sort(const struct ConfigSubset *sub, const char *name)
Get a sort config item by name.
Definition: helpers.c:292
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
void mutt_param_delete(struct ParameterList *pl, const char *attribute)
Delete a matching Parameter.
Definition: parameter.c:142
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:88
Prototypes for many functions.
struct ListHead chain
Mixmaster chain.
Definition: email.h:102
Select a postponed email.
Definition: type.h:56
struct Body * mutt_remove_multipart(struct Body *b)
Extract the multipart body if it exists.
Definition: multipart.c:126
struct Context * ctx_new(struct Mailbox *m)
Create a new Context.
Definition: context.c:77
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:87
void * mdata
Private data.
Definition: lib.h:155
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:160
A local copy of an email.
Definition: mxapi.h:41
Create/manipulate threading in emails.
LOFF_T length
length (in bytes) of attachment
Definition: body.h:45
Messages to be deleted.
Definition: mutt.h:94
A mailbox.
Definition: mailbox.h:81
int top
Entry that is the top of the current page.
Definition: lib.h:89
#define SEC_AUTOCRYPT_OVERRIDE
(Autocrypt) Indicates manual set/unset of encryption
Definition: lib.h:85
Type: &#39;text/*&#39;.
Definition: mime.h:38
No changes.
Definition: mxapi.h:78
void crypt_smime_getkeys(struct Envelope *env)
Wrapper for CryptModuleSpecs::smime_getkeys()
Definition: cryptglue.c:463
Match patterns to emails.
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
static short PostCount
Definition: postpone.c:116
RFC3676 Format Flowed routines.
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
API for encryption/signing of emails.
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
#define MUTT_QUIET
Do not print any messages.
Definition: mxapi.h:64
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:172
void sbar_set_title(struct MuttWindow *win, const char *title)
Set the title for the Simple Bar.
Definition: sbar.c:221
bool custom_search
The menu implements its own non-Menusearch()-compatible search, trickle OP_SEARCH*.
Definition: lib.h:93
void mutt_update_num_postponed(void)
Force the update of the number of postponed messages.
Definition: postpone.c:240
int pagelen
Number of entries per screen.
Definition: lib.h:74
struct Notify * notify
Notifications handler.
Definition: neomutt.h:38
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib...
Definition: email.h:39
static int postponed_config_observer(struct NotifyCallback *nc)
Notification that a Config Variable has changed - Implements observer_t.
Definition: postpone.c:263
#define SEND_REPLY
Reply to sender.
Definition: send.h:40
#define SEC_OPPENCRYPT
Opportunistic encrypt mode.
Definition: lib.h:83
enum QuadOption cs_subset_quad(const struct ConfigSubset *sub, const char *name)
Get a quad-value config item by name.
Definition: helpers.c:218
size_t mutt_buffer_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:312
unsigned int type
content-type primary type, ContentType
Definition: body.h:65
int max
Number of entries in the menu.
Definition: lib.h:71
void * event_data
Data from notify_send()
Definition: observer.h:44
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:84
Config has changed, NotifyConfig, EventConfig.
Definition: notify_type.h:42
#define SEC_SIGN
Email is signed.
Definition: lib.h:76
int mutt_num_postponed(struct Mailbox *m, bool force)
Return the number of postponed messages.
Definition: postpone.c:127
void mutt_parse_part(FILE *fp, struct Body *b)
Parse a MIME part.
Definition: parse.c:1678
SecurityFlags mutt_parse_crypt_hdr(const char *p, bool set_empty_signas, SecurityFlags crypt_app)
Parse a crypto header string.
Definition: postpone.c:580
int crypt_pgp_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:212
char * data
String.
Definition: list.h:36
char * subject
Email&#39;s subject.
Definition: envelope.h:66
Type: &#39;multipart/*&#39;.
Definition: mime.h:37
bool use_disp
Content-Disposition uses filename= ?
Definition: body.h:68
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
SecurityFlags mutt_is_application_pgp(struct Body *b)
Does the message use PGP?
Definition: crypt.c:565
bool unlink
If true, filename should be unlink()ed before free()ing this structure.
Definition: body.h:69
FILE * fp
pointer to the message data
Definition: mxapi.h:43
Postpone Dialog, dlg_select_postponed_email()
Definition: mutt_window.h:89
void mutt_adv_mktemp(struct Buffer *buf)
Create a temporary file.
Definition: muttlib.c:83
#define mutt_message(...)
Definition: logging.h:87
#define FREE(x)
Definition: memory.h:40
char * mutt_param_get(const struct ParameterList *pl, const char *s)
Find a matching Parameter.
Definition: parameter.c:84
struct Mailbox * mx_path_resolve(const char *path)
Get a Mailbox for a path.
Definition: mx.c:1668
static bool UpdateNumPostponed
Definition: postpone.c:117
Mapping between user-readable string and a constant.
Definition: mapping.h:31
Keep track when processing files.
Definition: state.h:44
bool notify_observer_remove(struct Notify *notify, observer_t callback, void *global_data)
Remove an observer from an object.
Definition: notify.c:228
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1604
struct MuttWindow * simple_dialog_new(enum MenuType mtype, enum WindowType wtype, const struct Mapping *help_data)
Create a simple index Dialog.
Definition: simple.c:128
char * d_filename
filename to be used for the content-disposition header.
Definition: body.h:47
void ctx_free(struct Context **ptr)
Free a Context.
Definition: context.c:49
Handling of global boolean variables.
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
Log at debug level 5.
Definition: logging.h:44
Convenience wrapper for the library headers.
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:589
A List node for strings.
Definition: list.h:34
#define MUTT_NOSORT
Do not sort the mailbox after opening it.
Definition: mxapi.h:61
bool mutt_buffer_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition: buffer.c:252
Decide how to display email content.
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1124
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
void * wdata
Private data.
Definition: mutt_window.h:145
&#39;IMAP&#39; Mailbox type
Definition: mailbox.h:53
#define N_(a)
Definition: message.h:32
struct Email * email
header information for message/rfc822
Definition: body.h:55
const char * name
Name of config item that changed.
Definition: subset.h:72
WHERE bool OptNews
(pseudo) used to change reader mode
Definition: options.h:44
MxStatus
Return values from mbox_check(), mbox_check_stats(), mbox_snc(), and mbox_close() ...
Definition: mxapi.h:75
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
return a stream pointer for a message
Definition: mx.c:1140
Log at debug level 3.
Definition: logging.h:42
int mutt_prepare_template(FILE *fp, struct Mailbox *m, struct Email *e_new, struct Email *e, bool resend)
Prepare a message template.
Definition: postpone.c:736
static struct Email * dlg_select_postponed_email(struct Mailbox *m)
Create a Menu to select a postponed message.
Definition: postpone.c:314
The header of an Email.
Definition: envelope.h:54
int msgno
Number displayed to the user.
Definition: email.h:87
User answered &#39;Yes&#39;, or assume &#39;Yes&#39;.
Definition: quad.h:39
An error occurred.
Definition: mxapi.h:77
int mutt_search_command(struct Mailbox *m, struct Menu *menu, int cur, int op)
Perform a search.
Definition: pattern.c:496