#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"
#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
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
}
}
-/**
- * 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
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, ¶m_conts, entries)
+ TAILQ_FOREACH(cont, &pl_conts, entries)
{
fputc(';', fp);
fprintf(fp, "%s=%s", cont->attribute, buf);
}
- mutt_param_free(¶m_conts);
+ mutt_param_free(&pl_conts);
}
}
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, ¶m_conts, entries)
+ TAILQ_FOREACH(cont, &pl_conts, entries)
{
fputc(';', fp);
buf[0] = 0;
fprintf(fp, "%s=%s", cont->attribute, buf);
}
- mutt_param_free(¶m_conts);
+ mutt_param_free(&pl_conts);
}
}
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;
/* 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;
/**
* 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
if (was_cr)
{
was_cr = false;
- if (ch != '\n')
- {
- info->binary = true;
- }
- else
+ if (ch == '\n')
{
if (whitespace)
info->space = true;
linelen = 0;
continue;
}
+
+ info->binary = true;
}
linelen++;
{
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 */
{
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))
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);
*/
void mutt_stamp_attachment(struct Body *a)
{
- a->stamp = time(NULL);
+ a->stamp = mutt_date_epoch();
}
/**
}
}
- 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);
{
if (a->encoding == ENC_BINARY)
return ENC_BINARY;
- else if (a->encoding == ENC_8BIT)
+ if (a->encoding == ENC_8BIT)
e = ENC_8BIT;
}
else
wraplen = C_WrapHeaders;
}
- else if ((wraplen <= 0) || (wraplen > MuttIndexWindow->cols))
- wraplen = MuttIndexWindow->cols;
+ else if (wraplen <= 0)
+ wraplen = 78;
if (tag)
{
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
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);
fputs("Reply-To:\n", fp);
if (!TAILQ_EMPTY(&env->mail_followup_to))
+ {
#ifdef USE_NNTP
if (!OptNewsSend)
#endif
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))
{
}
/* 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))
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);
{
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);
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)
* @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];
}
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);
}