• docs/v322_new.md src/sbbs3/websrvr.cpp

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Jul 31 21:43:31 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/72303693ef1b8dc2c11ff006
    Modified Files:
    docs/v322_new.md src/sbbs3/websrvr.cpp
    Log Message:
    websrvr: enforce download credits for unauthenticated file requests

    An anonymous request through the file-area virtual path
    (FileVPathPrefix) was authorized by user_can_download() alone, with no
    credit check, so every file in a directory with empty ARS downloaded
    free of charge regardless of its cost. The FTP server applies the
    credit gate to anonymous users, and issue #446 was closed with that
    being the intended policy.

    Both authorization paths now share check_file_download(), which applies
    the credit gate before the ARS check. Putting the two callers on one
    predicate is the point of the helper: the bypass existed because the
    anonymous branch and the authenticated one had drifted apart.

    The gate measures cost against user_available_credits() rather than
    user.cdt, so a user's free credits now count toward a download, as they
    already do on the Terminal and FTP servers, and as subtract_cdt()
    already assumed when charging: it spends free credits first.

    A refused anonymous request is logged at LOG_DEBUG. An insufficient
    credit refusal now reports NotEnoughCredits; both this and the existing authenticated failure log previously reported CantDownloadFromDir for
    it, naming the wrong cause.

    Fixes #1192

    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 Sat Aug 1 20:33:36 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/4de1032086f038890a75559e
    Modified Files:
    docs/v322_new.md src/sbbs3/websrvr.cpp
    Log Message:
    websrvr: load a file's cost before checking download credits (#1192)

    The credit gate added in 72303693ef (2026-07-31) compared against a
    cost that was always zero. resolve_vpath() loaded the file record at file_detail_index, and smb_getfile() skips smb_getmsghdr() entirely at
    that detail level, while cost is only ever set from the SMB_COST header
    field. Every comparison against it therefore passed: costed files
    downloaded free of charge for authenticated users as well as anonymous
    ones, while user_downloaded_file() still billed the account afterward
    (it reloads the record itself at file_detail_normal), so a user could
    go credit-negative.

    Read the header record when the directory's files can cost something. A
    free directory has none to report, since loadfile() zeroes the cost and download_is_free() short-circuits on the same DIR_FREE flag before any
    credit comparison, so the index record alone still suffices there and loadfile() is not asked to open the base twice.

    Verified against a live file base: an unauthenticated request for a 2,639,434-credit file is refused with 401, a zero-cost file in the same non-free directory returns 200, and a file in a free directory
    transfers in full.

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

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