-
Notifications
You must be signed in to change notification settings - Fork 0
What's New in 1.6.0
Released 15 March 2026 · NuGet
This release extends the API in three areas: access to non-standard archive files, a complete kludge extraction model, and a richer DOOR.ID capability surface. The diagnostics tool surfaces all three in its output formats.
QWK.NET recognises as standard MESSAGES.DAT, CONTROL.DAT, DOOR.ID, TOREADER.EXT, TODOOR.EXT, WELCOME, NEWS, and GOODBYE; BBS software has historically bundled additional vendor-specific files alongside these. Two new members in QwkPacket expose them.
IReadOnlyList<string> UnknownFiles { get; }Populated during Open(). Contains the name of every archive entry not in the standard set (comparison is case-insensitive). Empty when all files are known.
using QwkPacket packet = QwkPacket.Open("DEMO1.QWK");
foreach (string fileName in packet.UnknownFiles)
{
Console.WriteLine($"Non-standard file: {fileName}");
}Stream? OpenFile(string name)Opens a raw, caller-owned byte stream for any archive entry by name (case-insensitive). Returns null when the file is not present. Works for any archive entry, not only those listed in UnknownFiles.
| Argument | Behaviour |
|---|---|
name is null
|
throws ArgumentNullException
|
| File not found | returns null
|
| File found | returns readable Stream; caller must dispose |
// Read a non-standard file
using Stream? data = packet.OpenFile("BBSINFO.TXT");
if (data != null)
{
using var reader = new StreamReader(data, Encoding.ASCII);
Console.WriteLine(reader.ReadToEnd());
}
// Also works for standard files
using Stream? ctrl = packet.OpenFile("CONTROL.DAT");QWK packets carry machine-readable kludge lines at the top of message bodies, before the human-readable content. Prior to 1.6.0 the library recognised two conventions; 1.6.0 adds the third.
| Convention | Trigger | Key stored |
|---|---|---|
| QWKE extended headers | Key before : is To, From, or Subject (case-insensitive) |
To, From, or Subject
|
| @-kludge | Line begins @identifier:
|
Bare identifier (e.g. MSGID) |
| Ctrl-A kludge (new) | First character is U+0001 (SOH) or U+263A (CP437 glyph for byte 0x01) |
Token before the first space or colon |
Scanning stops at the first blank line or at any line that does not match one of these three forms.
The prefix character is stripped from the stored key in all three cases. A caller checking kludge.Key == "MSGID" finds the entry regardless of whether it arrived as a Ctrl-A kludge, an @-kludge, or a QWKE header. The original line text is preserved in kludge.RawLine for byte-level fidelity.
foreach (Message message in packet.Messages)
{
foreach (var kludge in message.Kludges)
{
// Key never contains a leading @ or SOH character
Console.WriteLine($"{kludge.Key} = {kludge.Value}");
}
}Previously the @ sigil was part of the stored key (e.g. kludge.Key == "@MSGID"). It is now stripped (e.g. kludge.Key == "MSGID").
Migration: remove the leading @ from any string literal used in a kludge key comparison.
// Before 1.6.0
if (kludge.Key == "@MSGID") { ... }
// 1.6.0 and later
if (kludge.Key == "MSGID") { ... }The following CONTROLTYPE values were previously mapped to Unknown. They are now fully enumerated:
| Member | DOOR.ID CONTROLTYPE value |
Meaning |
|---|---|---|
ResetAll |
RESETALL |
Reset all last-read pointers |
Yours |
YOURS |
Retrieve messages addressed to current user |
Mail |
MAIL |
Retrieve personal mail |
DeleteMail |
DELMAIL |
Delete personal mail |
Attach |
ATTACH |
File attachments |
Own |
OWN |
Mark messages as owned |
FileRequest |
FREQ |
FidoNet-style file requests |
Index |
NDX |
NDX index files produced |
TimeZone |
TZ |
Time-zone information in headers |
Via |
VIA |
VIA routing path in headers |
MessageId |
MSGID |
MSGID kludge lines |
Control |
CONTROL |
Extended CONTROL kludge lines |
The full DoorCapability enum now covers 20 values (Unknown maps any value not in the table above).
IReadOnlyList<string> ControlTypes { get; }Raw CONTROLTYPE line values in document order, preserving original casing. Useful when a capability maps to Unknown and you need the original string for round-trip fidelity or logging.
if (packet.DoorId != null)
{
// Typed capabilities
foreach (DoorCapability cap in packet.DoorId.Capabilities)
{
Console.WriteLine(cap); // e.g. Add, Drop, ResetAll
}
// Raw values (includes non-standard ones)
foreach (string raw in packet.DoorId.ControlTypes)
{
Console.WriteLine(raw); // e.g. "ADD", "CUSTOMEXT"
}
}All three output formats reflect the new data.
Text — a Capabilities: line appears in the BBS INFORMATION section when at least one CONTROLTYPE is present:
Door ID: MailDoor 1.50
Door System: PCBoard 15.1
Capabilities: Add, Drop, Reset, ResetAll, Yours, Mail
Markdown — a **Door Capabilities:** bullet after the Door ID line.
JSON — a doorCapabilities string array inside the top-level object (omitted when empty):
"doorCapabilities": ["Add", "Drop", "Reset", "ResetAll", "Yours", "Mail"]Text — an ADDITIONAL FILES: section after OPTIONAL FILES: (omitted when no non-standard files are present):
ADDITIONAL FILES:
- BBSINFO.TXT
- CUSTOM.DAT
Markdown — an ## Additional Files section after ## Optional Files.
JSON — an unknownFiles string array (always present when parse succeeds; empty array when no non-standard files exist):
"unknownFiles": ["BBSINFO.TXT", "CUSTOM.DAT"]-
@-kludge key comparisons — remove the leading@from any string literal compared againstkludge.Key. This is the only breaking change. - No other API signatures changed. New members (
UnknownFiles,OpenFile,ControlTypes) and new enum values are purely additive.