Add CONTROL.DAT parsing

This commit is contained in:
Bryan Ashby 2020-04-21 22:57:06 -06:00
parent 6edfe95dfe
commit d8f0601914
No known key found for this signature in database
GPG Key ID: B49EB437951D2542
1 changed files with 93 additions and 0 deletions

View File

@ -31,12 +31,17 @@ const SMBTZToUTCOffset = (smbTZ) => {
'4294' : '-11:00', // Bering '4294' : '-11:00', // Bering
// US Daylight // US Daylight
// :TODO: FINISH ME!
}[smbTZ]; }[smbTZ];
}; };
const QWKMessageBlockSize = 128; const QWKMessageBlockSize = 128;
// See the following:
// - http://fileformats.archiveteam.org/wiki/QWK
// - http://wiki.synchro.net/ref:qwk
//
const MessageHeaderParser = new Parser() const MessageHeaderParser = new Parser()
.endianess('little') .endianess('little')
.string('status', { .string('status', {
@ -272,6 +277,9 @@ class QWKPacketReader extends EventEmitter {
processPacketFiles(cb) { processPacketFiles(cb) {
async.series( async.series(
[ [
(callback) => {
return this.readControl(callback);
},
(callback) => { (callback) => {
return this.readHeadersExtension(callback); return this.readHeadersExtension(callback);
}, },
@ -285,6 +293,91 @@ class QWKPacketReader extends EventEmitter {
) )
} }
readControl(cb) {
//
// CONTROL.DAT is a CRLF text file containing information about
// the originating BBS, conf number <> name mapping, etc.
//
// References:
// - http://fileformats.archiveteam.org/wiki/QWK
//
if (!this.packetInfo.control) {
return cb(Errors.DoesNotExist('No control file found within QWK packet'));
}
const path = paths.join(this.packetInfo.tempDir, this.packetInfo.control.filename);
// note that we read as UTF-8. Legacy says it should be CP437/ASCII
// but this seems safer for now so conference names and the like
// can be non-English for example.
fs.readFile(path, { encoding : 'utf8' }, (err, controlLines) => {
if (err) {
return cb(err);
}
controlLines = splitTextAtTerms(controlLines);
let state = 'header';
const control = { confMap : {} };
let currConfNumber;
for (let lineNumber = 0; lineNumber < controlLines.length; ++lineNumber) {
const line = controlLines[lineNumber].trim();
switch (lineNumber) {
// first set of lines is header info
case 0 : control.bbsName = line; break;
case 1 : control.bbsLocation = line; break;
case 2 : control.bbsPhone = line; break;
case 3 : control.bbsSysOp = line; break;
case 4 : control.doorRegAndBoardID = line; break;
case 5 : control.packetCreationTime = line; break;
case 6 : control.toUser = line; break;
case 7 : break; // Qmail menu
case 8 : break; // unknown, always 0?
case 9 : break; // total messages in packet (often set to 0)
case 10 :
control.totalMessages = (parseInt(line) + 1);
state = 'confNumber';
break;
default :
switch (state) {
case 'confNumber' :
currConfNumber = parseInt(line);
if (isNaN(currConfNumber)) {
state = 'news';
control.welcomeFile = line;
} else {
state = 'confName';
}
break;
case 'confName' :
control.confMap[currConfNumber] = line;
state = 'confNumber';
break;
case 'news' :
control.newsFile = line;
state = 'logoff';
break;
case 'logoff' :
control.logoffFile = line;
state = 'footer';
break;
case 'footer' :
// some systems append additional info; we don't care.
break;
}
}
}
return cb(null);
});
}
readHeadersExtension(cb) { readHeadersExtension(cb) {
if (!this.packetInfo.headers) { if (!this.packetInfo.headers) {
return cb(null); // nothing to do return cb(null); // nothing to do