• New user can inherit the previous (deleted) user's e-mail when their r

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Thursday, July 16, 2026 13:37:59
    open https://gitlab.synchro.net/main/sbbs/-/issues/1186

    A newly-created user can inherit the e-mail of the previous (deleted) user whose record slot they were given. Reported by nelgin: a new user found messages in their inbox addressed to a *different* user with a similar name, on a system where users aren't normally deleted and where the new account holds the last (highest) user number.

    ## Root cause

    Inbox membership is decided solely by user number: `loadmail()` keeps a message if `idx.to == usernumber` (`src/sbbs3/getmail.c:141`), and for the mail base `idx.to` is just `atoi(msg->to_ext)` (`src/smblib/smblib.c:1804-1808`). The recipient *name* in the header is never consulted. So any mail left in `mail.shd` tagged with number N belongs to whoever holds slot N now.

    `del_user()` (`src/sbbs3/userdat.c:231`) marks the record `DELETED` and blanks the index name, but does not touch the mail base — correctly so, since `undel_user()` (`userdat.c:258`) can restore the account and purging at delete time would make undelete lossy. The purge therefore belongs at *slot reuse* time.

    `newuserdat()` (`src/sbbs3/userdat.c:3645`) is where slot reuse happens: it scans the user index for an empty slot whose user is flagged `DELETED` and has aged past `sys_deldays`, and reuses that number (lines 3667-3688). It then purges the previous occupant's leftovers — `data/file/NNNN.in`, `.out`, `data/file/NNNN.*`, `data/user/NNNN.*`, `data/msgs/NNNN.*`, the legacy `.ixb` pointers (lines 3713-3731) — but never the mail base.

    The only mail purge lives in the terminal-server signup path, `sbbs_t::newuser()`, which follows its `newuserdat()` call with `delallmail(useron.number, MAIL_ANY)` (`src/sbbs3/newuser.cpp:265`). Every other caller of `newuserdat()` skips it:

    - `web/root/newuser.ssjs:343` — the stock **web signup**, via `system.new_user()` (`src/sbbs3/js_system.cpp:1925`)
    - `web/root/ecwebv3/pages/999-newUser.ssjs:128`
    - `exec/makeuser.js:201`, `exec/ircbots/admin/admin_commands.js:181`
    - the `makeuser` utility (`src/sbbs3/makeuser.c:214`)

    So a web-created user inherits the dead user's inbox, while a terminal-created one does not.

    ## Why "no deletions" and "last user number" still fit

    The scavenger will reuse the *highest* slot if its occupant is deleted and aged out, so the new user legitimately holds the last user number. And the sysop needn't have deleted anyone by hand: aborted signups and `sys_autodel` inactivity purges (`src/sbbs3/main.cpp:4916`) both produce deleted slots. The "similar name" is expected — the mail's RECIPIENT header carries the previous occupant's name.

    Mail *sent* by the previous user is the same class of bug: it sits in live users' inboxes with `idx.from == N` and is re-attributed to the new user. This is why the existing purge uses `MAIL_ANY`.

    ## Reproduction

    Verified against a pristine build of current master, using an isolated ctrl/data tree with `deldays=0`:

    ```
    created user #1 'Alpha One'
    created user #2 'Beta Two'
    created user #3 'Bob Smith'
    target (last) user number: 3
    delivered 1 message to #3
    deleted user #3
    new user #3 'Bob Smyth'
    INBOX: to='Bob Smith' (#3) from='Alice' subject='Private stuff'

    RESULT: BUG - new user inherited 1 message(s)
    ```

    Steps: create users so the target is the last slot; deliver mail to the last user; `system.del_user()` it; then `system.new_user()` a similar name (the JS/web path). The new account reuses the slot and reads the dead user's mail.

    ## Suggested fix

    Move the purge into `newuserdat()`, alongside the per-user cleanup it already performs, so every signup path is covered rather than just the terminal one. That needs a C-level mail-delete helper (in `getmail.c`), since `delallmail()` is an `sbbs_t` method and `newuserdat()` is plain C reached by utilities that don't link the BBS core. The `delallmail()` call in `newuser.cpp` then becomes redundant.

    Note that adding such a helper to `getmail.c` makes `userdat.o` depend on `getmail.o`, which several link units (`makeuser`, `scfg`, `jsdoor`, `dupefind`, `gtkchat`, `upgrade_to_v319`/`v320`) don't currently include — both the GNU make object lists and the MSVC projects need updating.

    ## Related observation

    `system.del_user()` (`src/sbbs3/js_system.cpp:1961`) doesn't purge mail either, while `useredit` (`useredit.cpp:748`+`754`) and the `sys_autodel` sweep (`main.cpp:4916`) both do. Same shape: the C API leaves the purge to its callers and the JS binding forgets. Harmless once the purge is done at slot-reuse time, but worth noting.

    — *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)
  • From Rob Swindell@1:103/705 to GitLab note in main/sbbs on Thursday, July 16, 2026 13:38:12
    https://gitlab.synchro.net/main/sbbs/-/issues/1186#note_9587

    Companion issue: #1187 (sbbsecho: netmail addressed by user number bypasses the deleted-user check) — a feeder path that deposits mail into a deleted user's slot in the first place.

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