Add set_msgid_header with default setting 'auto'

pull/84/head
Martin Lambers 2 months ago
parent 2583739d04
commit c70cf49b9a
  1. 2
      NEWS
  2. 8
      doc/msmtp.1
  3. 12
      doc/msmtp.texi
  4. 1
      src/Makefile.am
  5. 26
      src/conf.c
  6. 14
      src/conf.h
  7. 73
      src/msgid.c
  8. 33
      src/msgid.h
  9. 193
      src/msmtp.c

@ -2,6 +2,8 @@ Version 1.8.21:
- Added a new configuration command 'eval' to replace the current configuration
file line with the output of a command (similar to passwordeval, but more
general).
- Added a new configuration command 'set_msgid_header' with the default setting
'auto'. This adds a Message-ID header to the mail if none is present.
Version 1.8.20:
- Added a new configuration command 'allow_from_override'. When off, the --from

@ -159,6 +159,8 @@ compatible with sendmail.
Set From header handling. See the \fBset_from_header\fP command.
.IP "\-\-set\-date\-header[=(\fIauto\fP|\fIoff\fP)]"
Set Date header handling. See the \fBset_date_header\fP command.
.IP "\-\-set\-msgid\-header[=(\fIauto\fP|\fIoff\fP)]"
Set Message-ID header handling. See the \fBset_msgid_header\fP command.
.IP "\-\-remove\-bcc\-headers[=(\fIon\fP|\fIoff\fP)]"
Enable or disable the removal of Bcc headers. See the \fBremove_bcc_headers\fP
command.
@ -242,6 +244,9 @@ with the \fB\-F\fP option.
- A Date header will be added if the mail does not have one. This can be changed
with the \fBset_date_header\fP command and \fB\-\-set\-date\-header\fP option.
.br
- A Message-ID header will be added if the mail does not have one. This can be changed
with the \fBset_msg_header\fP command and \fB\-\-set\-msgid\-header\fP option.
.br
- When \fBundisclosed_recipients\fP is set, the original To, Cc, and Bcc headers
are removed and replaced with "To: undisclosed-recipients:;".
.PP
@ -614,6 +619,9 @@ have one (this is the default), and \fIoff\fP never sets a Date header.
.br
For compatibility with older versions, \fBadd_missing_date_header\fP [(\fIon\fP|\fIoff\fP)]
is still supported and corresponds to the \fIauto\fP and \fIoff\fP settings.
.IP "set_msgid_header [(\fIauto\fP|\fIoff\fP)]"
When to set a Message-ID header: \fIauto\fP adds a Message-ID header if the mail does not
have one (this is the default), and \fIoff\fP never sets a Message-ID header.
.IP "remove_bcc_headers [(\fIon\fP|\fIoff\fP)]"
This command controls whether to remove Bcc headers. The default is to remove them.
.IP "undisclosed_recipients [(\fIon\fP|\fIoff\fP)]"

@ -442,6 +442,12 @@ have one (this is the default), and @samp{off} never sets a Date header.@*
For compatibility with older versions, @samp{add_missing_date_header [(on|off)]}
is still supported and corresponds to the @samp{auto} and @samp{off} settings.@*
@xref{Header handling}.
@anchor{set_msgid_header}
@item set_msgid_header [(auto|off)]
@cmindex set_msgid_header
When to set a Message-ID header: @samp{auto} adds a Message-ID header if the mail does not
have one (this is the default), and @samp{off} never sets a Message-ID header.@*
@xref{Header handling}.
@anchor{remove_bcc_headers}
@item remove_bcc_headers [(on|off)]
@cmindex remove_bcc_headers
@ -726,6 +732,10 @@ Set From header handling. @xref{set_from_header}.
@item --set-date-header[=(auto|off)]
@opindex --set-date-header
Set Date header handling. @xref{set_date_header}.
@anchor{--set-msgid-header}
@item --set-msgid-header[=(auto|off)]
@opindex --set-msgid-header
Set Message-ID header handling. @xref{set_msgid_header}.
@anchor{--remove-bcc-headers}
@item --remove-bcc-headers[=(on|off)]
@opindex --remove-bcc-headers
@ -1124,6 +1134,8 @@ The header will use the envelope from address and optionally a full name set
with the @samp{-F} option.
@item A Date header will be added if the mail does not have one. This can be changed
with the @ref{set_date_header} command.
@item A Message-ID header will be added if the mail does not have one. This can be changed
with the @ref{set_msgid_header} command.
@item When @ref{undisclosed_recipients} is set, the original To, Cc, and Bcc headers
are removed and replaced with "To: undisclosed-recipients:;".
@end itemize

@ -5,6 +5,7 @@ msmtp_SOURCES = \
eval.c eval.h \
list.c list.h \
msmtp.c \
msgid.c msgid.h \
net.c net.h \
netrc.c netrc.h \
readbuf.c readbuf.h \

@ -103,6 +103,7 @@ account_t *account_new(const char *conffile, const char *id)
a->proxy_port = 0;
a->set_from_header = 2;
a->set_date_header = 2;
a->set_msgid_header = 2;
a->remove_bcc_headers = 1;
a->undisclosed_recipients = 0;
a->source_ip = NULL;
@ -194,6 +195,7 @@ account_t *account_copy(account_t *acc)
a->proxy_port = acc->proxy_port;
a->set_from_header = acc->set_from_header;
a->set_date_header = acc->set_date_header;
a->set_msgid_header = acc->set_msgid_header;
a->remove_bcc_headers = acc->remove_bcc_headers;
a->undisclosed_recipients = acc->undisclosed_recipients;
a->source_ip = acc->source_ip ? xstrdup(acc->source_ip) : NULL;
@ -782,6 +784,10 @@ void override_account(account_t *acc1, account_t *acc2)
{
acc1->set_date_header = acc2->set_date_header;
}
if (acc2->mask & ACC_SET_MSGID_HEADER)
{
acc1->set_msgid_header = acc2->set_msgid_header;
}
if (acc2->mask & ACC_SOURCE_IP)
{
free(acc1->source_ip);
@ -1910,6 +1916,26 @@ int read_conffile(const char *conffile, FILE *f, list_t **acc_list,
break;
}
}
else if (strcmp(cmd, "set_msgid_header") == 0)
{
acc->mask |= ACC_SET_MSGID_HEADER;
if (*arg == '\0' || is_auto(arg))
{
acc->set_msgid_header = 2;
}
else if (is_off(arg))
{
acc->set_msgid_header = 0;
}
else
{
*errstr = xasprintf(
_("line %d: invalid argument %s for command %s"),
line, arg, cmd);
e = CONF_ESYNTAX;
break;
}
}
else if (strcmp(cmd, "remove_bcc_headers") == 0)
{
acc->mask |= ACC_REMOVE_BCC_HEADERS;

@ -4,7 +4,7 @@
* This file is part of msmtp, an SMTP client.
*
* Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007, 2008, 2010, 2011, 2014,
* 2016, 2018, 2019, 2020, 2021
* 2016, 2018, 2019, 2020, 2021, 2022
* Martin Lambers <marlam@marlam.de>
* Martin Stenberg <martin@gnutiken.se> (passwordeval support)
* Scott Shumate <sshumate@austin.rr.com> (aliases support)
@ -80,11 +80,12 @@
#define ACC_PROXY_PORT (1LL << 30LL)
#define ACC_SET_FROM_HEADER (1LL << 31LL)
#define ACC_SET_DATE_HEADER (1LL << 32LL)
#define ACC_REMOVE_BCC_HEADERS (1LL << 33LL)
#define ACC_UNDISCLOSED_RECIPIENTS (1LL << 34LL)
#define ACC_SOURCE_IP (1LL << 35LL)
#define ACC_LOGFILE_TIME_FORMAT (1LL << 36LL)
#define ACC_SOCKET (1LL << 37LL)
#define ACC_SET_MSGID_HEADER (1LL << 33LL)
#define ACC_REMOVE_BCC_HEADERS (1LL << 34LL)
#define ACC_UNDISCLOSED_RECIPIENTS (1LL << 35LL)
#define ACC_SOURCE_IP (1LL << 36LL)
#define ACC_LOGFILE_TIME_FORMAT (1LL << 37LL)
#define ACC_SOCKET (1LL << 38LL)
typedef struct
{
@ -140,6 +141,7 @@ typedef struct
/* header modifications */
int set_from_header; /* 0=off, 1=on, 2=auto */
int set_date_header; /* 0=off, 2=auto */
int set_msgid_header; /* 0=off, 2=auto */
int remove_bcc_headers; /* remove the Bcc headers from the mail? */
int undisclosed_recipients; /* remove To, Cc, Bcc, add anonymous To */
/* source ip binding */

@ -0,0 +1,73 @@
/*
* msgid.c
*
* This file is part of msmtp, an SMTP client.
*
* Copyright (C) 2022 Martin Lambers <marlam@marlam.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include "msgid.h"
#include "net.h"
#include "xalloc.h"
#include "md5-apps.h"
#ifndef CLOCK_BOOTTIME
# define CLOCK_BOOTTIME CLOCK_MONOTONIC
#endif
char* create_msgid(const char* envelope_from)
{
struct timespec ts_real;
struct timespec ts_boot;
pid_t pid;
char* hostname;
size_t hostname_len;
unsigned char* data;
size_t data_size;
char digest[33];
char *msgid;
/* The following information should unqiuely identify this mail:
* the system is identified via hostname and boot time, and
* the mail on this system via real time and pid. */
clock_gettime(CLOCK_REALTIME, &ts_real);
clock_gettime(CLOCK_MONOTONIC, &ts_boot);
pid = getpid();
hostname = net_get_canonical_hostname(NULL);
hostname_len = strlen(hostname);
/* Compute a hash over this data so that it cannot be recovered. */
data_size = sizeof(ts_real) + sizeof(ts_boot) + sizeof(pid) + hostname_len;
data = xmalloc(data_size);
memcpy(data, &ts_real, sizeof(ts_real));
memcpy(data + sizeof(ts_real), &ts_boot, sizeof(ts_boot));
memcpy(data + sizeof(ts_real) + sizeof(ts_boot), &pid, sizeof(pid));
memcpy(data + sizeof(ts_real) + sizeof(ts_boot) + sizeof(pid), hostname, hostname_len);
md5_digest(data, data_size, digest);
free(data);
if (strchr(envelope_from, '@'))
msgid = xasprintf("<%s.%s>", digest, envelope_from);
else
msgid = xasprintf("<%s.%s@%s>", digest, envelope_from, hostname);
free(hostname);
return msgid;
}

@ -0,0 +1,33 @@
/*
* msgid.h
*
* This file is part of msmtp, an SMTP client.
*
* Copyright (C) 2022 Martin Lambers <marlam@marlam.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MSGID_H
#define MSGID_H
/* Create a message id suitable for a Message-ID header and return it in an
* allocated buffer.
*
* The id will contain the hash over some unique identifiying information
* prepended to the envelope from address local@domain: <hash.local@domain>
*/
char* create_msgid(const char* envelope_from);
#endif

@ -61,6 +61,7 @@ extern int optind;
#include "aliases.h"
#include "password.h"
#include "eval.h"
#include "msgid.h"
#ifdef HAVE_TLS
# include "mtls.h"
#endif /* HAVE_TLS */
@ -626,8 +627,11 @@ error_exit:
* If 'from' is not NULL: extracts the address from the From header and stores
* it in an allocated string. A pointer to this string is stored in 'from'.
*
* If 'have_date' is not NULL: set this flag to 1 if a Date header is present
* , and to 0 otherwise.
* If 'have_date' is not NULL: set this flag to 1 if a Date header is present,
* and to 0 otherwise.
*
* If 'have_msgid' is not NULL: set this flag to 1 if a Message-ID header is
* present, and to 0 otherwise.
*
* See RFC2822, section 3 for the format of these headers.
*
@ -646,32 +650,43 @@ error_exit:
#define STATE_DATE2 4 /* we saw "^Da" */
#define STATE_DATE3 5 /* we saw "^Dat" */
#define STATE_DATE4 6 /* we saw "^Date" */
#define STATE_FROM1 7 /* we saw "^F" */
#define STATE_FROM2 8 /* we saw "^Fr" */
#define STATE_FROM3 9 /* we saw "^Fro" */
#define STATE_TO 10 /* we saw "^T" */
#define STATE_CC 11 /* we saw "^C" */
#define STATE_BCC1 12 /* we saw "^B" */
#define STATE_BCC2 13 /* we saw "^Bc" */
#define STATE_ADDRHDR_ALMOST 14 /* we saw "^To", "^Cc"
#define STATE_MSGID1 7 /* we saw "^M" */
#define STATE_MSGID2 8 /* we saw "^Me" */
#define STATE_MSGID3 9 /* we saw "^Mes" */
#define STATE_MSGID4 10 /* we saw "^Mess" */
#define STATE_MSGID5 11 /* we saw "^Messa" */
#define STATE_MSGID6 12 /* we saw "^Messag" */
#define STATE_MSGID7 13 /* we saw "^Message" */
#define STATE_MSGID8 14 /* we saw "^Message-" */
#define STATE_MSGID9 15 /* we saw "^Message-I" */
#define STATE_MSGID10 16 /* we saw "^Message-ID" */
#define STATE_FROM1 17 /* we saw "^F" */
#define STATE_FROM2 18 /* we saw "^Fr" */
#define STATE_FROM3 19 /* we saw "^Fro" */
#define STATE_TO 20 /* we saw "^T" */
#define STATE_CC 21 /* we saw "^C" */
#define STATE_BCC1 22 /* we saw "^B" */
#define STATE_BCC2 23 /* we saw "^Bc" */
#define STATE_ADDRHDR_ALMOST 24 /* we saw "^To", "^Cc"
or "^Bcc" */
#define STATE_RESENT 15 /* we saw part of "^Resent-" */
#define STATE_ADDRHDR_DEFAULT 16 /* in_rcpt_hdr and in_rcpt
#define STATE_RESENT 25 /* we saw part of "^Resent-" */
#define STATE_ADDRHDR_DEFAULT 26 /* in_rcpt_hdr and in_rcpt
state our position */
#define STATE_ADDRHDR_DQUOTE 17 /* duoble quotes */
#define STATE_ADDRHDR_BRACKETS_START 18 /* entering <...> */
#define STATE_ADDRHDR_IN_BRACKETS 19 /* an address inside <> */
#define STATE_ADDRHDR_PARENTH_START 20 /* entering (...) */
#define STATE_ADDRHDR_IN_PARENTH 21 /* a comment inside () */
#define STATE_ADDRHDR_IN_ADDRESS 22 /* a bare address */
#define STATE_ADDRHDR_BACKQUOTE 23 /* we saw a '\\' */
#define STATE_HEADERS_END 24 /* we saw "^$", the blank line
#define STATE_ADDRHDR_DQUOTE 27 /* duoble quotes */
#define STATE_ADDRHDR_BRACKETS_START 28 /* entering <...> */
#define STATE_ADDRHDR_IN_BRACKETS 29 /* an address inside <> */
#define STATE_ADDRHDR_PARENTH_START 30 /* entering (...) */
#define STATE_ADDRHDR_IN_PARENTH 31 /* a comment inside () */
#define STATE_ADDRHDR_IN_ADDRESS 32 /* a bare address */
#define STATE_ADDRHDR_BACKQUOTE 33 /* we saw a '\\' */
#define STATE_HEADERS_END 34 /* we saw "^$", the blank line
between headers and body */
int msmtp_read_headers(FILE *mailf, FILE *tmpf,
list_t *recipients,
char **from,
int *have_date,
int *have_msgid,
char **errstr)
{
int c;
@ -714,6 +729,10 @@ int msmtp_read_headers(FILE *mailf, FILE *tmpf,
{
*have_date = 0;
}
if (have_msgid)
{
*have_msgid = 0;
}
if (recipients)
{
normal_recipients_list = list_new();
@ -747,6 +766,8 @@ int msmtp_read_headers(FILE *mailf, FILE *tmpf,
resent_index = -1;
if (have_date && (c == 'd' || c == 'D'))
state = STATE_DATE1;
else if (have_msgid && (c == 'm' || c == 'M'))
state = STATE_MSGID1;
else if (from && from_hdr < 0 && (c == 'f' || c == 'F'))
state = STATE_FROM1;
else if (recipients && (c == 't' || c == 'T'))
@ -780,6 +801,8 @@ int msmtp_read_headers(FILE *mailf, FILE *tmpf,
state = folded_rcpthdr_savestate;
else if (have_date && (c == 'd' || c == 'D'))
state = STATE_DATE1;
else if (have_msgid && (c == 'm' || c == 'M'))
state = STATE_MSGID1;
else if (from && from_hdr < 0 && (c == 'f' || c == 'F'))
state = STATE_FROM1;
else if (recipients && (c == 't' || c == 'T'))
@ -875,6 +898,99 @@ int msmtp_read_headers(FILE *mailf, FILE *tmpf,
state = STATE_OTHER_HDR;
break;
case STATE_MSGID1:
if (c == 'e' || c == 'E')
state = STATE_MSGID2;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID2:
if (c == 's' || c == 'S')
state = STATE_MSGID3;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID3:
if (c == 's' || c == 'S')
state = STATE_MSGID4;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID4:
if (c == 'a' || c == 'A')
state = STATE_MSGID5;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID5:
if (c == 'g' || c == 'G')
state = STATE_MSGID6;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID6:
if (c == 'e' || c == 'E')
state = STATE_MSGID7;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID7:
if (c == '-')
state = STATE_MSGID8;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID8:
if (c == 'i' || c == 'I')
state = STATE_MSGID9;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID9:
if (c == 'd' || c == 'D')
state = STATE_MSGID10;
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_MSGID10:
if (c == ':')
{
*have_msgid = 1;
state = STATE_OTHER_HDR;
}
else if (c == '\n')
state = STATE_LINESTART_FRESH;
else
state = STATE_OTHER_HDR;
break;
case STATE_FROM1:
if (resent_block == 0 && resent_index != 6)
resent_block = 1;
@ -2347,6 +2463,7 @@ typedef struct
#define LONGONLYOPT_SOCKET (256 + 38)
#define LONGONLYOPT_SET_FROM_HEADER (256 + 39)
#define LONGONLYOPT_SET_DATE_HEADER (256 + 40)
#define LONGONLYOPT_SET_MSGID_HEADER (256 + 41)
int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[])
{
@ -2407,6 +2524,8 @@ int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[])
LONGONLYOPT_SET_FROM_HEADER },
{ "set-date-header", optional_argument, 0,
LONGONLYOPT_SET_DATE_HEADER },
{ "set-msgid-header", optional_argument, 0,
LONGONLYOPT_SET_MSGID_HEADER },
{ "remove-bcc-headers", optional_argument, 0,
LONGONLYOPT_REMOVE_BCC_HEADERS },
{ "undisclosed-recipients", optional_argument, 0,
@ -3084,6 +3203,24 @@ int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[])
conf->cmdline_account->mask |= ACC_SET_DATE_HEADER;
break;
case LONGONLYOPT_SET_MSGID_HEADER:
if (!optarg || is_auto(optarg))
{
conf->cmdline_account->set_msgid_header = 2;
}
else if (is_off(optarg))
{
conf->cmdline_account->set_msgid_header = 0;
}
else
{
print_error(_("invalid argument %s for %s"),
optarg, "--set-msgid-header");
error_code = 1;
}
conf->cmdline_account->mask |= ACC_SET_MSGID_HEADER;
break;
case LONGONLYOPT_ADD_MISSING_FROM_HEADER:
/* compatibility with < 1.8.8 */
if (!optarg || is_on(optarg))
@ -3319,7 +3456,8 @@ int msmtp_cmdline(msmtp_cmdline_conf_t *conf, int argc, char *argv[])
goto error_exit;
}
if ((error_code = msmtp_read_headers(tmpf, NULL,
list_last(conf->recipients), NULL, NULL, &errstr))
list_last(conf->recipients), NULL, NULL, NULL,
&errstr))
!= EX_OK)
{
print_error("%s", sanitize_string(errstr));
@ -3691,6 +3829,7 @@ int main(int argc, char *argv[])
FILE *prepend_header_tmpfile = NULL;
int have_from_header = 0;
int have_date_header = 0;
int have_msgid_header = 0;
/* Avoid the side effects of text mode interpretations on DOS systems. */
@ -3769,7 +3908,8 @@ int main(int argc, char *argv[])
if ((error_code = msmtp_read_headers(stdin, header_tmpfile,
conf.read_recipients
? list_last(conf.recipients) : NULL,
&envelope_from, &have_date_header, &errstr)) != EX_OK)
&envelope_from, &have_date_header, &have_msgid_header,
&errstr)) != EX_OK)
{
print_error("%s", sanitize_string(errstr));
goto exit;
@ -4055,7 +4195,8 @@ int main(int argc, char *argv[])
if (account->undisclosed_recipients
|| account->set_from_header == 1
|| (!have_from_header && account->set_from_header == 2)
|| (!have_date_header && account->set_date_header == 2))
|| (!have_date_header && account->set_date_header == 2)
|| (!have_msgid_header && account->set_msgid_header == 2))
{
if (!(prepend_header_tmpfile = tmpfile()))
{
@ -4089,6 +4230,12 @@ int main(int argc, char *argv[])
print_time_rfc2822(time(NULL), rfc2822_timestamp);
fprintf(prepend_header_tmpfile, "Date: %s\n", rfc2822_timestamp);
}
if (!have_msgid_header && account->set_msgid_header == 2)
{
char *msgid = create_msgid(account->from);
fprintf(prepend_header_tmpfile, "Message-ID: %s\n", msgid);
free(msgid);
}
if (prepend_header_tmpfile
&& fseeko(prepend_header_tmpfile, 0, SEEK_SET) != 0)
{

Loading…
Cancel
Save