Hi Kevin, > Date: 2026-08-03 12:43:26+0800 > From: "Kevin J. McCarthy" <[email protected]> > > Filter out characters, even stricter than RFC5322 requires, to allow > the message id to be used in a URL. > > If, for whatever reason, the from address is not available, fall back > to "@%f". > > Provide an example value in the manual: > set message_id_format="%z_%F" > noting that %F includes a '@' and so one must not also be inside the > variable. > --- > > This is a simple take on adding %F to use the from address. I didn't > try to optimize for speed, but suggestions are welcome. I added heavy > filtering to make sure it didn't violate the spec and also didn't > break adding to a URL without encoding. > > > init.h | 5 +++++ > messageid.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- > protos.h | 2 +- > sendlib.c | 4 ++-- > 4 files changed, 52 insertions(+), 4 deletions(-) > > diff --git a/init.h b/init.h > index 087ebfa8..d9fc0378 100644 > --- a/init.h > +++ b/init.h > @@ -2223,11 +2223,16 @@ struct option_t MuttVars[] = { > ** The old Message-ID format can be used by setting this to: > ** ``\fC<%Y%02m%02d%02H%02M%02S.G%c%p@%f>\fP'' > ** .pp > + ** An alternative to using %f is %F. This expando includes a '@', > + ** so when used there must not be a second '@' in the variable: > + ** ``\fC<%z_%F>\fP'' > + ** .pp > ** The following \fCprintf(3)\fP-style sequences are understood: > ** .dl > ** .dt %c .dd step counter looping from ``A'' to ``Z'' > ** .dt %d .dd current day of the month (GMT) > ** .dt %f .dd $$hostname > + ** .dt %F .dd from address of the message, including an '@' > ** .dt %H .dd current hour using a 24-hour clock (GMT) > ** .dt %m .dd current month number (GMT) > ** .dt %M .dd current minute of the hour (GMT) > diff --git a/messageid.c b/messageid.c > index 397ff31c..a18c7068 100644 > --- a/messageid.c > +++ b/messageid.c > @@ -22,6 +22,7 @@ > > #include "mutt.h" > #include "mutt_random.h" > +#include "mutt_idna.h" > > static char MsgIdPfx = 'A'; > > @@ -30,8 +31,34 @@ typedef struct msg_id_data > time_t now; > struct tm tm; > const char *fqdn; > + ENVELOPE *env; > } MSG_ID_DATA; > > +static const char *ALLOWED_FROM_CHARS = "abcdefghijklmnopqrstuvwxyz" > + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" > + "1234567890-_.";
I suggest a more generic name for this, which documents the fact that
this is the POSIX Portable Filename Character Set. Also, I suggest
using intermediate character sets that have a well-known name, to make
it easier to distinguish each character set.
#define CTYPE_LOWER_C "abcdefghijklmnopqrstuvwxyz"
#define CTYPE_UPPER_C "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
#define CTYPE_DIGIT_C "0123456789"
#define CTYPE_ALPHA_C CTYPE_LOWER_C CTYPE_UPPER_C
#define CTYPE_ALNUM_C CTYPE_ALPHA_C CTYPE_DIGIT_C
#define CTYPE_PFCHAR_C CTYPE_ALNUM_C "._-" // portable
filename character set
> +
> +/* This function is much stricter than RFC5322 specifies, because we also
> want
> + * the message-id to be passed in a URL as a path segment or parameter
> without
> + * needing encoding.
> + */
> +static void filter_from_addr(char *from)
> +{
> + int has_at = 0;
> +
> + if (!from)
> + return;
> +
> + while (*from)
> + {
> + if (*from == '@' && !has_at)
> + has_at = 1;
> + else if (!strchr(ALLOWED_FROM_CHARS, *from))
And then here I suggest adding another API, inspired by isascii(3):
// isascii_c - is [:ascii:] C-locale
#define ispfchar_c(c) (!streq(strchrnul(CTYPE_PFCHAR_C, c), ""))
#define streq(s1, s2) (!strcmp(s1, s2))
To be able to write it as
else if (!ispfchar_c(*from))
> + *from = '_';
> + from++;
> + }
> +}
In any case, the above seems okay.
Have a lovely day!
Alex
> +
> static const char *id_format_str(char *dest, size_t destlen, size_t col,
> int cols, char op, const char *src,
> const char *fmt, const char *ifstring,
> @@ -44,6 +71,7 @@ static const char *id_format_str(char *dest, size_t
> destlen, size_t col,
> unsigned char r_out[4 + 1];
> unsigned char z_raw[12]; /* 32 bit timestamp, plus 64 bit randomness */
> unsigned char z_out[16 + 1];
> + ADDRESS *from;
>
> switch (op)
> {
> @@ -107,12 +135,26 @@ static const char *id_format_str(char *dest, size_t
> destlen, size_t col,
> case 'f':
> mutt_format_s(dest, destlen, fmt, id_data->fqdn);
> break;
> +
> + case 'F':
> + from = rfc822_cpy_adr(id_data->env->from, 1);
> + if (!from || !from->mailbox)
> + {
> + snprintf(tmp, sizeof(tmp), "@%s", id_data->fqdn);
> + mutt_format_s(dest, destlen, fmt, tmp);
> + break;
> + }
> + mutt_addrlist_to_intl(from, NULL);
> + filter_from_addr(from->mailbox);
> + mutt_format_s(dest, destlen, fmt, from->mailbox);
> + rfc822_free_address(&from);
> + break;
> }
>
> return (src);
> }
>
> -char *mutt_gen_msgid(void)
> +char *mutt_gen_msgid(ENVELOPE *env)
> {
> MSG_ID_DATA id_data;
> BUFFER *buf, *tmp;
> @@ -123,6 +165,7 @@ char *mutt_gen_msgid(void)
> memcpy(&id_data.tm, gmtime(&id_data.now), sizeof(id_data.tm));
> if (!(id_data.fqdn = mutt_fqdn(0)))
> id_data.fqdn = NONULL(Hostname);
> + id_data.env = env;
>
> fmt = MessageIdFormat;
> if (!fmt)
> diff --git a/protos.h b/protos.h
> index 7ea95ae5..e03ca0eb 100644
> --- a/protos.h
> +++ b/protos.h
> @@ -156,7 +156,7 @@ void mutt_buffer_expand_multi_path_norel(BUFFER *src,
> const char *delimiter);
> void mutt_buffer_remove_path_password(BUFFER *dest, const char *src);
> char *mutt_find_hook(int, const char *);
> char *mutt_gecos_name(char *, size_t, struct passwd *);
> -char *mutt_gen_msgid(void);
> +char *mutt_gen_msgid(ENVELOPE *);
> char *mutt_get_body_charset(char *, size_t, BODY *);
> const char *mutt_get_name(ADDRESS *);
> char *mutt_get_parameter(const char *, PARAMETER *);
> diff --git a/sendlib.c b/sendlib.c
> index 150bc72d..612b0503 100644
> --- a/sendlib.c
> +++ b/sendlib.c
> @@ -2912,7 +2912,7 @@ void mutt_prepare_envelope(ENVELOPE *env, int final)
> mutt_set_followup_to(env);
>
> if (!env->message_id)
> - env->message_id = mutt_gen_msgid();
> + env->message_id = mutt_gen_msgid(env);
> }
>
> /* Take care of 8-bit => 7-bit conversion. */
> @@ -2975,7 +2975,7 @@ static int _mutt_bounce_message(FILE *fp, HEADER *h,
> ADDRESS *to, const char *re
> fprintf(f, "Resent-Date: %s\n", mutt_b2s(date));
> mutt_buffer_pool_release(&date);
>
> - msgid_str = mutt_gen_msgid();
> + msgid_str = mutt_gen_msgid(h->env);
> fprintf(f, "Resent-Message-ID: %s\n", msgid_str);
> fputs("Resent-To: ", f);
> mutt_write_address_list(to, f, 11, 0);
> --
> 2.55.0
>
--
<https://www.alejandro-colomar.es>
signature.asc
Description: PGP signature
