Move our tools to tools/ no longer need ansitex.src
This commit is contained in:
243
tools/export.js
Normal file
243
tools/export.js
Normal 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
29
tools/frame_load.js
Normal 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
121
tools/frames_check.js
Normal 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
19
tools/frames_list.js
Normal 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
20
tools/frames_tag.js
Normal 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
4
tools/msgbases_list.js
Normal 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
119
tools/save.js
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user