• docs/v322_new.md exec/loa

    From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Sunday, June 07, 2026 18:41:00
    https://gitlab.synchro.net/main/sbbs/-/commit/6033e9d61dad951da5d36309
    Modified Files:
    docs/v322_new.md exec/load/binkp.js
    Log Message:
    BinkIT: don't record successful binkp/1.1 callouts as failures

    The JSBinkP session loop only breaks out on *receiving* a final M_EOB,
    but in binkp/1.1's two-M_EOB handshake the completed state is usually
    reached by *sending* the last EOB. After sending it, the peer closes
    the connection, and the next loop iteration attempts one more M_EOB on
    the now-closed socket; that send fails and (since b795cf6a3d) marked the
    whole session as failed -- even though all files had already been sent
    and acknowledged. binkit.js then wrote "[callout failure]" to data/binkstats.ini with the sent file(s) listed.

    binkp/1.0 peers (Mystic, mbcico) exchange a single M_EOB each and break immediately on receipt, so they were recorded correctly; the bug hit
    binkp/1.1 peers (Synchronet/BinkIT, binkd) -- the vast majority of
    sessions -- producing huge "failed_callouts" counts despite mail
    flowing fine. Reported in DOVE-Net's sync_sysops by Khronos and Gamgee.

    Fix: a failed closing M_EOB send is benign once our sent files have all
    been acknowledged (pending_ack empty); break the loop without failing
    the session in that case. Only fail when files remain unacknowledged.
    The on-wire behavior is unchanged. Bumped JSBinkP revision to 6 so the
    fix is identifiable via the vers= field in binkstats.ini.

    Validated with a two-process TCP loopback harness: binkp/1.1 callout
    flips from false to true (file transferred either way); binkp/1.0 and
    no-files polls unchanged; a peer that drops mid-transfer still fails.

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

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian L@VERT to Git commit to main/sbbs/m on Friday, June 26, 2026 01:24:00
    https://gitlab.synchro.net/main/sbbs/-/commit/c00526fb8f7f91aa16e28eee
    Modified Files:
    docs/v322_new.md exec/load/salib.js exec/spamc.js
    Log Message:
    spamc.js/salib.js: let SpamAssassin see the originating relay IP

    Enable the synthetic Received-header injection (set msg.hello_name from
    the mailproc hello_name global) so spamd can identify the connecting
    client and run sender-IP DNSBLs (Spamhaus, etc.) and SPF -- previously
    every message scored with NO_RELAYS/NO_RECEIVED, neutering those checks.

    Strip the synthetic Received from the re-written message so the stored
    mail doesn't duplicate the Received the mail server adds at delivery
    (the duplicate that caused this to be disabled in f886a41 / only-3-strip). Strip it on its own: SA consumes the injected Return-Path, so a combined
    match never hit.

    Validated live: NO_RELAYS/NO_RECEIVED gone, SPF_PASS + RCVD_IN_* now
    firing, stored messages carry exactly one Received header.

    Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
    (cherry picked from commit 567c45486b2cb958d70a2fabe2bd9f29e2c8ade0)

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Windows@VERT to Git commit to main/sbbs/m on Saturday, August 01, 2026 01:20:00
    https://gitlab.synchro.net/main/sbbs/-/commit/63844fa110647fc2660da61b
    Modified Files:
    docs/v322_new.md exec/load/ircd/core.js unregistered.js
    Log Message:
    ircd: serialize manual CONNECT, and stop answering ERROR with a numeric

    IRCClient_do_connect() declined to start when Outbound_Connect_in_Progress
    was set but never set it itself, so for the duration of an operator's
    /CONNECT the flag stayed clear and a scheduled Automatic_Server_Connect()
    could dial a second server concurrently -- the case 0b4056bbcf (2024-06-19) added the flag to prevent. It also meant every manual /CONNECT logged "Outbound connection while !Outbound_Connect_in_Progress" when the socket callback found the flag already false.

    Unregistered_Commands() had no case for ERROR, so a server refusing our handshake -- which reports why with ERROR just before dropping the link --
    was answered with 461 "You have not registered", and the reason went no
    further than the raw protocol log. Log it and notify routing opers
    instead. Outbound links are named by their configured server name, since
    an unregistered client's hostname defaults to the socket's peer address,
    which is not yet resolvable at that point in the connect callback.

    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 L@VERT to Git commit to main/sbbs/m on Friday, September 18, 2026 23:15:00
    https://gitlab.synchro.net/main/sbbs/-/commit/45e2c6c9e5b64ac90f4b2728
    Modified Files:
    docs/v322_new.md exec/load/sbbsdefs.js src/sbbs3/logon.cpp newuser.cpp sbbsdefs.h src/sbbs3/scfg/scfg.c scfg.h scfgindex.h scfgnode.c scfgxtrn.c src/sbbs3/xtrn_sec.cpp
    Log Message:
    Add a "clear screen before execution" option for external programs

    Some external programs clear or overwrite the user's terminal as soon as
    they start. Run from a logon event, such a program can wipe text the user
    has not yet read. The new XTRN_CLS online program option clears the
    screen before the program is executed, using the BBS's own clear-screen
    (cls), which first prompts the user to continue if there is unread
    output on the screen. GitLab #1243.

    The screen is cleared once the program is sure to run (all early-return
    checks done) and before the drop file is written and the node status,
    start time, inactivity timeout, and MQTT exec event are set, so a long
    pause at the prompt affects none of them.

    Logon and New User fixed events get the same option (FEVENT_CLS), along
    with a pause-after-execution option (FEVENT_PAUSE). A fixed event's
    settings are external() mode bits, so these use bits that external()
    ignores. The other fixed events run offline and don't offer them.

    In SCFG, the online program "Pause After Execution" option becomes
    "Terminal Control" (None, Clear, Pause, or "Clear, Pause"), which prompts
    for both settings; ESC at the first prompt skips the second. The Logon
    and New User fixed event menus offer the same option. toggle_flag() now
    returns the list selection so a caller can detect an abort. The fixed
    event copy of the option is kept out of the search index, which cannot
    tell which fixed events offer it.

    scfgindex.h is regenerated, which also picks up the Msg/File Total
    Interval option added in 875357a01a (2026-08-17).

    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@VERT to Git commit to main/sbbs/m on Sunday, September 20, 2026 12:07:00
    https://gitlab.synchro.net/main/sbbs/-/commit/5de72e09952321c0bfffb38d
    Modified Files:
    docs/v322_new.md exec/load/filecontents_lib.js src/sbbs3/filedat.c ftpsrvr.cpp js_system.cpp mailsrvr.cpp sbbsecho.c services.cpp src/xpdev/genwrap.c genwrap.h
    Log Message:
    Run external commands without flashing a console window on Windows

    C's system() runs its command through cmd.exe, and when the calling
    process has no console of its own -- a Windows service, sbbsctrl.exe, or anything spawned by them -- cmd.exe allocates one, flashing a window onto
    the sysop's desktop. The web server made this obvious: every archive a
    web client peeks into with ?view= runs lsar.

    New in xpdev:

    xp_system() - system(), except that a console-less process spawns
    %COMSPEC% /S /C "..." with CREATE_NO_WINDOW instead. A caller that does
    have a console still goes through system(), so console output is
    unchanged there. Standard handles and handle inheritance are passed
    along as system() does (services.cpp hands a native service its socket
    that way).

    xp_popen() - popen(cmd, "r"), read to EOF, with the captured lines
    appended to a str_list_t. On Windows it builds the pipe with
    CreateProcess() rather than _popen(), which requires the calling process
    to have a console and so has never worked inside a server.

    system.popen() consequently works on Windows now and is no longer
    documented as UNIX-only, and system.exec() no longer flashes a window.
    Given the same treatment: the mail server's external mail processors, the Services' native services, SBBSecho's external programs, and the file-extraction command run for DIZ files. FTP's SITE EXEC loses its
    #ifdef and returns the command's output to the client on Windows too.

    filecontents_lib.js drops the temp-file redirect it was using to keep
    lsar off system.popen() (043587351c9) and captures the output directly.

    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@VERT to Git commit to main/sbbs/m on Sunday, September 20, 2026 12:43:00
    https://gitlab.synchro.net/main/sbbs/-/commit/9e9b6659281839c23872e22c
    Modified Files:
    docs/v322_new.md exec/load/filecontents_lib.js exec/webfileindex.ssjs Log Message:
    View self-extracting .exe archives in the web file index

    libarchive already reads the Zip or 7-Zip payload inside an SFX stub --
    it locates the archive's signature rather than insisting the file start
    with one -- so nothing but an extension gate stood in the way. Verified against both a 7-Zip SFX and an .exe with a Zip appended; a plain
    program still fails to extract and is stored as the negative verdict it
    is.

    webfileindex.ssjs kept its own list of archive extensions, a shorter one
    than the library's, so 'arj', 'zoo', 'tar', 'gz' and 'bz2' were listable
    but never offered. It now asks contents.is_archive(), leaving one list
    to maintain, and 'exe' is added to it.

    extract_external(): an empty lsarContents with a non-zero lsarError is a failure rather than an empty archive. lsar recognizes a self-extracting
    Zip whose stub offsets it won't follow and says exactly that; storing it
    as a zero-entry listing misreported the file (043587351c9).

    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 L@VERT to Git commit to main/sbbs/m on Monday, September 21, 2026 22:21:00
    https://gitlab.synchro.net/main/sbbs/-/commit/c265ffd0d3b4c794510c39b5
    Modified Files:
    docs/v322_new.md exec/load/822header.js newsutil.js src/smblib/smbtxt.c Log Message:
    Don't claim RFC-3676 format=flowed for message text that isn't

    newsutil.js (NNTP) and 822header.js (IMAP) synthesize a Content-Type
    header field for a message that has none stored, and asserted
    format=flowed whenever MSG_FIXED_FORMAT was clear. That flag is only
    ever set by an explicit "fixed" - a MIME format= parameter, a QWK
    "Format = fixed" header, an FTN \1FORMAT: kludge, or a local post made
    in raw-input mode - so a message that simply never said anything was
    reported as flowed.

    An omitted format parameter already means fixed (RFC 3676 4.1), so omit
    it rather than assert a wire format the text does not use: Synchronet
    message text is hard-wrapped and carries no trailing space on a soft
    break, and a conforming flowed reader also space-stuffs each line and
    reflows leading '>' quote blocks. The synthesized field is only
    generated when no Content-Type was stored, so a message that did arrive
    flowed keeps its own header field verbatim and is unaffected.

    Reported on DOVE-Net by ant: messages relayed over QWK with MIME
    excluded were served by NNTP as "charset=IBM437; format=flowed" with no trailing spaces anywhere in the body.

    mime_getpart() extracted the selected part's charset but passed NULL for auxattr, so only a single-part message's format= parameter could reach MSG_FIXED_FORMAT. Thread it through, so that - together with the
    preceding ";format=" typo fix in smb_parse_content_type() - a multi-part message whose text part declares format=fixed now round-trips.

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

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