mlmmj-process: clean up queue file on do_all_the_voodoo_here failure
When do_all_the_voodoo_here() fails, the partially written queue file
is left behind. Add unlink() to match the cleanup done in the earlier
rawmailfd open failure path.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When send_mail() fails for the only (or last) recipient, the address
gets popped into the 'addr' parameter, leaving 'addrs' empty. The
early return was checking only if addrs was empty, ignoring addr.
Effect: when sending to a single recipient fails, that recipient is
not saved to the requeue file and is lost entirely. The return value
of 0 (false) propagates back as "success", causing the caller to
delete the queue file as if sending succeeded.
Fix by only returning early when there is nothing to save (both addrs
empty AND addr NULL). This ensures failed recipients are properly
requeued for retry.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
listcontrol: replace __attribute__((fallthrough)) with comment
The __attribute__((fallthrough)) statement was added in GCC 7. Older
versions treat it as an empty declaration and emit a warning. Use a
/* fallthrough */ comment instead, which GCC (-Wimplicit-fallthrough)
and Clang both recognise as an intentional fallthrough annotation.
Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org> Assisted-by: claude-opus-4-6
Identifiers with double-underscore prefixes are reserved for the C
implementation. Older glibc headers (bits/stat.h) use __unused as a
struct field name, so the mlmmj macro expansion produces invalid syntax
when that header is included transitively via fcntl.h.
Rename to MLMMJ_UNUSED to avoid the conflict and stay out of the
reserved namespace.
Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org> Assisted-by: claude-opus-4-6
mlmmj-process: fix NULL deref in owner mail reprocessing path
When mail is addressed to listname+owner, mlmmj-process calls
do_all_the_voodoo_here() a second time to strip envelope headers
before forwarding to the list owner. This second call reuses the
same allheaders list that was already populated by the first call.
Inside do_all_the_voodoo_here(), scan_headers() appends the new
mail's headers to the passed-in allhdrs list, while allunfoldeds
is a fresh local list containing only the headers from the current
scan. The main loop then iterates all entries in allhdrs (which now
includes headers from both calls) while popping from allunfoldeds
(which only has entries from the second scan). Once the unfolded
entries are exhausted, tll_pop_front() on the empty list dereferences
NULL and crashes.
Fix this by passing a separate local strlist to the second call so
that allhdrs and allunfoldeds stay in sync.
Signed-off-by: Konstantin Ryabitsev <konstantin@linuxfoundation.org> Assisted-by: claude-opus-4-6
Set MALLOC_CHECK_=3 in test environment to detect memory corruption
issues like double-free and buffer overflows. When detected, glibc
will print a diagnostic and abort the program.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The condition
if (fd == -1 && !lock(fd, true))
does not make sense: if fd is -1 it will try to lock the fd.
Of course locking -1 does nothing.
What is meant is
if (fd == -1 || !lock(fd, true))
in other words if fd is valid, lock it.
Affected locations:
- src/mlmmj.c: unsubscribe() - writing subscriber .new files
- src/subscriberfuncs.c: subscribe_type() - appending to subscriber files
- src/incindexfile.c: incindexfile() - updating list index
Without this fix, these operations proceed without file locking, which
can at least theoretically cause data corruption if multiple processes
operate on the same files concurrently.
Fixes: 2af27b24 ("locking: use a more portable mechanism") Fixes: f9da8d13 ("mlmmj-sub: move all subcription code into a reusable function") Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
mlmmj-process: address TODO to free parsed headers
Add free_parsed_hdrs() helper to free readhdrs and all strlist
variables (fromemails, toemails, ccemails, etc.) before exit.
This is a cosmetic fix since mlmmj-process is a short-lived
executable that exits immediately after processing, so the OS
reclaims all memory anyway. However, it improves code hygiene
and removes the XXX comment.
Fixes: 4059b78 ("do_all_the_voodo_here introducton plus a massive cleanup")
subscriberfuncs: fix memory leak in autosubscribe_sender
The readhdrs structure allocated by scan_headers() was never freed,
causing a memory leak on every call to autosubscribe_sender(). Add
free_mailhdrs() calls to all return paths.
when a mailing list moderated for non subscribed people, if a moderator
accept the email of the sender automatically register the send in the
list of "nomail" subscribers.
Implement a new moderation_modemailsender test.
It is identical to moderation_modemailsender, but
sets modemailsender and verifies that the moderation email
goes to the sender.
Some people make their lists subscriber-only for the only reason
that they want senders to confirm they can receive email.
For example, this is a weak form of anti-spam (senders which can not
receive mail and reply to it are filtered out).
However, this annoys one-time contributors which really just want to
post a random mail here and there.
To address this, add a new modemailsender option for sending moderation
emails back to senders instead of moderators. They can now then confirm
it themselves - less work for moderators!
Specifically, when the control/modemailsender file is present,
moderation emails are sent to the original sender instead of moderators.
Only affects moderated lists.
Graham Leggett [Fri, 3 Jan 2025 14:27:36 +0000 (14:27 +0000)]
Add option to copy From: to Reply-To:
The replyto boolean causes the original From: header to be copied
to Reply-To:, so that emails can be accepted from senders that
enforce DMARC policies.
this is too naive and cannot cope with the complexity of emails.
Either mails are restricted to be plain/text and control/footer is
enough or preprocess with ah external program the email, this work
does not belong directly to mlmmj and I don't intend into maintaining
this code.
if a qualifier was passed to the deny action, this qualifier is then
used as an extension when looking for the template of deny message to
send to the user.