[PATCH] Support display name substitution in customheaders
Lumin Etherlight <[email protected]> Fri, 27 Mar 2026 21:14:16 +0300
| Newsgroups | org.mlmmj.mlmmj |
|---|---|
| Organization | Etherlight Systems - https://etherlight.link |
| Message-ID | <83Su95j/[email protected]> |
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.
This allows the following `customheaders' use-case:
From: $postername$ via Project-List <[email protected]>
Which would render, for example, as:
From: John Doe via Project-List <[email protected]>
For the user:
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(-)
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>
strlist *find_email_adr(const char *str, strlist *retval);
+char *find_display_name(char *name);
#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 char *name);
#endif /* WRAPPERS_H */
diff --git a/src/do_all_the_voodoo_here.c b/src/do_all_the_voodoo_here.c
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 = tll_init();
char *posteraddr = NULL;
+ char *postername = NULL;
bool hdrsadded = false;
bool subject_present = false;
int dupfd = dup(infd);
@@ -111,9 +112,16 @@ int do_all_the_voodoo_here(int infd, int outfd, int hdrfd, int footfd,
if(hdrfd >= 0) {
strlist fromemails = tll_init();
if ( readhdrs[0].valuecount == 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 = xstrdup(tll_front(fromemails));
+ postername = find_display_name(readhdrs[0].values[0]);
+ if (!postername) {
+ postername = 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) == 0)) {
if(hdrfd >= 0) {
- if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "") < 0) {
+ if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "", postername ) < 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,
if(!hdrsadded ) {
if(hdrfd >= 0) {
- if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "") < 0) {
+ if(process_headers(hdrfd,outf,posteraddr ? posteraddr : "", postername) < 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, int 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, int 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;
}
fsync(outfd);
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);
}
-int process_headers(int infd, FILE *outf, const char *from)
+int process_headers(int infd, FILE *outf, const char *from, const char *name)
{
char *line = NULL, *p, *to_be_subst, *new_line;
size_t linecap = 0;
@@ -120,6 +120,18 @@ int process_headers(int infd, FILE *outf, const char *from)
return -1;
}
free(new_line);
+ } else if ((to_be_subst = strstr(p, "$postername$")) != NULL) {
+ *to_be_subst = '\0';
+ to_be_subst += 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:
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 = xmalloc(strlen(name));
+ saw_angled_brackets = 0;
+ nbuf_len = 0;
+
+ for (p = name; (c = *p) != '\0' && isspace(c); p++);
+ while ((c = *p++) != '\0') {
+ switch (c) {
+ case '(':
+ p = 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++] = *p++;
+ }
+ while(isspace(*p)) p++;
+ /* -------------------------------*/
+ break;
+ case '<': saw_angled_brackets = 1; goto finished;
+ case ',': goto finished;
+ default: nbuf[nbuf_len++] = c; break;
+ }
+ }
+
+ finished:
+ if (!saw_angled_brackets || nbuf_len == 0) { free(nbuf); return NULL; }
+ for (p--, p--; (c = *p) && isspace(c); p--, nbuf_len--);
+ nbuf[nbuf_len++] = '\0';
+ return nbuf;
+}
--
2.46.4