• POP3: TOP sends and kill-file deletes attachments; MSG_FILEATTACH left

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Tue Aug 11 19:24:27 2026
    open https://gitlab.synchro.net/main/sbbs/-/issues/1226

    Two related defects in the POP3 server's handling of file attachments (`src/sbbs3/mailsrvr.cpp`), found while investigating a
    `!ERROR opening/encoding/sending` in `data/error.log`.

    ### 1. `TOP` transmits attachments, and deletes them

    `TOP` and `RETR` share `sockmsgtxt()` / `sockmimetext()`, and the `maxlines` argument bounds **only** the body-text loop. The attachment loop that follows it is unguarded, so `TOP <n> 0` — a headers-only preview — MIME-encodes and transmits the entire attachment, and on success `remove()`s the file when `MSG_KILLFILE` is set.

    RFC 1939 defines `TOP` as the headers, a blank line, and the first *n* lines
    of the body. Sending a multi-megabyte attachment to a client that asked for zero body lines is wrong on its own; destroying the file during a preview is data loss. A client that sweeps the mailbox with `TOP n 0` before deciding
    what to `RETR` will consume every kill-file attachment in the preview pass and then deliver an empty MIME part on the `RETR` that follows.

    The `tls_dkim_cap` guard on the same `remove()` call is the identical failure mode already handled for one caller (the DKIM sign/send two-pass), where rendering a message twice deleted the file on the first pass.

    ### 2. `MSG_FILEATTACH` survives the kill-file removal

    After a successful `RETR` the attachment file is removed, but the message keeps `MSG_FILEATTACH | MSG_KILLFILE` in its header and stays in the mailbox. Every subsequent fetch of that message re-attempts the attachment against a file that the earlier fetch deleted, and logs:

    ```
    mail 4400 POP3 !ERROR opening/encoding/sending s:\sbbs\data\file/0614.in/new_textures.png
    ```

    Any client configured to leave mail on the server hits this on its next poll. With two clients on one mailbox it is unavoidable — whichever polls second never receives the attachment at all.

    ### Observed sequence

    One local message, `MSG_FILEATTACH | MSG_KILLFILE` (`auxattr` 0x0a), attached via the Terminal Server; mailbox polled by two clients against two hosts sharing the mail base:

    | Time (2026-08-11) | Server | Command | Result |
    |---|---|---|---|
    | 02:24:36 | Linux | `RETR 857` | attachment sent, file removed |
    | 18:10:30 | Windows | `TOP 857 0` | file missing, error logged |
    | 18:11:14 | Windows | `RETR 857` | file missing, error logged |

    The message's `auxattr` is still 0x0a afterwards; nothing clears it.

    ### Suggested fix

    1. Propagate the partial-fetch state into `sockmimetext()` and skip the
    attachment loop entirely when the client issued `TOP` (`maxlines != -1`).
    2. Clear `MSG_FILEATTACH` (and `MSG_KILLFILE`) from the header once the
    `remove()` succeeds. The POP3 path already rewrites the header there to set
    `MSG_READ`.

    — *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)