Move our tools to tools/ no longer need ansitex.src

This commit is contained in:
2023-12-27 22:39:26 +11:00
parent d9b056c5cd
commit 00a1ad795d
9 changed files with 34 additions and 2 deletions

243
tools/export.js Normal file
View File

@@ -0,0 +1,243 @@
load('smbdefs.js');
// ANSItex specific includes
load('ansitex/load/defs.js');
load('ansitex/load/funcs.js');
/**
* Work out which key should be used for a page, or validate if a key used is valid
*
* @param page
* @param signed
* @returns {*}
*/
function getKey(page,signed) {
log(LOG_DEBUG,'+ CHECKING key:'+signed+' for page: '+page);
var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini'));
var match = '';
if (f.open("r")) {
var BreakException = {};
// Default Key
var key = f.iniGetValue('prefix','key');
try {
f.iniGetSections("prefix:").forEach(function (prefix) {
var p = prefix.substr(7);
var pk = f.iniGetValue(prefix, 'Key', '');
var re = new RegExp('^' + p, 'g');
// If it was signed, is the key a value one
if (signed && signed.length) {
if (pk === signed) {
//print('SIGNED key is valid:' + signed);
key = signed;
throw BreakException;
}
// Which key should sign this page
} else if (page.toString().match(re) && (p.length > match.length)) {
match = p;
key = pk;
}
});
} catch (e) {
if (e !== BreakException) throw e;
}
}
f.close();
log(LOG_DEBUG,'- key:'+key);
return key;
}
/**
* Export message from message base
*
* @param msgbase
*/
function msgBaseExport(msgbase) {
var ini = new File(msgbase.file+'.ini');
var i=0;
if (ini.open("r")) {
export_ptr=ini.iniGetValue('videotex','export_ptr',0);
ini.close();
} else {
log(LOG_ERROR,'! ERROR: Unable to open INI for ['+msgbase.file+']');
}
// If pointer doesnt exist, reset it from the message base.
if (export_ptr === undefined) {
var f = new File(file_getcase(msgbase.file+'.sbl'));
if (f.open('rb')) {
export_ptr = f.readBin(4);
f.close();
}
}
highest = export_ptr;
var total_msgs = msgbase.total_msgs;
log(LOG_DEBUG,'| msgBaseExport: export_ptr='+export_ptr+'|last_msg='+msgbase.last_msg+'|total_msgs='+total_msgs);
if (msgbase.last_msg >= export_ptr)
i = total_msgs-(msgbase.last_msg-export_ptr);
for (; i<total_msgs; i++) {
if (js.terminated)
break;
var idx = msgbase.get_msg_index(true,i);
if (! idx) {
log(LOG_ERROR,'! ERROR: Reading index of msg offset ['+i+'] : ['+msgbase.error+']');
continue;
}
// Skip message until we get to our last one read.
if (idx.number <= export_ptr)
continue;
if (idx.number > highest)
highest = idx.number;
// Only check messages from Videotex
if (idx.from !== crc16_calc('videotex')) {
log(LOG_DEBUG,'! IGNORING: Message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
// Get the message header
var hdr = msgbase.get_msg_header(true,i);
// Ignore deleted messages
if (hdr.attr&MSG_DELETE) {
log(LOG_DEBUG,'! IGNORING: Deleted message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
// Ignore locally posted messages
if (! hdr.from_net_type) {
log(LOG_DEBUG,'! IGNORING: Local message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
log(LOG_DEBUG,'+ PROCESSING: Message offset ['+i+'] in ['+msgbase.file+'] from ['+hdr.from_net_addr+'] ('+hdr.subject+')');
var body = msgbase.get_msg_body(
/* by_offset: */true,
i,
/* strip Ctrl-A */true,
/* rfc822-encoded: */false,
/* include tails: */false);
t = new File(system.temp_dir+'videotex.gpg');
if (! t.open('w+')) {
log(LOG_ERROR,'! ERROR: Unable to open temp file ['+system.temp_dir+'videotex.gpg'+']');
exit(1);
}
try {
t.write(base64_decode(body));
t.close();
} catch (error) {
log(LOG_ERROR,error);
exit(1);
}
// Check if the signature is good
result = system.exec('gpgv --keyring '+system.mods_dir+'ansitex/keys/pubring.kbx '+system.temp_dir+'videotex.gpg');
if (result !== 0 ) {
log(LOG_ERROR,'! ERROR: Invalid Signature for message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
if (file_exists(system.temp_dir+'videotex.tex'))
file_remove(system.temp_dir+'videotex.tex')
// Check that the signature is allowed to author the frames
result = system.exec('gpg --homedir '+system.mods_dir+'ansitex/keys --batch --status-fd 2 -o '+system.temp_dir+'videotex.tex '+system.temp_dir+'videotex.gpg 2>'+system.temp_dir+'videotex.log');
if (result !== 0 ) {
log(LOG_ERROR,'! ERROR: Failed to extract message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
// Put frame in place.
f = new File(system.temp_dir+'videotex.tex');
if (! f.exists || ! f.open('r')) {
log(LOG_ERROR,'! ERROR: Failed to open frame for message offset ['+i+'] in ['+msgbase.file+']');
exit(1);
}
try {
frame = JSON.parse(f.read());
x = new Frame(0);
frame.render = x.render;
// @todo Figure out how to delete this duplicate code
Object.defineProperty(frame,'page', {
get: function() {return this.frame+this.index}
});
x = null;
} catch (error) {
log(LOG_ERROR,error);
continue;
}
// Get the key that signed the message
f = new File(system.temp_dir+'videotex.log');
if (! f.exists || ! f.open('r')) {
log(LOG_ERROR,'! ERROR: Failed to open gpg log for message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
var signed = '';
while (string = f.readln()) {
var matches = string.match(/\s+GOODSIG\s+.*\<(.*)\>/);
if (matches) {
signed = matches[1];
}
}
f.close();
if (signed !== getKey(frame.frame,signed)) {
log(LOG_ERROR,'! ERROR: Key ['+signed+' is not authorised for message offset ['+i+'] in ['+msgbase.file+']');
continue;
}
// Save the frame.
log(LOG_INFO,'Updating page ['+frame.page+'] from ['+hdr.from_net_addr+'] signed with ['+signed+']');
saveFrame(frame);
}
if (ini.open(file_exists(ini.name) ? 'r+':'w+')) {
ini.iniSetValue('videotex','export_ptr',highest);
ini.close();
} else {
log(LOG_ERROR,'! ERROR: Unable to save to INI for ['+msgbase.file+']');
}
}
var msgbase = new MsgBase(findMsgBase(null).code);
log(LOG_DEBUG,'+ ANSITEX_EXPORT: Open message base in ['+msgbase.file+']');
if (! msgbase.open()) {
log(LOG_ERROR,'! ERROR: Unable to open ['+msgbase.file+']');
exit(-1);
}
msgBaseExport(msgbase);
msgbase.close();

29
tools/frame_load.js Normal file
View File

@@ -0,0 +1,29 @@
load('ansitex/load/funcs.js');
load('ansitex/load/page.js');
/* parse command arguments */
if (argv.length !== 1) {
writeln('! ERROR: Need 1 arguments only');
exit(1);
}
var file = argv.shift();
var ext = file_getext(file).substr(1).toLowerCase();
// Type of frame to load
switch (ext) {
case 'tex':
require('ansitex/load/session/ansitex.js','SESSION_ANSITEX');
break;
case 'vtx':
require('ansitex/load/session/viewdata.js','SESSION_VIEWDATA');
break;
}
var page = new Page(false);
page.import(file);
//page.build();
//page.get(new PageObject(980,'a'));
page.save();

121
tools/frames_check.js Normal file
View File

@@ -0,0 +1,121 @@
/**
* This will go through our videotex and ansitex frames and check for inconsistencies.
*
* We'll check for:
* + Mismatched metadata items
* + Out of sync content
*/
// Load many SBBS definitions
require('sbbsdefs.js','SS_USERON');
// Load text.dat definitions
require('text.js','TOTAL_TEXT');
// Key definitions
require('key_defs.js','KEY_ESC');
ansi = load({},'ansiterm_lib.js');
load('ansitex/load/funcs.js');
// ANSItex specific includes
require('ansitex/load/defs.js','ACTION_EXIT');
require('ansitex/load/frame-ansi.js','FRAME_ANSI');
require('ansitex/load/frame-viewdata.js','FRAME_VIEWDATA');
/* parse command arguments */
if (argv.length !== 1) {
writeln('! ERROR: Need only 1 argument');
exit(1);
}
//const vtx_ext = 'tex';
const vtx_src = 'bin';
const ans_src = 'ans';
const page = argv.shift();
const vtx_srcname = page+'.'+vtx_src;
const ans_srcname = page+'.'+ans_src;
var errors = false;
PAGE_FILE_PREFX = /^[0-9]+[a-z]$/;
if (! PAGE_FILE_PREFX.test(page)) {
writeln('PAGE is not a frame: '+page);
exit(2);
}
writeln('Comparing Frame: '+page);
// Load frame
vtx = new FrameViewdata();
vtx.load(page,'bin');
if (! vtx.content) {
writeln('- ! ERROR: VTX File doesnt exist? :'+page);
errors = true;
} else {
// Check content between TEX/ANS & VTX/BIN
vtx_srcfile = new File(system.mods_dir+'ansitex/text/'+vtx_srcname);
if (! vtx_srcfile.exists || ! vtx_srcfile.open('r')) {
writeln('- ! ERROR: VTX SRC File doesnt exist? :'+vtx_srcname);
errors = true;
} else {
writeln('- LOADING: VTX Source :'+vtx_srcname);
var x = base64_decode(vtx.content);
var y = vtx_srcfile.read();
// Check Content
if (x !== y) {
writeln(' - Page Content :'+md5_calc(x));
writeln(' - Source Content :'+md5_calc(y));
writeln('- ! WARNING: Content Differs.'+base64_decode(vtx.content));
errors = true;
}
}
}
ans = new FrameAnsi();
ans.load(page,'ans');
if (! ans.content) {
writeln('- ! ERROR: ANS File doesnt exist? :'+page);
errors = true;
} else {
// Check content between TEX/ANS & VTX/BIN
ans_srcfile = new File(system.mods_dir+'ansitex/text/'+ans_srcname);
if (! ans_srcfile.exists || ! ans_srcfile.open('r')) {
writeln('- ! ERROR: VTX SRC File doesnt exist? :'+ans_srcname);
errors = true;
} else {
writeln('- LOADING: ANS Source :'+ans_srcname);
var x = base64_decode(ans.content);
var y = ans_srcfile.read();
if (x !== y) {
// Check Content
writeln(' - Page Content :'+md5_calc(x,true));
writeln(' - Source Content :'+md5_calc(y,true));
writeln('- ! WARNING: Content Differs.'+base64_decode(vtx.content));
errors = true;
}
}
}
// Checking keys
if (vtx.content && ans.content)
for each (var k in ['frame','index','key','cost','owner','type','isPublic','isAccessible']) {
if (JSON.stringify(vtx[k]) !== JSON.stringify(ans[k])) {
writeln('- Checking KEY: '+k);
writeln(' - ! VTX: '+vtx[k]);
writeln(' - ! ANS: '+ans[k]);
errors = true;
}
}
if (errors)
exit(1);
else
writeln('= OK');

19
tools/frames_list.js Normal file
View File

@@ -0,0 +1,19 @@
load('load/string.js');
load('load/funcs.js');
load('ansitex/load/msgbases.js');
if (argv.length !== 1) {
writeln('ERROR: Need a msgbase page prefix');
exit(1);
}
ma = new MsgAreas();
area = ma.getArea(argv[0]);
writeln('Opening ['+argv[0]+'] - ('+area.msgbase.cfg.code+')');
writeln('- First:'+area.msgbase.first_msg);
writeln('- Last:'+area.msgbase.last_msg);
for (var x in area.headers) {
writeln(padright(area.headers[x].number,4,' ')+':'+area.headers[x].tags);
}

20
tools/frames_tag.js Normal file
View File

@@ -0,0 +1,20 @@
/**
* Go through our messages and tag a frame id for messages without one.
* @note: May need to run jsexec with -m 32MB to overcome memory issues
*/
load('load/string.js');
load('ansitex/load/msgbases.js');
const ma = new MsgAreas()
for (var i=0;i<ma.areas.length;i++) {
if (argv[0] && (argv[0] !== ma.areas[i].msgbase.cfg.code))
continue;
writeln('Area : '+ma.areas[i].full_name);
writeln('Total Messages : '+ma.areas[i].headers.length);
writeln('- Tagged Messages : '+ma.areas[i].list_tagged.length);
writeln('- Untagged Messages: '+ma.areas[i].list_untagged.length);
ma.areas[i].tag_msgs();
}

4
tools/msgbases_list.js Normal file
View File

@@ -0,0 +1,4 @@
load('ansitex/load/msgbases.js');
var ma = new MsgAreas();
writeln(JSON.stringify(ma.list));

119
tools/save.js Normal file
View File

@@ -0,0 +1,119 @@
// ANSItex specific includes
load('ansitex/load/defs.js');
load('ansitex/load/funcs.js');
var options = loadOptions();
// Modes of operation:
// -s -p x -i y = To send a stored page via the msgbase
// {-s} -p x -i y -f file = To update (and optionally send) a page with new content
// -all details = to create a new page
// Import
send = (argv.indexOf('-s') >= 0);
// Page
p = getArg('-p','No page specified with -p',true);
// Index
i = getArg('-i','No index specified with -i',true);
// File to convert
file = getArg('-f','No file specified with -f',false);
frame = new TexFrame();
frame.load(pageStr({frame: p,index: i}));
if (! send || ! frame || file) {
if (frame.page === null) {
frame = new TexFrame();
frame.frame = p;
frame.index = i;
}
// Key
key = getArg('-k','No index specified with -k',false);
// Cost
cost = getArg('-c','No index specified with -c',false);
// Owner
owner = getArg('-o','No owner specified with -o',false);
// Owner
type = getArg('-t','No type specified with -t',false);
if (file) {
f = new File(file);
if (! f.exists || ! f.open('r')) {
log(LOG_ERROR,'! ERROR: Unable to open ['+file+']');
exit(1);
}
frame.content = base64_encode(f.read());
f.close();
}
if (owner) {
frame.owner = base64_encode(owner.replace(/\\1/g,"\1"));
}
if (key) {
frame.key = key.split(',').map(function(t){return parseInt(t)});
if (frame.key.length !== 10) {
log(LOG_ERROR,'! ERROR: Must specify 10 keys with -k');
exit(1);
}
}
// Public
if (argv.indexOf('-P') >= 0)
frame.isPublic = true;
if (argv.indexOf('-A') >= 0)
frame.isAccessible = true;
if (cost)
frame.cost = cost;
if (type)
frame.type = type; // @todo validate this is a valid type.
// Date
frame.date = new Date().toISOString();
// Final validation
if (! frame.owner) {
log(LOG_ERROR,'! ERROR: No owner specified ['+file+']');
exit(1);
}
// Store the frame in file
frame.save();
}
// @NOTE: We need to use a binary signature then base64 encode it, as mailers may strip 0x0a while messages are in transit.
if (send === 1 && options.gpg_key) {
if (! file) {
file = system.mods_dir+'ansitex/text/'+frame.page+'.tex';
}
if (! file_exists(file)) {
log(LOG_ERROR,'! ERROR: File '+file+' doesnt exist?');
exit(1);
}
if (file_exists(file+'.asc'))
file_remove(file+'.asc')
result = system.exec('gpg --homedir '+system.mods_dir+'ansitex/keys --clearsign --batch --local-user '+options.gpg_key+' -s '+file);
w = new File(file+'.asc');
if (w.open('r')) {
msg = base64_encode(w.read());
} else {
log(LOG_ERROR,'! ERROR: Unable to send with GPG for '+frame.page + ' Error: '+w.error);
exit(1);
}
w.close();
msgBaseImport(null,frame.page,msg);
printf('GPG Result: %s',result);
}