• src/sbbs3/mailsrvr.cpp

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wed May 6 19:41:53 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/92ae6263408f0ddd5d05d802
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: bound sockmimetext line scan with strnlen (CID 639931)

    The inner while-loop walks (*np + len) up to RFC822_MAX_LINE_LEN bytes
    relying on the embedded NUL test to stop early. When np points at the
    "\r\n" literal used as the empty-body fallback (issue #822), Coverity
    loses track of the literal's length and reports a 997-byte OVERRUN.
    Compute the scan length up-front with strnlen so the bound is explicit; behavior is unchanged but the OVERRUN false-positive is silenced.

    Co-Authored-By: Claude Opus 4.7 <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 Thu May 14 03:17:17 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/52099548d7f5782b818bdbe7
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: POP3 reply -ERR (not !UNSUPPORTED) to USER/PASS in TRANSACTION state

    Some clients (e.g. Thunderbird) reuse an already-authenticated TCP socket
    and re-issue USER/PASS. Per RFC 1939 these are AUTHORIZATION-state-only commands, so respond with a plain -ERR and keep the session alive,
    matching Dovecot/Courier behavior and suppressing the misleading
    "!UNSUPPORTED COMMAND" log notice.

    Refs main/sbbs#1123

    Co-Authored-By: Claude Sonnet 4.6 <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 Mon Jul 27 02:09:08 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/7e492e0eeb8e9cd553fb6a2f
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mail: only auto-detect an IPv4 DNS server address

    dns_getmx() takes the server as a DWORD, so an IPv6 address cannot reach
    it: resolve_ip() returns INADDR_NONE, the send is abandoned with the
    message left for a later attempt, and "INVALID DNS server address" is
    logged. get_dns_server() picked at random from every address getNameServerList() reported, so a host with resolvers of both families
    failed on some sends and not others -- and on Windows the list can now
    include IPv6 servers where it never could before.

    Filter to the addresses the look-up can use, and say so when a detected
    list holds none of them, rather than leaving the caller to report an empty address as invalid.

    The precision on the adjacent sprintf() was one greater than the room the buffer has for text.

    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 Windows 11)@VERT to Git commit to main/sbbs/master on Mon Jul 27 02:10:11 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/b19bdde0674b55673b4d1005
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: log the offending command on invalid POP3 message numbers

    The POP3 LIST/UIDL, RETR/TOP, and DELE handlers logged only the parsed
    message number when rejecting an out-of-range argument. When the argument
    is missing or non-numeric, strtoul() yields 0, so the log always reads
    "INVALID message #0" with no indication of what the client actually sent
    -- exactly the case where the command text is needed to diagnose it.

    Log the command line and the mailbox message count, matching the detail
    already present in the successful-retrieval log message.

    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 Windows 11)@VERT to Git commit to main/sbbs/master on Tue Jul 28 15:19:37 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/e0b7043e35c210a67ec71999
    Modified Files:
    src/sbbs3/mailsrvr.cpp
    Log Message:
    mailsrvr: don't mistake a quoted address local-part for a display name

    A QWKnet reply sent via SMTP failed to import at the destination BBS:

    QWK NetMail from VERT to EOTLBBS!nelgin
    !QWK NetMail from VERT to UNKNOWN USER: EOTLBBS!nelgin

    smtp_netmailaddr() renders a QWKnet sender as qwkid!user@host, so a
    reply is addressed to e.g. <EOTLBBS!nelgin@vert.synchro.net> -- and a
    mail client may quote the local part, which RFC 5322 allows:

    To: nelgin <"EOTLBBS!nelgin"@vert.synchro.net>

    parse_mail_address() took the first quote anywhere in the string as the
    start of a display name, so it returned "EOTLBBS!nelgin" as the name.
    The SMTP envelope had been parsed correctly (recipient "nelgin", routed
    to QWKnet node EOTLBBS), but the To: header re-parse overwrites the same buffer, and that name is what gets stored as the message recipient. The
    QWK packer writes it verbatim, and the receiving system finds no such
    user.

    Treat a quote as a display-name delimiter only where a display name can
    appear -- before the '<' of the address -- and, for a QWKnet recipient,
    take the name from the envelope rather than the To: header, as the
    FidoNet case already does. The header parse also feeds the sender name
    of unauthenticated mail, so an odd From: address was mangled the same
    way.

    The quoted/parenthesized display-name handling dates to cbcda654b4 (2002-08-22).

    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 Fri Aug 7 15:55:33 2026
    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 Sun Aug 9 07:19:11 2026
    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 Tue Aug 11 21:39:59 2026
    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