Re: [PATCH] Add %F expando to $message_id_format to use from address.
Alejandro Colomar via Mutt-dev <[email protected]> Mon, 3 Aug 2026 13:12:46 +0200
| Newsgroups | gmane.mail.mutt.devel |
|---|---|
| Message-ID | <anB0GFYGkcA1CMT4@devuan> |
--z5lozktmoy5cygj6 Content-Type: text/plain; protected-headers=v1; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable From: Alejandro Colomar <[email protected]> To: [email protected] Subject: Re: [PATCH] Add %F expando to $message_id_format to use from address. Message-ID: <anB0GFYGkcA1CMT4@devuan> References: <[email protected]> MIME-Version: 1.0 In-Reply-To: <[email protected]> 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. >=20 > If, for whatever reason, the from address is not available, fall back > to "@%f". >=20 > Provide an example value in the manual: > set message_id_format=3D"%z_%F" > noting that %F includes a '@' and so one must not also be inside the > variable. > --- >=20 > 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. >=20 >=20 > init.h | 5 +++++ > messageid.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- > protos.h | 2 +- > sendlib.c | 4 ++-- > 4 files changed, 52 insertions(+), 4 deletions(-) >=20 > 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[] =3D { > ** 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 @@ > =20 > #include "mutt.h" > #include "mutt_random.h" > +#include "mutt_idna.h" > =20 > static char MsgIdPfx =3D 'A'; > =20 > @@ -30,8 +31,34 @@ typedef struct msg_id_data > time_t now; > struct tm tm; > const char *fqdn; > + ENVELOPE *env; > } MSG_ID_DATA; > =20 > +static const char *ALLOWED_FROM_CHARS =3D "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 c= haracter set > + > +/* This function is much stricter than RFC5322 specifies, because we als= o want > + * the message-id to be passed in a URL as a path segment or parameter w= ithout > + * needing encoding. > + */ > +static void filter_from_addr(char *from) > +{ > + int has_at =3D 0; > + > + if (!from) > + return; > + > + while (*from) > + { > + if (*from =3D=3D '@' && !has_at) > + has_at =3D 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 =3D '_'; > + 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 des= tlen, 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; > =20 > 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 =3D 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; > } > =20 > return (src); > } > =20 > -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 =3D mutt_fqdn(0))) > id_data.fqdn =3D NONULL(Hostname); > + id_data.env =3D env; > =20 > fmt =3D 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); > =20 > if (!env->message_id) > - env->message_id =3D mutt_gen_msgid(); > + env->message_id =3D mutt_gen_msgid(env); > } > =20 > /* Take care of 8-bit =3D> 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); > =20 > - msgid_str =3D mutt_gen_msgid(); > + msgid_str =3D 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); > --=20 > 2.55.0 >=20 --=20 <https://www.alejandro-colomar.es> --z5lozktmoy5cygj6 Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEES7Jt9u9GbmlWADAi64mZXMKQwqkFAmpwd6cACgkQ64mZXMKQ wqkn0A/+O7z/sF9yQXvRNwCb20OMLqlmmlXcr7XumUh3AQu+bkyGbHJvo7/P5Rsq AkhOkcXP9m+Ezs+SlOrfvdGp0rMytpVXe75WD1pjzFqutBgoUdsYll/AwpokAbm8 bNW0vj3EaxC5q/ywq85egEvmHjapeZqAi5ed628upWA+BAiPN9uLQDYiIkQhx4kO 5KumIC6TVHqmgdJMZDuwv87RfS5cPaGIrKi0+H7HbVFcUxwl3UzsHDBwodNicS8y 58rWr/XdJT7OEaMR3ZwdbTvFmqurXfbLqi4Cwv8joh4gWvJZnDmL3QAiM2/0GeWI TwUH/RnLDNCNM4IU5/FTCBjLotYzclqZYBOYEojBDDjASNeEqAIAHMTGcZNXcCzN CCGLwIHiHflc/0NKdeep5q7FuwIKFAW3Z0thhS2S3WI+Cl/dIUncARyvcmHGFy34 1E8yYWJkiqzmbyrVuXUmeNBex4nlaLsBQ5cCsE5VjuDKTmd9VFUCJ9zb/CDRV3HU K6sDqEf6ZPhjSxGRpdzqNa3HMOq6IW8E93tjOlHqISiRWk1AkHPJvpW/Uk5r8P4r UHjWKHhDloCRscDk3hZZHIejWZxl7IGXMBnY7ND4iW9jXSiojT6H0mM9RrYDSGCU V8ZrE5VxhQMeRkvewS3ETlH3HAh1e+G7qb5a3tD46r6n5Aa3fU0= =G30O -----END PGP SIGNATURE----- --z5lozktmoy5cygj6--