Re: [PATCH] Support display name substitution in customheaders
Jona Müller <[email protected]> Sun, 29 Mar 2026 15:32:36 +0200
| Newsgroups | org.mlmmj.mlmmj |
|---|---|
| Message-ID | <ackp9MFJ2kfoNzao@radeon> |
--qdrw4o4n223Z4PZh Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable I would also +1 RFC2047 as we have many people with non-ascii names (Myself included). From experience, this can be quite a pain to support correctly as around the world people still use a lot of encodings other than UTF-8. The = most common in my experience are: ISO-8859-1 (Western europe latin1) ISO-2022-JP (non-UTF-8 Japanese) A quick hack would be to throw out any encoded words (=3D?UTF-8?...) and ju= st use the local part (address) in that case.=20 The options to do it correctly would be in my opinion:=20 1. Do it by hand and suffer 2. Introduce some large dependency that handles it I'm against 2, and I think having few dependencies is one of mlmmj's streng= ths. Tests would be very nice, I agree.=20 Cheers, -jm =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D Independend GmbH [email protected] +49 6832 265556 http://ind.me On Sun, Mar 29, 2026 at 02:33:28PM +0200, Baptiste Daroussin wrote: > after a quick read of the match, the extracted display name is not saniti= zed, it should probably be. >=20 > the parser at quick glance seems fragile, what happen if the display name= is "Doe, John" >=20 > Would be nice to get a bunch of tests to make sure everything works as ex= pected. >=20 > also I don t think rfc 2047 is handled. >=20 > Best regards, > bapt >=20 >=20 > Le 29 mars 2026 10:13:54 GMT+02:00, Baptiste Daroussin <[email protected]> a = =E9crit=A0: > >Thank you, I will have a look at it asap, and see integrate it if possib= le in 2.0.0 which I intend to release soon > > > >Best regards, > >Bapt > > > > > >March 27, 2026 at 7:14 PM, "Lumin Etherlight" <[email protected]= nk mailto:[email protected]?to=3D%22Lumin%20Etherlight%22%20%3Clu= min%2Bmlmmj%40etherlight.link%3E > wrote: > > > > > >>=20 > >> Allow substitution of the variable $postername$ for > >> the display name of the user. The display name is > >> extracted from the sender's From: header. If a name > >> is not found, then this variable is substituted for > >> user's address, i.e. the same value as $posteraddr$. > >> Proper handling of comments, and quoted strings, in > >> the From: address is also included. > >>=20 > >> This allows the following `customheaders' use-case: > >>=20 > >> From: $postername$ via Project-List <[email protected]> > >>=20 > >> Which would render, for example, as: > >>=20 > >> From: John Doe via Project-List <[email protected]> > >>=20 > >> For the user: > >>=20 > >> John Doe <[email protected]>. > >> --- > >> AUTHORS | 1 + > >> include/find_email_adr.h | 1 + > >> include/wrappers.h | 2 +- > >> src/do_all_the_voodoo_here.c | 22 ++++++++++++--- > >> src/dumpfd2fd.c | 14 +++++++++- > >> src/find_email_adr.c | 52 ++++++++++++++++++++++++++++++++++++ > >> 6 files changed, 86 insertions(+), 6 deletions(-) > >>=20 > >> diff --git a/AUTHORS b/AUTHORS > >> index be21ced..9d9b7df 100644 > >> --- a/AUTHORS > >> +++ b/AUTHORS > >> @@ -1,3 +1,4 @@ > >> Mads Martin Joergensen <[email protected]> > >> Morten K. Poulsen <[email protected]> > >> Ben Schmidt <[email protected]> > >> +Lumin Etherlight <[email protected]> > >> diff --git a/include/find_email_adr.h b/include/find_email_adr.h > >> index e5da89b..707fdd6 100644 > >> --- a/include/find_email_adr.h > >> +++ b/include/find_email_adr.h > >> @@ -27,5 +27,6 @@ > >> #include <stddef.h> > >> =20 > >> strlist *find_email_adr(const char *str, strlist *retval); > >> +char *find_display_name(char *name); > >> =20 > >> #endif /* FIND_EMAIL_ADR_H */ > >> diff --git a/include/wrappers.h b/include/wrappers.h > >> index 2953bd2..5a2c144 100644 > >> --- a/include/wrappers.h > >> +++ b/include/wrappers.h > >> @@ -31,6 +31,6 @@ ssize_t readn(int fd, void *vptr, size_t n); > >> int random_int(void); > >> int dumpfd2fd(int infd, int outfd); > >> int copy_file(int infd, int outfd, size_t bufsiz); > >> -int process_headers(int infd, FILE *outf, const char *from); > >> +int process_headers(int infd, FILE *outf, const char *from, const cha= r *name); > >> =20 > >> #endif /* WRAPPERS_H */ > >> diff --git a/src/do_all_the_voodoo_here.c b/src/do_all_the_voodoo_here= =2Ec > >> index 13aeb74..14214a8 100644 > >> --- a/src/do_all_the_voodoo_here.c > >> +++ b/src/do_all_the_voodoo_here.c > >> @@ -80,6 +80,7 @@ int do_all_the_voodoo_here(int infd, int outfd, int = hdrfd, int footfd, > >> char *hdrline, *unfolded, *unqp; > >> strlist allunfoldeds =3D tll_init(); > >> char *posteraddr =3D NULL; > >> + char *postername =3D NULL; > >> bool hdrsadded =3D false; > >> bool subject_present =3D false; > >> int dupfd =3D dup(infd); > >> @@ -111,9 +112,16 @@ int do_all_the_voodoo_here(int infd, int outfd, i= nt hdrfd, int footfd, > >> if(hdrfd >=3D 0) { > >> strlist fromemails =3D tll_init(); > >> if ( readhdrs[0].valuecount =3D=3D 1 ) { > >> - find_email_adr(readhdrs[0].values[0], &fromemails); > >> - if (tll_length(fromemails) > 0) > >> + find_email_adr(readhdrs[0].values[0], &fromemails); > >> + if (tll_length(fromemails) > 0) { > >> posteraddr =3D xstrdup(tll_front(fromemails)); > >> + postername =3D find_display_name(readhdrs[0].values[0]); > >> + if (!postername) { > >> + postername =3D posteraddr ? > >> + xstrdup(posteraddr) : > >> + xcalloc(1, 1); > >> + } > >> + } > >> } > >> tll_free_and_free(fromemails, free); > >> } > >> @@ -129,12 +137,13 @@ int do_all_the_voodoo_here(int infd, int outfd, = int hdrfd, int footfd, > >> if(!hdrsadded && > >> ( strncasecmp(hdrline, "mime", 4) =3D=3D 0)) { > >> if(hdrfd >=3D 0) { > >> - if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "") < 0) { > >> + if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "", posterna= me ) < 0) { > >> log_error(LOG_ARGS, "Could not " > >> "add extra headers"); > >> free(unfolded); > >> tll_free_and_free(allunfoldeds, free); > >> free(posteraddr); > >> + free(postername); > >> fclose(outf); > >> fclose(f); > >> return -1; > >> @@ -174,11 +183,12 @@ int do_all_the_voodoo_here(int infd, int outfd, = int hdrfd, int footfd, > >> =20 > >> if(!hdrsadded ) { > >> if(hdrfd >=3D 0) { > >> - if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "") < 0) { > >> + if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "", posterna= me) < 0) { > >> log_error(LOG_ARGS, "Could not " > >> "add extra headers"); > >> tll_free_and_free(allunfoldeds, free); > >> free(posteraddr); > >> + free(postername); > >> fclose(outf); > >> fclose(f); > >> return -1; > >> @@ -195,6 +205,7 @@ int do_all_the_voodoo_here(int infd, int outfd, in= t hdrfd, int footfd, > >> tll_free_and_free(allunfoldeds, free); > >> log_error(LOG_ARGS, "Error writing hdrs."); > >> free(posteraddr); > >> + free(postername); > >> fclose(outf); > >> fclose(f); > >> return -1; > >> @@ -209,6 +220,7 @@ int do_all_the_voodoo_here(int infd, int outfd, in= t hdrfd, int footfd, > >> if(dumpfd2fd(infd, outfd) < 0) { > >> log_error(LOG_ARGS, "Error when dumping rest of mail"); > >> free(posteraddr); > >> + free(postername); > >> fclose(f); > >> return -1; > >> } > >> @@ -219,11 +231,13 @@ int do_all_the_voodoo_here(int infd, int outfd, = int hdrfd, int footfd, > >> if(dumpfd2fd(footfd, outfd) < 0) { > >> log_error(LOG_ARGS, "Error when adding footer"); > >> free(posteraddr); > >> + free(postername); > >> return -1; > >> } > >> =20 > >> fsync(outfd); > >> =20 > >> free(posteraddr); > >> + free(postername); > >> return 0; > >> } > >> diff --git a/src/dumpfd2fd.c b/src/dumpfd2fd.c > >> index 674e2b0..431bac2 100644 > >> --- a/src/dumpfd2fd.c > >> +++ b/src/dumpfd2fd.c > >> @@ -84,7 +84,7 @@ int dumpfd2fd(int from, int to) > >> return (r); > >> } > >> =20 > >> -int process_headers(int infd, FILE *outf, const char *from) > >> +int process_headers(int infd, FILE *outf, const char *from, const cha= r *name) > >> { > >> char *line =3D NULL, *p, *to_be_subst, *new_line; > >> size_t linecap =3D 0; > >> @@ -120,6 +120,18 @@ int process_headers(int infd, FILE *outf, const c= har *from) > >> return -1; > >> } > >> free(new_line); > >> + } else if ((to_be_subst =3D strstr(p, "$postername$")) !=3D NULL) { > >> + *to_be_subst =3D '\0'; > >> + to_be_subst +=3D 12; > >> + xasprintf(&new_line, "%s%s%s", p, name, to_be_subst); > >> + if (fprintf(outf, "%s", new_line) < 0) { > >> + log_error(LOG_ARGS, "Could not write headers"); > >> + free(new_line); > >> + free(line); > >> + fclose(f); > >> + return -1; > >> + } > >> + free(new_line); > >> } else { > >> if (fprintf(outf, "%s", p) < 0) { > >> log_error(LOG_ARGS, "Could not write headers"); > >> diff --git a/src/find_email_adr.c b/src/find_email_adr.c > >> index 0830098..77b7cef 100644 > >> --- a/src/find_email_adr.c > >> +++ b/src/find_email_adr.c > >> @@ -239,3 +239,55 @@ oncemore: > >> =20 > >> return retstruct; > >> } > >> + > >> +/* ------------------------------------------------------------------= ---------*\ > >> + * Extract the display name of the first user, > >> + * removing comments, for example: > >> + * > >> + * for the input string: > >> + * "John (Amazing) Smith" <[email protected]>, > >> + * "Jane Doe" <[email protected] > >> + * > >> + * returns: "John Smith" > >> +\* ------------------------------------------------------------------= ---------*/ > >> +char * > >> +find_display_name (char *name) > >> +{ > >> + char *p, *nbuf, c; > >> + int nbuf_len, saw_angled_brackets; > >> + > >> + if (!name) return NULL; > >> + nbuf =3D xmalloc(strlen(name)); > >> + saw_angled_brackets =3D 0; > >> + nbuf_len =3D 0; > >> + > >> + for (p =3D name; (c =3D *p) !=3D '\0' && isspace(c); p++); > >> + while ((c =3D *p++) !=3D '\0') { > >> + switch (c) { > >> + case '(': > >> + p =3D skip_comment(p); > >> + /* -------------------------------*\ > >> + * Copy at most one space in place > >> + * of the comment. Don't copy any > >> + * whitespace if we are still at > >> + * the beginning of the string. > >> + */ > >> + if (nbuf_len > 0 && isspace(*p) && > >> + !isspace(nbuf[nbuf_len-1])) { > >> + nbuf[nbuf_len++] =3D *p++; > >> + } > >> + while(isspace(*p)) p++; > >> + /* -------------------------------*/ > >> + break; > >> + case '<': saw_angled_brackets =3D 1; goto finished; > >> + case ',': goto finished; > >> + default: nbuf[nbuf_len++] =3D c; break; > >> + } > >> + } > >> + > >> + finished: > >> + if (!saw_angled_brackets || nbuf_len =3D=3D 0) { free(nbuf); return = NULL; } > >> + for (p--, p--; (c =3D *p) && isspace(c); p--, nbuf_len--); > >> + nbuf[nbuf_len++] =3D '\0'; > >> + return nbuf; > >> +} > >> --=20 > >> 2.46.4 > >> >=20 >=20 --=20 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D Independend GmbH [email protected] +49 6832 265556 http://ind.me --qdrw4o4n223Z4PZh Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEM0qOw5hydnArKgH2jALoLo7pDpwFAmnJKfQACgkQjALoLo7p DpzeKQ/9FcbMOlqMi/gdidud5Cqk4hKoBU3OBTrwBs8MKx8FpYjfofOHijnUxsoe vOH0jufeyhBwgVtsq9+rmMzkg1KFQm+UGjyrZqGpN6+USpVdQdTJrPLYHKOX3X+2 lkuEZFF5W4qQNyTv2eBZuZlsYo9heZ0blqniw0mG/LOMI+Px9wDXRrZL6ZHij4Sf AfvBwbvZufCedbuny9Hfr0viYP1faLcX4OILglXYiUolTJDJYfmXIW2eALDNCxuw /GbMuO0oQcw52fYFYy4ldojZODCHH4BtZvB5cUfLa1F2nSMxVHVCDgeNyX+y+gI7 c2IMrkYNsVvQRb8IVZmeMiLBAQKogLF0Dq+8pttSSTmEsbbpqepuknZjCfkSwcKQ 2SU9FVbIKhdPr5hD1BJXw0+j4ZZyMjy76tcSNt37KmefCeSgjAuh2WpvW9poV20D gW4/tfnk7PWh/bvOp63liFz+a4u7jUbyCXAdvsKPu6vHiyL1PSJIoJqOehDlR6r2 bwhreYn3h1gn/N9aKK5fHMdmHMcxCtOqdWbVPgbSydWRJPIRkmbTptPbVcIHkNoY 8OY3crry3Fi+8n+7xhQRvVqJtPCzflUbB32LAh8mftOxiX0JatJtAnUIXVe5g7dz m/r2o6YOUBy9QvNEx3rXZGUa7fkygSKJ/geClnxKGnCQn2keKnA= =OtV5 -----END PGP SIGNATURE----- --qdrw4o4n223Z4PZh--