]> granicus.if.org Git - neomutt/blobdiff - sendlib.c
merge: light refactoring
[neomutt] / sendlib.c
index 74c305017b9c3dd48ccb60836a30ce4cfc95c366..5f2bf3df45dc3caa8919e78af9a1a470abf602a7 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -31,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"
@@ -73,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
@@ -93,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
@@ -227,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
@@ -376,9 +415,9 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
       if (!np->attribute || !np->value)
         continue;
 
-      struct ParameterList param_conts = rfc2231_encode_string(np->attribute, np->value);
+      struct ParameterList pl_conts = rfc2231_encode_string(np->attribute, np->value);
       struct Parameter *cont = NULL;
-      TAILQ_FOREACH(cont, &param_conts, entries)
+      TAILQ_FOREACH(cont, &pl_conts, entries)
       {
         fputc(';', fp);
 
@@ -408,7 +447,7 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
         fprintf(fp, "%s=%s", cont->attribute, buf);
       }
 
-      mutt_param_free(&param_conts);
+      mutt_param_free(&pl_conts);
     }
   }
 
@@ -444,9 +483,9 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
           else
             t = fn;
 
-          struct ParameterList param_conts = rfc2231_encode_string("filename", t);
+          struct ParameterList pl_conts = rfc2231_encode_string("filename", t);
           struct Parameter *cont = NULL;
-          TAILQ_FOREACH(cont, &param_conts, entries)
+          TAILQ_FOREACH(cont, &pl_conts, entries)
           {
             fputc(';', fp);
             buf[0] = 0;
@@ -467,7 +506,7 @@ int mutt_write_mime_header(struct Body *a, FILE *fp)
             fprintf(fp, "%s=%s", cont->attribute, buf);
           }
 
-          mutt_param_free(&param_conts);
+          mutt_param_free(&pl_conts);
         }
       }
 
@@ -482,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;
@@ -540,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;
@@ -588,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
@@ -644,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;
@@ -661,6 +691,8 @@ static void update_content_info(struct Content *info, struct ContentState *s,
         linelen = 0;
         continue;
       }
+
+      info->binary = true;
     }
 
     linelen++;
@@ -1153,8 +1185,7 @@ enum ContentType 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 */
@@ -1218,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))
@@ -1310,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);
@@ -1407,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();
 }
 
 /**
@@ -1543,12 +1572,12 @@ struct Body *mutt_make_message_attach(struct Mailbox *m, struct Email *e, bool a
     }
   }
 
-  mutt_copy_message(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);
@@ -1667,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;
   }
 
@@ -2140,8 +2169,8 @@ 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)
   {
@@ -2201,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
@@ -2230,8 +2324,6 @@ 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);
@@ -2330,6 +2422,7 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
     fputs("Reply-To:\n", fp);
 
   if (!TAILQ_EMPTY(&env->mail_followup_to))
+  {
 #ifdef USE_NNTP
     if (!OptNewsSend)
 #endif
@@ -2337,6 +2430,10 @@ int mutt_rfc822_write_header(FILE *fp, struct Envelope *env,
       fputs("Mail-Followup-To: ", fp);
       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))
   {
@@ -2348,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))
@@ -2359,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);
@@ -2752,8 +2829,8 @@ int mutt_invoke_sendmail(struct AddressList *from, struct AddressList *to,
   {
     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);
@@ -3005,7 +3082,7 @@ static int bounce_message(FILE *fp, struct Email *e, struct AddressList *to,
     FREE(&msgid_str);
     fputs("Resent-To: ", fp_tmp);
     mutt_write_addrlist(to, fp_tmp, 11, 0);
-    mutt_copy_header(fp, e, fp_tmp, chflags, NULL);
+    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)
@@ -3165,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];
@@ -3264,6 +3341,12 @@ int mutt_write_fcc(const char *path, struct Email *e, const char *msgid,
     }
     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);
   }