• src/sbbs3/mailsrvr.cpp

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Friday, August 07, 2026 15:55:33
    https://gitlab.synchro.net/main/sbbs/-/commit/7df7ab3a7d825846e02790fc
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    Don't tag an authenticated user's local mail as netmail (issue #1215)

    An SMTP-authenticated user's message to a local recipient was stored with SENDER_NETTYPE of NET_INTERNET and a SENDERNETADDR of the sender's own
    address on one of this system's own domains, even though both parties are
    local users. Mail readers key off the sender net type to decide between a netmail reply and a regular e-mail reply, so replying to such a message
    tried, and failed, to send netmail to an address this system won't route.

    The NET_NONE branch dates from d21065229b (knee-4-prepare, 2009-11-12),
    which qualified it with "subnum != INVALID_SUB" because it was added for messages posted to a sub-board via authenticated SMTP. E-mail was never covered.

    The sender header fields are written once, before the per-recipient loop,
    and a single submission can name both local and remote recipients, so the
    net address can't simply be dropped: for a recipient on another system it
    is what selects the reverse-path as the envelope sender, and what routes a delivery-failure notice back. Set the sender to the authenticated user's
    alias up front, and add the sender net type and address per recipient
    copy, only for the copies whose recipient is not local.

    Mail leaving the system is unchanged. A local recipient's copy now looks
    like any other local e-mail: sender alias plus SENDEREXT, no net address.
    The From header presented over POP3 and IMAP is unaffected, as it is
    rendered from the preserved RFC822FROM field.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Sunday, August 09, 2026 07:19:11
    https://gitlab.synchro.net/main/sbbs/-/commit/6bd3794453efbefffbb6ebd7
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: don't pass an uninitialized buffer to filterFile::listed()

    email_addr_is_exempt() declared a local 'fname' buffer and handed it to listed() as the optional second search string without ever initializing
    it. That argument is a second candidate matched against the same list,
    so trash_in_list() ran parse_ip() and findstr_compare() over
    uninitialized stack memory: a sender could be spuriously exempted from
    DNSBL checking by whatever the stack happened to hold, and the compare
    walks past the buffer when that junk contains no terminator.

    The buffer is a leftover from the pre-cache API, where it held the dnsbl_exempt.cfg path for findstr(netmail, fname). 8409089bbc (pulse-4-memories, 2026-02-12) replaced that call with a cached
    filterFile lookup and dropped the SAFEPRINTF that filled the buffer,
    but kept passing it.

    Only one address is looked up here, so drop the argument (it defaults
    to nullptr) along with the buffer.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Tuesday, August 11, 2026 21:39:59
    https://gitlab.synchro.net/main/sbbs/-/commit/2f379c7389b043fc4da124a6
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: don't send or kill attachments during a POP3 TOP (issue #1226)

    TOP and RETR share sockmsgtxt(), where maxlines bounds only the body-text
    loop: the attachment loop that follows it ran unconditionally. A
    headers-only "TOP n 0" therefore MIME-encoded and transmitted the entire attachment, then removed the file when MSG_KILLFILE was set, destroying
    it during what the client had asked for as a preview. A client that
    sweeps a mailbox with TOP before choosing what to RETR consumed every
    kill-file attachment in the preview pass and received an empty MIME part
    on the retrieval that followed.

    Skip the attachment loop for a partial fetch, closing the multipart after
    the truncated text part so that TOP and RETR still describe the same
    message structure.

    Separately, a successful RETR removed the attachment file but left MSG_FILEATTACH set on a message that remains in the mailbox, so every
    later fetch re-attempted an attachment whose file the earlier fetch had deleted:

    !ERROR opening/encoding/sending data/file/NNNN.in/attachment.png

    Any client configured to leave mail on the server hits that on its next
    poll, and with two clients polling one mailbox the second never receives
    the attachment at all. Report the kill back to the POP3 thread and clear MSG_FILEATTACH/MSG_KILLFILE where the header is already being rewritten.
    That update no longer depends on MAIL_OPT_NO_READ_POP3, which is
    precisely the setting a leave-mail-on-server client runs under.

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Tuesday, August 18, 2026 22:04:59
    https://gitlab.synchro.net/main/sbbs/-/commit/126ed841933b774477b5c1b1
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: name the rejected address in the sender-auth 550 replies

    The 550 reply for an unauthorized MAIL FROM or From: address named only the address to use instead, never the address that was rejected. The submitter
    is left to guess which of the settings in their mail client the server is objecting to, and only the server log records it -- which the submitter
    cannot read.

    Seen on Vertrauen: "550 Sender address not authorized for this account, try <PHARCYDE@vert.synchro.net>" was read as a statement about the server
    hostname the client had connected to, and answered with a question about
    which hostname to use, when the setting at fault was the sender address.

    Grow from_errmsg to 512 bytes: the header 'FROM' message now formats two addresses of up to 127 characters each, which 256 bytes could truncate.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wednesday, September 30, 2026 19:08:47
    https://gitlab.synchro.net/main/sbbs/-/commit/c6c715a40a2556421292cdc8
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    Mail Server: don't log POP3 passwords or SMTP AUTH credentials

    Fixes #1267

    With the receive/response debug options on, the mail server logged the
    POP3 PASS line, the SMTP AUTH LOGIN username and password responses, the
    AUTH PLAIN response (all base64, trivially decoded), and the AUTH PLAIN
    initial response on the command line was logged at LOG_DEBUG regardless of options. The "!Bad AUTH ..." notices logged the raw argument at NOTICE
    level. Anyone who could read the log could recover every password used to authenticate to the server.

    Redact those lines unless the sysop has set the "Echo Passwords Locally"
    system option (SM_ECHO_PW), the way the password-failure messages already
    honor it: a received command line is logged through loggable_line(), which replaces a POP3 password or an AUTH initial response with a placeholder,
    and a line that is nothing but credentials (an AUTH response) is logged as "<credentials>".

    Verified on a scratch mail server with the debug options on: POP3 USER/PASS, AUTH PLAIN (initial response and challenge forms), AUTH LOGIN and a
    malformed AUTH LOGIN argument all authenticate or fail as before, and the
    log holds no trace of the password in clear or base64; with SM_ECHO_PW set
    the lines are logged in full, as before.

    Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Tuesday, October 13, 2026 23:29:30
    https://gitlab.synchro.net/main/sbbs/-/commit/d4f33e47976414b619032701
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: stop sending the message after a body line fails to send

    When a send failed partway through the message body, sockmimetext()
    broke out of the body loop but still went on to MIME-encode any
    attachments and send the "." end-of-data terminator over the dead
    connection. With DKIM signing enabled, that terminator finalized the
    verify pass's body hash over the truncated body, so every dropped
    connection mid-DATA was also logged as the error "DKIM body hash changed between sign and send - message delivery aborted", though nothing had
    changed and the delivery had already failed.

    Return as soon as a body line fails to send, as is already done when the
    header terminator fails.

    Fixes #1279

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Tuesday, October 13, 2026 23:29:30
    https://gitlab.synchro.net/main/sbbs/-/commit/1728d248fb27d5d1cf0737e9
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: POP3 LIST/UIDL skips unreadable messages instead of stopping

    In the POP3 LIST and UIDL (all messages) loop, a failure to read a
    message's index entry, lock its header, or read its header ended the
    listing early. Every message after the bad one was then missing from
    the client's view of the mailbox. Skip just the bad message instead:
    the error is still logged, and the other messages keep their POP3
    numbers, so RETR and DELE still work on them. A failed socket write
    still ends the loop.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows 11)@VERT to Git commit to main/sbbs/master on Monday, October 26, 2026 00:05:59
    https://gitlab.synchro.net/main/sbbs/-/commit/dd868cf96f01d669158e480b
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    Mail server: keep the mailproc script's callback struct alive with its context

    Fixes a crash in sbbs.dll (remove_js_socket_event) when an SMTP client
    thread ends.

    b43e1c8256 (2009-01-14) made js_mailproc() reuse one JS runtime, context
    and global object per SMTP client thread, created on the first mailproc
    and destroyed only when the thread ends, but left its js_callback_t as a
    local of js_mailproc(). The "js" object and the client.socket Socket
    object created in that persistent global recorded a pointer to the local,
    which was gone as soon as the first js_mailproc() call returned.

    Nothing followed that stale pointer after the call until 85e04a4262 (2026-09-30) made every Socket object remember its script's callback
    struct at creation and consult it when closed. From then on, destroying
    the context at thread end finalized client.socket, do_js_close() walked
    the event list of a js_callback_t that was a dead stack frame, and the
    mail server crashed with an access violation.

    Move the js_callback_t to smtp_client_thread(), beside the runtime,
    context and global, and pass it to js_mailproc(). It is still zeroed and
    given the logger on each invocation, as before.

    Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wednesday, November 11, 2026 13:04:30
    https://gitlab.synchro.net/main/sbbs/-/commit/59bb834db75f09c19a9bfd0c
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: don't treat a failed user re-read as an unauthenticated client

    When an authenticated SMTP client sent to an external address, the relay
    check re-read the user's record with getuserdat() and ignored the result. getuserdat() zeroes user->number on failure, so a transient error such as
    a user record lock timeout made the session unauthenticated: the RCPT was refused as an "ILLEGAL RELAY ATTEMPT" (553/550, a permanent failure) and
    the rest of the session ran without the user. The emails-per-day check
    re-read the record the same way.

    Re-read into a copy that only replaces the session's user record on
    success, and answer a failure with a 451 temporary error so the client
    retries.

    Seen with GitLab's notification mail: concurrent sessions for the same
    user stalled ~105 seconds in lockuserdat() and one was rejected as an
    illegal relay.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net