]> granicus.if.org Git - neomutt/blobdiff - sendlib.c
merge: light refactoring
[neomutt] / sendlib.c
index deacc9db5628d8eb46c5d1a9b6ab585a7887f5c9..5f2bf3df45dc3caa8919e78af9a1a470abf602a7 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -4,6 +4,7 @@
  *
  * @authors
  * Copyright (C) 1996-2002,2009-2012 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
  *
  * @copyright
  * This program is free software: you can redistribute it and/or modify it under
@@ -30,7 +31,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <iconv.h>
-#include <inttypes.h>
+#include <inttypes.h> // IWYU pragma: keep
 #include <limits.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <unistd.h>
 #include "mutt/mutt.h"
 #include "address/lib.h"
-#include "config/lib.h"
 #include "email/lib.h"
+#include "core/lib.h"
 #include "mutt.h"
 #include "sendlib.h"
 #include "context.h"
 #include "copy.h"
 #include "curs_lib.h"
 #include "filter.h"
+#include "format_flags.h"
 #include "globals.h"
 #include "handler.h"
-#include "hook.h"
-#include "mailbox.h"
+#include "mutt_mailbox.h"
 #include "mutt_parse.h"
 #include "mutt_window.h"
 #include "muttlib.h"
 #include "mx.h"
 #include "ncrypt/ncrypt.h"
 #include "options.h"
+#include "pager.h"
 #include "send.h"
 #include "smtp.h"
 #include "state.h"
@@ -72,6 +74,9 @@
 #else
 #define EX_OK 0
 #endif
+#ifdef USE_AUTOCRYPT
+#include "autocrypt/autocrypt.h"
+#endif
 
 /* These Config Variables are only used in sendlib.c */
 bool C_Allow8bit; ///< Config: Allow 8-bit messages, don't use quoted-printable or base64
@@ -92,6 +97,51 @@ bool C_UseEnvelopeFrom; ///< Config: Set the envelope sender of the message
 bool C_UserAgent;       ///< Config: Add a 'User-Agent' head to outgoing mail
 short C_WrapHeaders;    ///< Config: Width to wrap headers in outgoing messages
 
+#define MUTT_RANDTAG_LEN 16
+
+/**
+ * struct B64Context - Cursor for the Base64 conversion
+ */
+struct B64Context
+{
+  char buffer[3];
+  short size;
+  short linelen;
+};
+
+/**
+ * struct ContentState - Info about the body of an email
+ */
+struct ContentState
+{
+  bool from;
+  int whitespace;
+  bool dot;
+  int linelen;
+  bool was_cr;
+};
+
+/**
+ * The next array/enum pair is used to to keep track of user headers that
+ * override pre-defined headers NeoMutt would emit. Keep the array sorted and
+ * in sync with the enum.
+ */
+static const char *const userhdrs_override_headers[] = {
+  "content-type:",
+  "user-agent:",
+};
+
+enum UserHdrsOverrideIdx
+{
+  USERHDRS_OVERRIDE_CONTENT_TYPE,
+  USERHDRS_OVERRIDE_USER_AGENT,
+};
+
+struct UserHdrsOverride
+{
+  bool is_overridden[mutt_array_size(userhdrs_override_headers)];
+};
+
 /**
  * encode_quoted - Encode text as quoted printable
  * @param fc     Cursor for converting a file's encoding
@@ -226,16 +276,6 @@ static void encode_quoted(struct FgetConv *fc, FILE *fp_out, bool istext)
   }
 }
 
-/**
- * struct B64Context - Cursor for the Base64 conversion
- */
-struct B64Context
-{
-  char buffer[3];
-  short size;
-  short linelen;
-};
-
 /**
  * b64_init - Set up the base64 conversion
  * @param bctx Cursor for the base64 conversion
@@ -356,52 +396,58 @@ static void encode_8bit(struct FgetConv *fc, FILE *fp_out)
  */
 int mutt_write_mime_header(struct Body *a, FILE *fp)
 {
+  if (!a || !fp)
+    return -1;
+
+  int len;
+  int tmplen;
+  char buf[256] = { 0 };
+
   fprintf(fp, "Content-Type: %s/%s", TYPE(a), a->subtype);
 
   if (!TAILQ_EMPTY(&a->parameter))
   {
-    size_t len = 25 + mutt_str_strlen(a->subtype); /* approximate len. of content-type */
+    len = 25 + mutt_str_strlen(a->subtype); /* approximate len. of content-type */
 
     struct Parameter *np = NULL;
     TAILQ_FOREACH(np, &a->parameter, entries)
     {
-      char *tmp = NULL;
-
-      if (!np->value)
+      if (!np->attribute || !np->value)
         continue;
 
-      fputc(';', fp);
-
-      char buf[8192] = { 0 };
-      tmp = mutt_str_strdup(np->value);
-      const int encode = rfc2231_encode_string(&tmp);
-      mutt_addr_cat(buf, sizeof(buf), tmp, MimeSpecials);
-
-      /* Dirty hack to make messages readable by Outlook Express for the Mac:
-       * force quotes around the boundary parameter even when they aren't
-       * needed.  */
-
-      if ((mutt_str_strcasecmp(np->attribute, "boundary") == 0) && (strcmp(buf, tmp) == 0))
+      struct ParameterList pl_conts = rfc2231_encode_string(np->attribute, np->value);
+      struct Parameter *cont = NULL;
+      TAILQ_FOREACH(cont, &pl_conts, entries)
       {
-        snprintf(buf, sizeof(buf), "\"%s\"", tmp);
-      }
+        fputc(';', fp);
 
-      FREE(&tmp);
+        buf[0] = 0;
+        mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
 
-      const int tmplen = mutt_str_strlen(buf) + mutt_str_strlen(np->attribute) + 1;
+        /* Dirty hack to make messages readable by Outlook Express
+         * for the Mac: force quotes around the boundary parameter
+         * even when they aren't needed.
+         */
+        if (!mutt_str_strcasecmp(cont->attribute, "boundary") &&
+            !mutt_str_strcmp(buf, cont->value))
+          snprintf(buf, sizeof(buf), "\"%s\"", cont->value);
 
-      if (len + tmplen + 2 > 76)
-      {
-        fputs("\n\t", fp);
-        len = tmplen + 8;
-      }
-      else
-      {
-        fputc(' ', fp);
-        len += tmplen + 1;
+        tmplen = mutt_str_strlen(buf) + mutt_str_strlen(cont->attribute) + 1;
+        if (len + tmplen + 2 > 76)
+        {
+          fputs("\n\t", fp);
+          len = tmplen + 1;
+        }
+        else
+        {
+          fputc(' ', fp);
+          len += tmplen + 1;
+        }
+
+        fprintf(fp, "%s=%s", cont->attribute, buf);
       }
 
-      fprintf(fp, "%s%s=%s", np->attribute, encode ? "*" : "", buf);
+      mutt_param_free(&pl_conts);
     }
   }
 
@@ -420,6 +466,7 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
     if (a->disposition < sizeof(dispstr) / sizeof(char *))
     {
       fprintf(fp, "Content-Disposition: %s", dispstr[a->disposition]);
+      len = 21 + mutt_str_strlen(dispstr[a->disposition]);
 
       if (a->use_disp && (a->disposition != DISP_INLINE))
       {
@@ -429,8 +476,6 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
 
         if (fn)
         {
-          char *tmp = NULL;
-
           /* Strip off the leading path... */
           char *t = strrchr(fn, '/');
           if (t)
@@ -438,13 +483,30 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
           else
             t = fn;
 
-          char buf[256];
-          buf[0] = '\0';
-          tmp = mutt_str_strdup(t);
-          const int encode = rfc2231_encode_string(&tmp);
-          mutt_addr_cat(buf, sizeof(buf), tmp, MimeSpecials);
-          FREE(&tmp);
-          fprintf(fp, "; filename%s=%s", encode ? "*" : "", buf);
+          struct ParameterList pl_conts = rfc2231_encode_string("filename", t);
+          struct Parameter *cont = NULL;
+          TAILQ_FOREACH(cont, &pl_conts, entries)
+          {
+            fputc(';', fp);
+            buf[0] = 0;
+            mutt_addr_cat(buf, sizeof(buf), cont->value, MimeSpecials);
+
+            tmplen = mutt_str_strlen(buf) + mutt_str_strlen(cont->attribute) + 1;
+            if (len + tmplen + 2 > 76)
+            {
+              fputs("\n\t", fp);
+              len = tmplen + 1;
+            }
+            else
+            {
+              fputc(' ', fp);
+              len += tmplen + 1;
+            }
+
+            fprintf(fp, "%s=%s", cont->attribute, buf);
+          }
+
+          mutt_param_free(&pl_conts);
         }
       }
 
@@ -459,8 +521,15 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
   if (a->encoding != ENC_7BIT)
     fprintf(fp, "Content-Transfer-Encoding: %s\n", ENCODING(a->encoding));
 
-  if (C_CryptProtectedHeadersWrite && a->mime_headers)
+  if ((C_CryptProtectedHeadersWrite
+#ifdef USE_AUTOCRYPT
+       || C_Autocrypt
+#endif
+       ) &&
+      a->mime_headers)
+  {
     mutt_rfc822_write_header(fp, a->mime_headers, NULL, MUTT_WRITE_HEADER_MIME, false, false);
+  }
 
   /* Do NOT add the terminator here!!! */
   return ferror(fp) ? -1 : 0;
@@ -517,7 +586,7 @@ int mutt_write_mime_body(struct Body *a, FILE *fp)
 
   /* This is pretty gross, but it's the best solution for now... */
   if (((WithCrypto & APPLICATION_PGP) != 0) && (a->type == TYPE_APPLICATION) &&
-      (mutt_str_strcmp(a->subtype, "pgp-encrypted") == 0))
+      (mutt_str_strcmp(a->subtype, "pgp-encrypted") == 0) && !a->filename)
   {
     fputs("Version: 1\n", fp);
     return 0;
@@ -565,29 +634,17 @@ int mutt_write_mime_body(struct Body *a, FILE *fp)
 
 /**
  * mutt_generate_boundary - Create a unique boundary id for a MIME part
- * @param parm MIME part
+ * @param pl MIME part
  */
-void mutt_generate_boundary(struct ParameterList *parm)
+void mutt_generate_boundary(struct ParameterList *pl)
 {
   char rs[MUTT_RANDTAG_LEN + 1];
 
   mutt_rand_base32(rs, sizeof(rs) - 1);
   rs[MUTT_RANDTAG_LEN] = 0;
-  mutt_param_set(parm, "boundary", rs);
+  mutt_param_set(pl, "boundary", rs);
 }
 
-/**
- * struct ContentState - Info about the body of an email
- */
-struct ContentState
-{
-  bool from;
-  int whitespace;
-  bool dot;
-  int linelen;
-  bool was_cr;
-};
-
 /**
  * update_content_info - Cache some info about an email
  * @param info   Info about an Attachment
@@ -621,11 +678,7 @@ static void update_content_info(struct Content *info, struct ContentState *s,
     if (was_cr)
     {
       was_cr = false;
-      if (ch != '\n')
-      {
-        info->binary = true;
-      }
-      else
+      if (ch == '\n')
       {
         if (whitespace)
           info->space = true;
@@ -638,6 +691,8 @@ static void update_content_info(struct Content *info, struct ContentState *s,
         linelen = 0;
         continue;
       }
+
+      info->binary = true;
     }
 
     linelen++;
@@ -745,7 +800,7 @@ static void update_content_info(struct Content *info, struct ContentState *s,
  * in.
  */
 static size_t convert_file_to(FILE *fp, const char *fromcode, int ncodes,
-                              const char **tocodes, int *tocode, struct Content *info)
+                              char const *const *tocodes, int *tocode, struct Content *info)
 {
   char bufi[256], bufu[512], bufo[4 * sizeof(bufi)];
   size_t ret;
@@ -783,7 +838,7 @@ static size_t convert_file_to(FILE *fp, const char *fromcode, int ncodes,
     const char *ib = bufi;
     char *ob = bufu;
     size_t obl = sizeof(bufu);
-    n = iconv(cd1, (ICONV_CONST char **) (ibl ? &ib : 0), &ibl, &ob, &obl);
+    n = iconv(cd1, (ICONV_CONST char **) ((ibl != 0) ? &ib : 0), &ibl, &ob, &obl);
     /* assert(n == (size_t)(-1) || !n); */
     if ((n == (size_t)(-1)) && (((errno != EINVAL) && (errno != E2BIG)) || (ib == bufi)))
     {
@@ -937,7 +992,7 @@ static size_t convert_file_from_to(FILE *fp, const char *fromcodes, const char *
         continue;
       fcode = mutt_str_substr_dup(c, c1);
 
-      ret = convert_file_to(fp, fcode, ncodes, (const char **) tcode, &cn, info);
+      ret = convert_file_to(fp, fcode, ncodes, (char const *const *) tcode, &cn, info);
       if (ret != (size_t)(-1))
       {
         *fromcode = fcode;
@@ -951,7 +1006,7 @@ static size_t convert_file_from_to(FILE *fp, const char *fromcodes, const char *
   else
   {
     /* There is only one fromcode */
-    ret = convert_file_to(fp, fromcodes, ncodes, (const char **) tcode, &cn, info);
+    ret = convert_file_to(fp, fromcodes, ncodes, (char const *const *) tcode, &cn, info);
     if (ret != (size_t)(-1))
     {
       *tocode = tcode[cn];
@@ -1015,9 +1070,7 @@ struct Content *mutt_get_content_info(const char *fname, struct Body *b)
   if (b && (b->type == TYPE_TEXT) && (!b->noconv && !b->force_charset))
   {
     char *chs = mutt_param_get(&b->parameter, "charset");
-    char *fchs = b->use_disp ?
-                     ((C_AttachCharset && *C_AttachCharset) ? C_AttachCharset : C_Charset) :
-                     C_Charset;
+    char *fchs = b->use_disp ? (C_AttachCharset ? C_AttachCharset : C_Charset) : C_Charset;
     if (C_Charset && (chs || C_SendCharset) &&
         (convert_file_from_to(fp, fchs, chs ? chs : C_SendCharset, &fromcode,
                               &tocode, info) != (size_t)(-1)))
@@ -1066,22 +1119,18 @@ struct Content *mutt_get_content_info(const char *fname, struct Body *b)
  * The longest match is used so that we can match 'ps.gz' when 'gz' also
  * exists.
  */
-int mutt_lookup_mime_type(struct Body *att, const char *path)
+enum ContentType mutt_lookup_mime_type(struct Body *att, const char *path)
 {
   FILE *fp = NULL;
   char *p = NULL, *q = NULL, *ct = NULL;
   char buf[PATH_MAX];
-  char subtype[256], xtype[256];
-  int szf, sze, cur_sze;
-  int type;
+  char subtype[256] = { 0 };
+  char xtype[256] = { 0 };
+  int sze, cur_sze = 0;
   bool found_mimetypes = false;
+  enum ContentType type = TYPE_OTHER;
 
-  *subtype = '\0';
-  *xtype = '\0';
-  type = TYPE_OTHER;
-  cur_sze = 0;
-
-  szf = mutt_str_strlen(path);
+  int szf = mutt_str_strlen(path);
 
   for (int count = 0; count < 4; count++)
   {
@@ -1136,8 +1185,7 @@ int mutt_lookup_mime_type(struct Body *att, const char *path)
         {
           sze = mutt_str_strlen(p);
           if ((sze > cur_sze) && (szf >= sze) &&
-              ((mutt_str_strcasecmp(path + szf - sze, p) == 0) ||
-               (mutt_str_strcasecmp(path + szf - sze, p) == 0)) &&
+              (mutt_str_strcasecmp(path + szf - sze, p) == 0) &&
               ((szf == sze) || (path[szf - sze - 1] == '.')))
           {
             /* get the content-type */
@@ -1153,7 +1201,7 @@ int mutt_lookup_mime_type(struct Body *att, const char *path)
             for (q = p; *q && !IS_SPACE(*q); q++)
               ;
 
-            mutt_str_substr_cpy(subtype, p, q, sizeof(subtype));
+            mutt_str_substr_copy(p, q, subtype, sizeof(subtype));
 
             type = mutt_check_mime_type(ct);
             if (type == TYPE_OTHER)
@@ -1201,9 +1249,7 @@ static void transform_to_7bit(struct Body *a, FILE *fp_in)
   {
     if (a->type == TYPE_MULTIPART)
     {
-      if (a->encoding != ENC_7BIT)
-        a->encoding = ENC_7BIT;
-
+      a->encoding = ENC_7BIT;
       transform_to_7bit(a->parts, fp_in);
     }
     else if (mutt_is_message_type(a->type, a->subtype))
@@ -1293,7 +1339,7 @@ void mutt_message_to_7bit(struct Body *a, FILE *fp)
   transform_to_7bit(a->parts, fp_in);
 
   mutt_copy_hdr(fp_in, fp_out, a->offset, a->offset + a->length,
-                CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
+                CH_MIME | CH_NONEWLINE | CH_XMIT, NULL, 0);
 
   fputs("MIME-Version: 1.0\n", fp_out);
   mutt_write_mime_header(a->parts, fp_out);
@@ -1390,7 +1436,7 @@ static void set_encoding(struct Body *b, struct Content *info)
  */
 void mutt_stamp_attachment(struct Body *a)
 {
-  a->stamp = time(NULL);
+  a->stamp = mutt_date_epoch();
 }
 
 /**
@@ -1526,12 +1572,12 @@ struct Body *mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool a
     }
   }
 
-  mutt_copy_message_ctx(fp, m, e, cmflags, chflags);
+  mutt_copy_message(fp, m, e, cmflags, chflags, 0);
 
   fflush(fp);
   rewind(fp);
 
-  body->email = mutt_email_new();
+  body->email = email_new();
   body->email->offset = 0;
   /* we don't need the user headers here */
   body->email->env = mutt_rfc822_read_header(fp, body->email, false, false);
@@ -1553,21 +1599,23 @@ struct Body *mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool a
  */
 static void run_mime_type_query(struct Body *att)
 {
-  FILE *fp, *fp_err;
-  char cmd[STR_COMMAND];
+  FILE *fp = NULL, *fp_err = NULL;
   char *buf = NULL;
   size_t buflen;
   int dummy = 0;
   pid_t pid;
+  struct Buffer *cmd = mutt_buffer_pool_get();
 
-  mutt_file_expand_fmt_quote(cmd, sizeof(cmd), C_MimeTypeQueryCommand, att->filename);
+  mutt_buffer_file_expand_fmt_quote(cmd, C_MimeTypeQueryCommand, att->filename);
 
-  pid = mutt_create_filter(cmd, NULL, &fp, &fp_err);
+  pid = mutt_create_filter(mutt_b2s(cmd), NULL, &fp, &fp_err);
   if (pid < 0)
   {
-    mutt_error(_("Error running \"%s\""), cmd);
+    mutt_error(_("Error running \"%s\""), mutt_b2s(cmd));
+    mutt_buffer_pool_release(&cmd);
     return;
   }
+  mutt_buffer_pool_release(&cmd);
 
   buf = mutt_file_read_line(buf, &buflen, fp, &dummy, 0);
   if (buf)
@@ -1593,7 +1641,7 @@ struct Body *mutt_make_file_attach(const char *path)
   struct Body *att = mutt_body_new();
   att->filename = mutt_str_strdup(path);
 
-  if (C_MimeTypeQueryCommand && *C_MimeTypeQueryCommand && C_MimeTypeQueryFirst)
+  if (C_MimeTypeQueryCommand && C_MimeTypeQueryFirst)
     run_mime_type_query(att);
 
   /* Attempt to determine the appropriate content-type based on the filename
@@ -1601,7 +1649,7 @@ struct Body *mutt_make_file_attach(const char *path)
   if (!att->subtype)
     mutt_lookup_mime_type(att, path);
 
-  if (!att->subtype && C_MimeTypeQueryCommand && *C_MimeTypeQueryCommand && !C_MimeTypeQueryFirst)
+  if (!att->subtype && C_MimeTypeQueryCommand && !C_MimeTypeQueryFirst)
   {
     run_mime_type_query(att);
   }
@@ -1648,7 +1696,7 @@ static int get_toplevel_encoding(struct Body *a)
   {
     if (a->encoding == ENC_BINARY)
       return ENC_BINARY;
-    else if (a->encoding == ENC_8BIT)
+    if (a->encoding == ENC_8BIT)
       e = ENC_8BIT;
   }
 
@@ -1686,21 +1734,21 @@ static bool check_boundary(const char *boundary, struct Body *b)
  */
 struct Body *mutt_make_multipart(struct Body *b)
 {
-  struct Body *new = mutt_body_new();
-  new->type = TYPE_MULTIPART;
-  new->subtype = mutt_str_strdup("mixed");
-  new->encoding = get_toplevel_encoding(b);
+  struct Body *new_body = mutt_body_new();
+  new_body->type = TYPE_MULTIPART;
+  new_body->subtype = mutt_str_strdup("mixed");
+  new_body->encoding = get_toplevel_encoding(b);
   do
   {
-    mutt_generate_boundary(&new->parameter);
-    if (check_boundary(mutt_param_get(&new->parameter, "boundary"), b))
-      mutt_param_delete(&new->parameter, "boundary");
-  } while (!mutt_param_get(&new->parameter, "boundary"));
-  new->use_disp = false;
-  new->disposition = DISP_INLINE;
-  new->parts = b;
+    mutt_generate_boundary(&new_body->parameter);
+    if (check_boundary(mutt_param_get(&new_body->parameter, "boundary"), b))
+      mutt_param_delete(&new_body->parameter, "boundary");
+  } while (!mutt_param_get(&new_body->parameter, "boundary"));
+  new_body->use_disp = false;
+  new_body->disposition = DISP_INLINE;
+  new_body->parts = b;
 
-  return new;
+  return new_body;
 }
 
 /**
@@ -1725,8 +1773,8 @@ struct Body *mutt_remove_multipart(struct Body *b)
 }
 
 /**
- * mutt_write_address_list - wrapper around mutt_write_address()
- * @param addr    Address list
+ * mutt_write_addrlist - wrapper around mutt_write_address()
+ * @param al      Address list
  * @param fp      File to write to
  * @param linelen Line length to use
  * @param display True if these addresses will be displayed to the user
@@ -1734,18 +1782,16 @@ struct Body *mutt_remove_multipart(struct Body *b)
  * So we can handle very large recipient lists without needing a huge temporary
  * buffer in memory
  */
-void mutt_write_address_list(struct Address *addr, FILE *fp, int linelen, bool display)
+void mutt_write_addrlist(struct AddressList *al, FILE *fp, int linelen, bool display)
 {
-  struct Address *tmp = NULL;
   char buf[1024];
   int count = 0;
 
-  while (addr)
+  struct Address *a = NULL;
+  TAILQ_FOREACH(a, al, entries)
   {
-    tmp = addr->next;
-    addr->next = NULL;
     buf[0] = '\0';
-    mutt_addr_write(buf, sizeof(buf), addr, display);
+    mutt_addr_write(buf, sizeof(buf), a, display);
     size_t len = mutt_str_strlen(buf);
     if (count && (linelen + len > 74))
     {
@@ -1754,7 +1800,7 @@ void mutt_write_address_list(struct Address *addr, FILE *fp, int linelen, bool d
     }
     else
     {
-      if (count && addr->mailbox)
+      if (count && a->mailbox)
       {
         fputc(' ', fp);
         linelen++;
@@ -1762,13 +1808,12 @@ void mutt_write_address_list(struct Address *addr, FILE *fp, int linelen, bool d
       linelen += len;
     }
     fputs(buf, fp);
-    addr->next = tmp;
-    if (!addr->group && addr->next && addr->next->mailbox)
+    struct Address *next = TAILQ_NEXT(a, entries);
+    if (!a->group && next && next->mailbox)
     {
       linelen++;
       fputc(',', fp);
     }
-    addr = addr->next;
     count++;
   }
   fputc('\n', fp);
@@ -1829,7 +1874,7 @@ void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
 static int print_val(FILE *fp, const char *pfx, const char *value,
                      CopyHeaderFlags chflags, size_t col)
 {
-  while (value && *value)
+  while (value && (value[0] != '\0'))
   {
     if (fputc(*value, fp) == EOF)
       return -1;
@@ -1843,13 +1888,13 @@ static int print_val(FILE *fp, const char *pfx, const char *value,
     }
     if (*value == '\n')
     {
-      if (*(value + 1) && pfx && *pfx && (fputs(pfx, fp) == EOF))
+      if ((value[1] != '\0') && pfx && (pfx[0] != '\0') && (fputs(pfx, fp) == EOF))
         return -1;
       /* for display, turn folding spaces into folding tabs */
-      if ((chflags & CH_DISPLAY) && ((*(value + 1) == ' ') || (*(value + 1) == '\t')))
+      if ((chflags & CH_DISPLAY) && ((value[1] == ' ') || (value[1] == '\t')))
       {
         value++;
-        while (*value && ((*value == ' ') || (*value == '\t')))
+        while ((value[0] != '\0') && ((value[0] == ' ') || (value[0] == '\t')))
           value++;
         if (fputc('\t', fp) == EOF)
           return -1;
@@ -1876,18 +1921,18 @@ static int fold_one_header(FILE *fp, const char *tag, const char *value,
                            const char *pfx, int wraplen, CopyHeaderFlags chflags)
 {
   const char *p = value;
-  char buf[8192] = "";
+  char buf[8192] = { 0 };
   int first = 1, col = 0, l = 0;
   const bool display = (chflags & CH_DISPLAY);
 
-  mutt_debug(5, "pfx=[%s], tag=[%s], flags=%d value=[%s]\n", pfx, tag, chflags,
-             NONULL(value));
+  mutt_debug(LL_DEBUG5, "pfx=[%s], tag=[%s], flags=%d value=[%s]\n", pfx, tag,
+             chflags, NONULL(value));
 
   if (tag && *tag && (fprintf(fp, "%s%s: ", NONULL(pfx), tag) < 0))
     return -1;
-  col = mutt_str_strlen(tag) + ((tag && *tag) ? 2 : 0) + mutt_str_strlen(pfx);
+  col = mutt_str_strlen(tag) + ((tag && (tag[0] != '\0')) ? 2 : 0) + mutt_str_strlen(pfx);
 
-  while (p && *p)
+  while (p && (p[0] != '\0'))
   {
     int fold = 0;
 
@@ -1903,12 +1948,12 @@ static int fold_one_header(FILE *fp, const char *tag, const char *value,
     const int w = mutt_mb_width(buf, col, display);
     const int enc = mutt_str_startswith(buf, "=?", CASE_MATCH);
 
-    mutt_debug(5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n", buf, col, w, *next);
+    mutt_debug(LL_DEBUG5, "word=[%s], col=%d, w=%d, next=[0x0%x]\n", buf, col, w, *next);
 
     /* insert a folding \n before the current word's lwsp except for
      * header name, first word on a line (word longer than wrap width)
      * and encoded words */
-    if (!first && !enc && col && (col + w >= wraplen))
+    if (!first && !enc && col && ((col + w) >= wraplen))
     {
       col = mutt_str_strlen(pfx);
       fold = 1;
@@ -1921,7 +1966,7 @@ static int fold_one_header(FILE *fp, const char *tag, const char *value,
     if (display && fold)
     {
       char *pc = buf;
-      while (*pc && ((*pc == ' ') || (*pc == '\t')))
+      while ((pc[0] != '\0') && ((pc[0] == ' ') || (pc[0] == '\t')))
       {
         pc++;
         col--;
@@ -1943,9 +1988,9 @@ static int fold_one_header(FILE *fp, const char *tag, const char *value,
      * XXX this covers ASCII space only, for display we probably
      * want something like iswspace() here */
     const char *sp = next;
-    while (*sp && ((*sp == ' ') || (*sp == '\t')))
+    while ((sp[0] != '\0') && ((sp[0] == ' ') || (sp[0] == '\t')))
       sp++;
-    if (*sp == '\n')
+    if (sp[0] == '\n')
     {
       next = sp;
       col = 0;
@@ -1973,20 +2018,20 @@ static int fold_one_header(FILE *fp, const char *tag, const char *value,
  */
 static char *unfold_header(char *s)
 {
-  char *p = s, *q = s;
+  char *p = s;
+  char *q = s;
 
-  while (p && *p)
+  while (p && (p[0] != '\0'))
   {
     /* remove CRLF prior to FWSP, turn \t into ' ' */
-    if ((*p == '\r') && *(p + 1) && (*(p + 1) == '\n') && *(p + 2) &&
-        ((*(p + 2) == ' ') || (*(p + 2) == '\t')))
+    if ((p[0] == '\r') && (p[1] == '\n') && ((p[2] == ' ') || (p[2] == '\t')))
     {
       *q++ = ' ';
       p += 3;
       continue;
     }
     /* remove LF prior to FWSP, turn \t into ' ' */
-    else if ((*p == '\n') && *(p + 1) && ((*(p + 1) == ' ') || (*(p + 1) == '\t')))
+    else if ((p[0] == '\n') && ((p[1] == ' ') || (p[1] == '\t')))
     {
       *q++ = ' ';
       p += 2;
@@ -1995,7 +2040,7 @@ static char *unfold_header(char *s)
     *q++ = *p++;
   }
   if (q)
-    *q = '\0';
+    q[0] = '\0';
 
   return s;
 }
@@ -2024,8 +2069,8 @@ static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char
   if (!(chflags & CH_DISPLAY) && ((pfxw + max <= wraplen) || is_from))
   {
     valbuf = mutt_str_substr_dup(start, end);
-    mutt_debug(5, "buf[%s%s] short enough, max width = %d <= %d\n", NONULL(pfx),
-               valbuf, max, wraplen);
+    mutt_debug(LL_DEBUG5, "buf[%s%s] short enough, max width = %d <= %d\n",
+               NONULL(pfx), valbuf, max, wraplen);
     if (pfx && *pfx)
     {
       if (fputs(pfx, fp) == EOF)
@@ -2107,7 +2152,7 @@ static int write_one_header(FILE *fp, int pfxw, int max, int wraplen, const char
 int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
                           const char *pfx, int wraplen, CopyHeaderFlags chflags)
 {
-  char *p = (char *) value, *last = NULL, *line = NULL;
+  char *last = NULL, *line = NULL;
   int max = 0, w, rc = -1;
   int pfxw = mutt_strwidth(pfx);
   char *v = mutt_str_strdup(value);
@@ -2124,15 +2169,15 @@ int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
     else
       wraplen = C_WrapHeaders;
   }
-  else if ((wraplen <= 0) || (wraplen > MuttIndexWindow->cols))
-    wraplen = MuttIndexWindow->cols;
+  else if (wraplen <= 0)
+    wraplen = 78;
 
   if (tag)
   {
     /* if header is short enough, simply print it */
     if (!display && (mutt_strwidth(tag) + 2 + pfxw + mutt_strwidth(v) <= wraplen))
     {
-      mutt_debug(5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
+      mutt_debug(LL_DEBUG5, "buf[%s%s: %s] is short enough\n", NONULL(pfx), tag, v);
       if (fprintf(fp, "%s%s: %s\n", NONULL(pfx), tag, v) <= 0)
         goto out;
       rc = 0;
@@ -2145,7 +2190,7 @@ int mutt_write_one_header(FILE *fp, const char *tag, const char *value,
     }
   }
 
-  p = v;
+  char *p = v;
   last = v;
   line = v;
   while (p && *p)
@@ -2185,6 +2230,71 @@ out:
   return rc;
 }
 
+/**
+ * userhdrs_override_cmp - Compare a user-defined header with an element of the userhdrs_override_headers list
+ * @param a Pointer to the string containing the user-defined header
+ * @param b Pointer to an element of the userhdrs_override_headers list
+ * @retval -1 a precedes b
+ * @retval  0 a and b are identical
+ * @retval  1 b precedes a
+ */
+static int userhdrs_override_cmp(const void *a, const void *b)
+{
+  const char *ca = a;
+  const char *cb = *(const char **) b;
+  return mutt_str_strncasecmp(ca, cb, strlen(cb));
+}
+
+/**
+ * write_userhdrs - Write user-defined headers and keep track of the interesting ones
+ * @param fp       FILE pointer where to write the headers
+ * @param userhdrs List of headers to write
+ * @param privacy  Omit headers that could identify the user
+ * @retval obj UserHdrsOverride struct containing a bitmask of which unique headers were written
+ */
+static struct UserHdrsOverride write_userhdrs(FILE *fp, const struct ListHead *userhdrs, bool privacy)
+{
+  struct UserHdrsOverride overrides = { { 0 } };
+
+  struct ListNode *tmp = NULL;
+  STAILQ_FOREACH(tmp, userhdrs, entries)
+  {
+    char *const colon = strchr(tmp->data, ':');
+    if (!colon)
+    {
+      continue;
+    }
+
+    const char *const value = mutt_str_skip_email_wsp(colon + 1);
+    if (*value == '\0')
+    {
+      continue; /* don't emit empty fields. */
+    }
+
+    /* check whether the current user-header is an override */
+    size_t curr_override = (size_t) -1;
+    const char *const *idx = bsearch(tmp->data, userhdrs_override_headers,
+                                     mutt_array_size(userhdrs_override_headers),
+                                     sizeof(char *), userhdrs_override_cmp);
+    if (idx != NULL)
+    {
+      curr_override = idx - userhdrs_override_headers;
+      overrides.is_overridden[curr_override] = true;
+    }
+
+    if (privacy && (curr_override == USERHDRS_OVERRIDE_USER_AGENT))
+    {
+      continue;
+    }
+
+    *colon = '\0';
+    mutt_write_one_header(fp, tmp->data, value, NULL, 0, CH_NO_FLAGS);
+    *colon = ':';
+  }
+
+  return overrides;
+}
+
 /**
  * mutt_rfc822_write_header - Write out one RFC822 header line
  * @param fp      File to write to
@@ -2214,32 +2324,30 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
                              bool privacy, bool hide_protected_subject)
 {
   char buf[1024];
-  char *p = NULL, *q = NULL;
-  bool has_agent = false; /* user defined user-agent header field exists */
 
   if ((mode == MUTT_WRITE_HEADER_NORMAL) && !privacy)
     fputs(mutt_date_make_date(buf, sizeof(buf)), fp);
 
   /* UseFrom is not consulted here so that we can still write a From:
    * field if the user sets it with the 'my_hdr' command */
-  if (env->from && !privacy)
+  if (!TAILQ_EMPTY(&env->from) && !privacy)
   {
     buf[0] = '\0';
-    mutt_addr_write(buf, sizeof(buf), env->from, false);
+    mutt_addrlist_write(buf, sizeof(buf), &env->from, false);
     fprintf(fp, "From: %s\n", buf);
   }
 
-  if (env->sender && !privacy)
+  if (!TAILQ_EMPTY(&env->sender) && !privacy)
   {
     buf[0] = '\0';
-    mutt_addr_write(buf, sizeof(buf), env->sender, false);
+    mutt_addrlist_write(buf, sizeof(buf), &env->sender, false);
     fprintf(fp, "Sender: %s\n", buf);
   }
 
-  if (env->to)
+  if (!TAILQ_EMPTY(&env->to))
   {
     fputs("To: ", fp);
-    mutt_write_address_list(env->to, fp, 4, 0);
+    mutt_write_addrlist(&env->to, fp, 4, 0);
   }
   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
 #ifdef USE_NNTP
@@ -2247,10 +2355,10 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
 #endif
       fputs("To:\n", fp);
 
-  if (env->cc)
+  if (!TAILQ_EMPTY(&env->cc))
   {
     fputs("Cc: ", fp);
-    mutt_write_address_list(env->cc, fp, 4, 0);
+    mutt_write_addrlist(&env->cc, fp, 4, 0);
   }
   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
 #ifdef USE_NNTP
@@ -2258,13 +2366,13 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
 #endif
       fputs("Cc:\n", fp);
 
-  if (env->bcc)
+  if (!TAILQ_EMPTY(&env->bcc))
   {
     if ((mode == MUTT_WRITE_HEADER_POSTPONE) || (mode == MUTT_WRITE_HEADER_EDITHDRS) ||
         ((mode == MUTT_WRITE_HEADER_NORMAL) && C_WriteBcc))
     {
       fputs("Bcc: ", fp);
-      mutt_write_address_list(env->bcc, fp, 5, 0);
+      mutt_write_addrlist(&env->bcc, fp, 5, 0);
     }
   }
   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
@@ -2305,22 +2413,27 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
   if (env->message_id && !privacy)
     fprintf(fp, "Message-ID: %s\n", env->message_id);
 
-  if (env->reply_to)
+  if (!TAILQ_EMPTY(&env->reply_to))
   {
     fputs("Reply-To: ", fp);
-    mutt_write_address_list(env->reply_to, fp, 10, 0);
+    mutt_write_addrlist(&env->reply_to, fp, 10, 0);
   }
   else if (mode == MUTT_WRITE_HEADER_EDITHDRS)
     fputs("Reply-To:\n", fp);
 
-  if (env->mail_followup_to)
+  if (!TAILQ_EMPTY(&env->mail_followup_to))
+  {
 #ifdef USE_NNTP
     if (!OptNewsSend)
 #endif
     {
       fputs("Mail-Followup-To: ", fp);
-      mutt_write_address_list(env->mail_followup_to, fp, 18, 0);
+      mutt_write_addrlist(&env->mail_followup_to, fp, 18, 0);
     }
+  }
+
+  /* Add any user defined headers */
+  struct UserHdrsOverride userhdrs_overrides = write_userhdrs(fp, &env->userhdrs, privacy);
 
   if ((mode == MUTT_WRITE_HEADER_NORMAL) || (mode == MUTT_WRITE_HEADER_POSTPONE))
   {
@@ -2332,8 +2445,11 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
     }
 
     /* Add the MIME headers */
-    fputs("MIME-Version: 1.0\n", fp);
-    mutt_write_mime_header(attach, fp);
+    if (!userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_CONTENT_TYPE])
+    {
+      fputs("MIME-Version: 1.0\n", fp);
+      mutt_write_mime_header(attach, fp);
+    }
   }
 
   if (!STAILQ_EMPTY(&env->in_reply_to))
@@ -2343,41 +2459,18 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
     fputc('\n', fp);
   }
 
-  /* Add any user defined headers */
-  struct ListNode *tmp = NULL;
-  STAILQ_FOREACH(tmp, &env->userhdrs, entries)
+#ifdef USE_AUTOCRYPT
+  if (C_Autocrypt)
   {
-    p = strchr(tmp->data, ':');
-    if (p)
-    {
-      q = p;
-
-      *p = '\0';
-
-      p = mutt_str_skip_email_wsp(p + 1);
-      if (!*p)
-      {
-        *q = ':';
-        continue; /* don't emit empty fields. */
-      }
-
-      /* check to see if the user has overridden the user-agent field */
-      if (mutt_str_startswith(tmp->data, "user-agent", CASE_IGNORE))
-      {
-        has_agent = true;
-        if (privacy)
-        {
-          *q = ':';
-          continue;
-        }
-      }
-
-      mutt_write_one_header(fp, tmp->data, p, NULL, 0, CH_NO_FLAGS);
-      *q = ':';
-    }
+    if (mode == MUTT_WRITE_HEADER_NORMAL)
+      mutt_autocrypt_write_autocrypt_header(env, fp);
+    if (mode == MUTT_WRITE_HEADER_MIME)
+      mutt_autocrypt_write_gossip_headers(env, fp);
   }
+#endif
 
-  if ((mode == MUTT_WRITE_HEADER_NORMAL) && !privacy && C_UserAgent && !has_agent)
+  if ((mode == MUTT_WRITE_HEADER_NORMAL) && !privacy && C_UserAgent &&
+      !userhdrs_overrides.is_overridden[USERHDRS_OVERRIDE_USER_AGENT])
   {
     /* Add a vanity header */
     fprintf(fp, "User-Agent: NeoMutt/%s%s\n", PACKAGE_VERSION, GitVer);
@@ -2652,24 +2745,42 @@ static int send_msg(const char *path, char **args, const char *msg, char **tempf
 }
 
 /**
- * add_args - Add an Address to a dynamic array
+ * add_args_one - Add an Address to a dynamic array
  * @param[out] args    Array to add to
  * @param[out] argslen Number of entries in array
  * @param[out] argsmax Allocated size of the array
  * @param[in]  addr    Address to add
  * @retval ptr Updated array
  */
-static char **add_args(char **args, size_t *argslen, size_t *argsmax, struct Address *addr)
+static char **add_args_one(char **args, size_t *argslen, size_t *argsmax, struct Address *addr)
 {
-  for (; addr; addr = addr->next)
+  /* weed out group mailboxes, since those are for display only */
+  if (addr->mailbox && !addr->group)
   {
-    /* weed out group mailboxes, since those are for display only */
-    if (addr->mailbox && !addr->group)
-    {
-      if (*argslen == *argsmax)
-        mutt_mem_realloc(&args, (*argsmax += 5) * sizeof(char *));
-      args[(*argslen)++] = addr->mailbox;
-    }
+    if (*argslen == *argsmax)
+      mutt_mem_realloc(&args, (*argsmax += 5) * sizeof(char *));
+    args[(*argslen)++] = addr->mailbox;
+  }
+  return args;
+}
+
+/**
+ * add_args - Add a list of Addresses to a dynamic array
+ * @param[out] args    Array to add to
+ * @param[out] argslen Number of entries in array
+ * @param[out] argsmax Allocated size of the array
+ * @param[in]  al      Addresses to add
+ * @retval ptr Updated array
+ */
+static char **add_args(char **args, size_t *argslen, size_t *argsmax, struct AddressList *al)
+{
+  if (!al)
+    return args;
+
+  struct Address *a = NULL;
+  TAILQ_FOREACH(a, al, entries)
+  {
+    args = add_args_one(args, argslen, argsmax, a);
   }
   return args;
 }
@@ -2703,8 +2814,9 @@ static char **add_option(char **args, size_t *argslen, size_t *argsmax, char *s)
  * @retval  0 Success
  * @retval -1 Failure
  */
-int mutt_invoke_sendmail(struct Address *from, struct Address *to, struct Address *cc,
-                         struct Address *bcc, const char *msg, int eightbit)
+int mutt_invoke_sendmail(struct AddressList *from, struct AddressList *to,
+                         struct AddressList *cc, struct AddressList *bcc,
+                         const char *msg, int eightbit)
 {
   char *ps = NULL, *path = NULL, *s = NULL, *childout = NULL;
   char **args = NULL;
@@ -2717,8 +2829,8 @@ int mutt_invoke_sendmail(struct Address *from, struct Address *to, struct Addres
   {
     char cmd[1024];
 
-    mutt_expando_format(cmd, sizeof(cmd), 0, MuttIndexWindow->cols,
-                        NONULL(C_Inews), nntp_format_str, 0, MUTT_FORMAT_NO_FLAGS);
+    mutt_expando_format(cmd, sizeof(cmd), 0, sizeof(cmd), NONULL(C_Inews),
+                        nntp_format_str, 0, MUTT_FORMAT_NO_FLAGS);
     if (!*cmd)
     {
       i = nntp_post(Context->mailbox, msg);
@@ -2795,9 +2907,9 @@ int mutt_invoke_sendmail(struct Address *from, struct Address *to, struct Addres
       if (C_EnvelopeFromAddress)
       {
         args = add_option(args, &argslen, &argsmax, "-f");
-        args = add_args(args, &argslen, &argsmax, C_EnvelopeFromAddress);
+        args = add_args_one(args, &argslen, &argsmax, C_EnvelopeFromAddress);
       }
-      else if (from && !from->next)
+      else if (!TAILQ_EMPTY(from) && !TAILQ_NEXT(TAILQ_FIRST(from), entries))
       {
         args = add_option(args, &argslen, &argsmax, "-f");
         args = add_args(args, &argslen, &argsmax, from);
@@ -2884,20 +2996,21 @@ void mutt_prepare_envelope(struct Envelope *env, bool final)
 {
   if (final)
   {
-    if (env->bcc && !(env->to || env->cc))
+    if (!TAILQ_EMPTY(&env->bcc) && TAILQ_EMPTY(&env->to) && TAILQ_EMPTY(&env->cc))
     {
       /* some MTA's will put an Apparently-To: header field showing the Bcc:
        * recipients if there is no To: or Cc: field, so attempt to suppress
        * it by using an empty To: field.  */
-      env->to = mutt_addr_new();
-      env->to->group = 1;
-      env->to->next = mutt_addr_new();
+      struct Address *to = mutt_addr_new();
+      to->group = true;
+      mutt_addrlist_append(&env->to, to);
+      mutt_addrlist_append(&env->to, mutt_addr_new());
 
       char buf[1024];
       buf[0] = '\0';
       mutt_addr_cat(buf, sizeof(buf), "undisclosed-recipients", AddressSpecials);
 
-      env->to->mailbox = mutt_str_strdup(buf);
+      to->mailbox = mutt_str_strdup(buf);
     }
 
     mutt_set_followup_to(env);
@@ -2926,7 +3039,7 @@ void mutt_unprepare_envelope(struct Envelope *env)
     rfc2047_decode(&item->data);
   }
 
-  mutt_addr_free(&env->mail_followup_to);
+  mutt_addrlist_clear(&env->mail_followup_to);
 
   /* back conversions */
   rfc2047_decode_envelope(env);
@@ -2942,8 +3055,8 @@ void mutt_unprepare_envelope(struct Envelope *env)
  * @retval  0 Success
  * @retval -1 Failure
  */
-static int bounce_message(FILE *fp, struct Email *e, struct Address *to,
-                          const char *resent_from, struct Address *env_from)
+static int bounce_message(FILE *fp, struct Email *e, struct AddressList *to,
+                          const char *resent_from, struct AddressList *env_from)
 {
   if (!e)
     return -1;
@@ -2968,8 +3081,8 @@ static int bounce_message(FILE *fp, struct Email *e, struct Address *to,
     fprintf(fp_tmp, "Resent-Message-ID: %s\n", msgid_str);
     FREE(&msgid_str);
     fputs("Resent-To: ", fp_tmp);
-    mutt_write_address_list(to, fp_tmp, 11, 0);
-    mutt_copy_header(fp, e, fp_tmp, chflags, NULL);
+    mutt_write_addrlist(to, fp_tmp, 11, 0);
+    mutt_copy_header(fp, e, fp_tmp, chflags, NULL, 0);
     fputc('\n', fp_tmp);
     mutt_file_copy_bytes(fp, fp_tmp, e->content->length);
     if (mutt_file_fclose(&fp_tmp) != 0)
@@ -2994,13 +3107,13 @@ static int bounce_message(FILE *fp, struct Email *e, struct Address *to,
  * mutt_bounce_message - Bounce an email message
  * @param fp Handle of message
  * @param e  Email
- * @param to Address to bounce to
+ * @param to AddressList to bounce to
  * @retval  0 Success
  * @retval -1 Failure
  */
-int mutt_bounce_message(FILE *fp, struct Email *e, struct Address *to)
+int mutt_bounce_message(FILE *fp, struct Email *e, struct AddressList *to)
 {
-  if (!fp || !e || !to)
+  if (!fp || !e || !to || TAILQ_EMPTY(to))
     return -1;
 
   const char *fqdn = mutt_fqdn(true);
@@ -3009,6 +3122,8 @@ int mutt_bounce_message(FILE *fp, struct Email *e, struct Address *to)
 
   resent_from[0] = '\0';
   struct Address *from = mutt_default_from();
+  struct AddressList from_list = TAILQ_HEAD_INITIALIZER(from_list);
+  mutt_addrlist_append(&from_list, from);
 
   /* mutt_default_from() does not use $realname if the real name is not set
    * in $from, so we add it here.  The reason it is not added in
@@ -3018,18 +3133,17 @@ int mutt_bounce_message(FILE *fp, struct Email *e, struct Address *to)
   if (!from->personal)
     from->personal = mutt_str_strdup(C_Realname);
 
-  if (fqdn)
-    mutt_addr_qualify(from, fqdn);
+  mutt_addrlist_qualify(&from_list, fqdn);
 
-  rfc2047_encode_addrlist(from, "Resent-From");
-  if (mutt_addrlist_to_intl(from, &err))
+  rfc2047_encode_addrlist(&from_list, "Resent-From");
+  if (mutt_addrlist_to_intl(&from_list, &err))
   {
     mutt_error(_("Bad IDN %s while preparing resent-from"), err);
     FREE(&err);
-    mutt_addr_free(&from);
+    mutt_addrlist_clear(&from_list);
     return -1;
   }
-  mutt_addr_write(resent_from, sizeof(resent_from), from, false);
+  mutt_addrlist_write(resent_from, sizeof(resent_from), &from_list, false);
 
 #ifdef USE_NNTP
   OptNewsSend = false;
@@ -3038,13 +3152,12 @@ int mutt_bounce_message(FILE *fp, struct Email *e, struct Address *to)
   /* prepare recipient list. idna conversion appears to happen before this
    * function is called, since the user receives confirmation of the address
    * list being bounced to.  */
-  struct Address *resent_to = mutt_addr_copy_list(to, false);
-  rfc2047_encode_addrlist(resent_to, "Resent-To");
-
-  int rc = bounce_message(fp, e, resent_to, resent_from, from);
-
-  mutt_addr_free(&resent_to);
-  mutt_addr_free(&from);
+  struct AddressList resent_to = TAILQ_HEAD_INITIALIZER(resent_to);
+  mutt_addrlist_copy(&resent_to, to, false);
+  rfc2047_encode_addrlist(&resent_to, "Resent-To");
+  int rc = bounce_message(fp, e, &resent_to, resent_from, &from_list);
+  mutt_addrlist_clear(&resent_to);
+  mutt_addrlist_clear(&from_list);
 
   return rc;
 }
@@ -3129,7 +3242,7 @@ int mutt_write_multiple_fcc(const char *path, struct Email *e, const char *msgid
  * @retval -1 Failure
  */
 int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
-                   bool post, char *fcc, char **finalpath)
+                   bool post, const char *fcc, char **finalpath)
 {
   struct Message *msg = NULL;
   char tempfile[PATH_MAX];
@@ -3223,11 +3336,17 @@ int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
     if (e->security & SEC_SIGN)
     {
       fputc('S', msg->fp);
-      if (C_PgpSignAs && *C_PgpSignAs)
+      if (C_PgpSignAs)
         fprintf(msg->fp, "<%s>", C_PgpSignAs);
     }
     if (e->security & SEC_INLINE)
       fputc('I', msg->fp);
+#ifdef USE_AUTOCRYPT
+    if (e->security & SEC_AUTOCRYPT)
+      fputc('A', msg->fp);
+    if (e->security & SEC_AUTOCRYPT_OVERRIDE)
+      fputc('Z', msg->fp);
+#endif
     fputc('\n', msg->fp);
   }
 
@@ -3238,7 +3357,7 @@ int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
     if (e->security & SEC_ENCRYPT)
     {
       fputc('E', msg->fp);
-      if (C_SmimeEncryptWith && *C_SmimeEncryptWith)
+      if (C_SmimeEncryptWith)
         fprintf(msg->fp, "C<%s>", C_SmimeEncryptWith);
     }
     if (e->security & SEC_OPPENCRYPT)
@@ -3246,7 +3365,7 @@ int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
     if (e->security & SEC_SIGN)
     {
       fputc('S', msg->fp);
-      if (C_SmimeSignAs && *C_SmimeSignAs)
+      if (C_SmimeSignAs)
         fprintf(msg->fp, "<%s>", C_SmimeSignAs);
     }
     if (e->security & SEC_INLINE)