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 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();