-
Notifications
You must be signed in to change notification settings - Fork 0
QWK.NET ‐ Validation Modes
QWK.NET provides three validation modes to handle the reality that historical QWK packets may contain malformed data, missing fields, or non-standard extensions. This page explains what problem validation solves, how each mode behaves, and when to use them.
Real-world QWK packets from historical BBS software often deviate from the specification:
- Missing required fields - Some BBS implementations omitted fields or used shorter records
- Format variations - Date formats, delimiters, and field lengths varied between implementations
- Malformed data - Corruption from storage media, incomplete transfers, or software bugs
- Non-standard extensions - Vendor-specific additions that don't match the specification
Without validation modes, the library would either:
- Fail completely on any deviation (too strict for real-world packets)
- Silently accept invalid data (too permissive for quality assurance)
Validation modes provide a balance: detect and report issues whilst allowing processing to continue when appropriate.
| Mode | Behaviour | Default Values | Exception Handling |
|---|---|---|---|
| Strict | Throws immediately on any error | None (fails before defaults) | Throws QwkFormatException
|
| Lenient | Logs warnings, continues parsing | Sensible defaults applied | No exceptions, accumulates warnings |
| Salvage | Best-effort recovery from damage | Aggressive defaults | No exceptions, accumulates warnings |
Purpose: Fail fast when packet correctness is critical.
Behaviour:
- Throws
QwkFormatExceptionimmediately upon encountering any structural error or missing required field - Processing stops at the first error
- No default values are applied
- No partial data is returned
Example:
try
{
using QwkPacket packet = QwkPacket.Open("packet.qwk", ValidationMode.Strict);
// If packet has any errors, execution never reaches here
Console.WriteLine($"BBS: {packet.Control.BbsName}");
}
catch (QwkFormatException ex)
{
// Exception thrown on first error encountered
Console.WriteLine($"Validation failed: {ex.Message}");
}Typical Use Cases:
- Production systems requiring strict specification compliance
- Quality assurance pipelines that must reject invalid packets
- Packet generation tools that need to verify correct output
- Automated systems where any deviation indicates a serious problem
Purpose: Process packets with minor issues whilst recording warnings.
Behaviour:
- Logs warnings for issues but continues parsing
- Applies sensible default values for missing or invalid fields:
- Missing required fields → empty string or appropriate default
- Invalid date formats →
DateTime.MinValue - Invalid numbers →
0 - Empty BBS ID →
"UNKNOWN"
- Accumulates all validation issues in
ValidationReport - Never throws exceptions during parsing
Example:
using QwkPacket packet = QwkPacket.Open("packet.qwk", ValidationMode.Lenient);
// Parsing always succeeds, even with issues
ValidationReport report = packet.ValidationReport;
if (!report.IsValid)
{
// Check what issues were found
foreach (var error in report.Errors)
{
Console.WriteLine($"Error: {error.Message} at {error.Location}");
}
foreach (var warning in report.Warnings)
{
Console.WriteLine($"Warning: {warning.Message} at {warning.Location}");
}
}
// Packet data is still accessible, with defaults applied where needed
Console.WriteLine($"BBS: {packet.Control.BbsName}"); // May be "UNKNOWN" if missingTypical Use Cases:
- General-purpose packet reading applications
- Offline mail readers that should display packets even with minor issues
- Archive browsing tools
- Most production scenarios where partial data is better than no data
- Recommended for most scenarios
Purpose: Extract maximum data from damaged or highly suspect packets.
Behaviour:
- Similar to Lenient mode but with more aggressive recovery strategies
- Makes best-effort assumptions to extract as much data as possible
- May attempt to recover from:
- Truncated records
- Corrupted index files
- Missing structural elements
- Severely malformed headers
- Accumulates all validation issues in
ValidationReport - Never throws exceptions during parsing
Example:
using QwkPacket packet = QwkPacket.Open("damaged-packet.qwk", ValidationMode.Salvage);
ValidationReport report = packet.ValidationReport;
Console.WriteLine($"Recovered {packet.Messages.Count} message(s)");
Console.WriteLine($"Errors: {report.Errors.Count}, Warnings: {report.Warnings.Count}");
// Even with many errors, some data may be recoverable
foreach (Message message in packet.Messages)
{
// Some messages may have incomplete headers or bodies
Console.WriteLine($"{message.From} → {message.To}: {message.Subject}");
}Typical Use Cases:
- Digital archiving projects processing historical packets
- Forensic analysis of corrupted packets
- Recovery tools for damaged archives
- Research projects where partial data is valuable
- Processing packets from unreliable sources
Strict: Throws QwkFormatException immediately
Lenient: Logs warning, uses empty string or appropriate default
Salvage: Logs warning, uses empty string or appropriate default (same as Lenient for this case)
Strict: Throws QwkFormatException immediately
Lenient: Logs warning, sets date to DateTime.MinValue
Salvage: Logs warning, sets date to DateTime.MinValue (same as Lenient for this case)
Strict: Throws QwkFormatException immediately
Lenient: Logs warning, uses 0
Salvage: Logs warning, uses 0 (same as Lenient for this case)
Strict: Throws QwkFormatException when index cannot be parsed
Lenient: Logs warning, may skip index validation or use partial data
Salvage: Logs warning, attempts aggressive recovery (may reconstruct offsets, skip invalid entries)
Strict: Throws QwkFormatException if message structure is invalid
Lenient: Logs warning, returns partial message body if possible
Salvage: Logs warning, attempts to recover partial body, may extract text even from corrupted structure
Use Strict when:
- You need guaranteed specification compliance
- Invalid packets indicate a serious problem that must halt processing
- You're generating packets and want to verify correctness
- Your application cannot handle partial or default data
Use Lenient when:
- You want to process packets with minor issues
- Partial data is acceptable
- You're building general-purpose tools
- This is the recommended default for most scenarios
Use Salvage when:
- Processing archival or damaged packets
- Maximum data recovery is more important than correctness
- You're doing forensic analysis
- Packets are known to be corrupted or incomplete
All modes (except Strict, which throws immediately) accumulate validation issues in a ValidationReport accessible via packet.ValidationReport. The report provides:
-
IsValid- Whether the packet passed all validation checks -
Errors- List of error-level issues -
Warnings- List of warning-level issues -
Infos- List of informational messages -
ToHumanReadableString()- Formatted report output -
ToJson()- JSON export for automated processing
- API Overview - High-level API map and typical workflows
- Library README - Usage guide and key concepts