From 9c7b8b7920cfb6452f132fdd4ea8fef38d7bf3b2 Mon Sep 17 00:00:00 2001 From: deuce <> Date: Fri, 15 Jan 2016 03:25:20 +0000 Subject: [PATCH] Clean up TODO items up to the actual callout (except for one that will be an issue in 2038). --- exec/binkit.js | 233 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 181 insertions(+), 52 deletions(-) diff --git a/exec/binkit.js b/exec/binkit.js index c8e562169d..d5d833988d 100644 --- a/exec/binkit.js +++ b/exec/binkit.js @@ -1,6 +1,17 @@ /* * Intentionally simple "Advanced BinkleyTerm Style Outbound" * mailer. + * + * Known limitations: + * 1) Does NOT support upper-case filenames in derived files/paths. + * This means that all .flo files must be in lower-case, all hex + * filename/path components must be in lower-case, and the ".pnt" + * directory extension must be in lower case. + * 2) Will not check in a zone-specified directory for the default + * zone. That is, if the default zone is zone 1, and the outbound + * is "/path/to/outbound", it will not correctly handle the case + * where there is a "/path/to/outboud.001" directory. The + * behaviour in this case is undefined and likely to be bad. * * See FTS-5005 for details. */ @@ -8,22 +19,6 @@ load("binkp.js"); load("fidocfg.js"); -function addr_from_path(path) -{ - // Convert a path into a 4D fidonet address - // TODO: 5D based on root outbound name. - -} - -function insense(str) -{ - if (system.platform !== 'Win32') - return str.replace(/[a-zA-Z]/g, function(c) { - return '['+c.toUpperCase()+c.toLowerCase()+']'; - }); - return str; -} - function lock_flow(file, csy) { var ret = { @@ -31,15 +26,51 @@ function lock_flow(file, csy) cst:new File(file.replace(/\.*?$/, '.csy')) }; + // Takes ownership of a lockfile if it's more than six hours old. + /* + * Race-safe version... + * 1) If date is "too old", pick a random time in the future. + * 2) Set the file date to that random time. + * 3) Wait one second. + * 4) If the file date is the random time you chose, set the date + * to now and take "ownership" of the file. + */ + function take_lockfile(f) + { + var orig_date; + var future; + var remain; + var now = time(); + + // TODO: This is hacked for a signed 32-bit time_t... watch out in 2038! + orig_date = f.date; + if (orig_date > (now - 60*60*6)) + return false; + remain = 0x80000000 - now; + future = now + random(remain); + f.date = future; + mswait(1000); + if (f.date != future) + return false; + if (!f.open("wb")) { + f.date = orig_date; + return false; + } + f.date = now; + return true; + } + if (!ret.bsy.open("web")) { - // TODO: The suggested check if it's stale introduces a race condition. - return undefined; + if (!take_lockfile(ret.bsy)) + return undefined; } if (csy) { if (!ret.csy.open("web")) { - bsy.close(); - bsy.remove(); - return undefined; + if (!take_lockfile(ret.csy)) { + ret.bsy.close(); + ret.bsy.remove(); + return undefined; + } } } ret.bsy.writeln("BinkIT"); @@ -60,12 +91,23 @@ function unlock_flow(locks) } } -function callout_rx_callback(fname) +function callout_auth_cb(mode, bp) +{ + /* + * TODO: Add all outgoing mail for all authenticated addresses. + * This is tricky since we need to only use addresses that are + * configured with the single password we sent. The multiple + * password extension (FRL-1013) was withdrawn, so we can't do + * that. + */ +} + +function callout_rx_callback(fname, bp) { // TODO: Handle received files. } -function callout_want_callback(fobj, fsize, fdate, offset) +function callout_want_callback(fobj, fsize, fdate, offset, bp) { /* * TODO: Currently a copy/paste from binkp.js... @@ -85,7 +127,7 @@ function callout_want_callback(fobj, fsize, fdate, offset) return this.file.ACCEPT; } -function callout(file, scfg) +function callout(addr, scfg) { /* * TODO: We can force 5D stuff here by adding a "fidonet" argument @@ -94,10 +136,11 @@ function callout(file, scfg) var myaddr = FIDO.parse_addr(system.fido_addr_list[0], 1); var bp = new BinkP('BinkIT/'+("$Revision$".split(' ')[1]), undefined, callout_rx_callback); + log(LOG_INFO, "Callout to "+addr+" started."); // Force debug mode for now... bp.debug = true; bp.default_zone = myaddr.zone; - bp.default_domain = maddr.domain; + bp.default_domain = myaddr.domain; bp.want_callback = callout_want_callback; bp.rx_callback = callout_rx_callback; @@ -111,41 +154,121 @@ function callout(file, scfg) function run_one_outbound_dir(dir, scfg) { + var myaddr = FIDO.parse_addr(system.fido_addr_list[0], 1); var flow_files; var lock_files; + var addr; + var ext; var i; log(LOG_DEBUG, "Running outbound dir "+dir); - // First, look for any node with pending netmail and handle that node. - while (!js.terminated) { - flow_files = directory(dir+insense('*.?ut')); - if (flow_files.length == 0) - break; - for (i=0; i<flow_files.length; i++) { - // Find one we can create a .bsy and .csy file for... - if (file_getext(flow_files[i]).substr(0,2).search(/^\.[icdo]$/i) !== 0) - continue; - if ((lock_files = lock_flow(flow_files[i], true))!==undefined) { - // TODO: Check hold file. - break; - } + function check_held(addr) + { + var until; + var f = new File(scfg.outboud.replace(/[\/\\]$/,'')+addr.flo_outbound(myaddr.zone)+'.hld'); + + if (!f.exists) + return false; + if (!f.open("rb")) { + log(LOG_ERROR, "Unable to open hold file '"+f.name+"'"); + return true; } - if (i<flow_files.length) { - log(LOG_DEBUG, "Attempting callout for file "+flow_files[i]); - // Use a try/catch to ensure we clean up the lock files. - try { - callout(flow_files[i], scfg); + until = f.readln(); + if (until.search(/^[0-9]+$/) !== 0) { + log(LOG_WARNING, "First line of '"+f.name+"' invalid ("+until+"). Should be a positive integer."); + return false; + } + f.close(); + until = parseInt(until, 10); + if (until < time()) { + f.remove(); + log(LOG_INFO, "Removed stale ("+system.timestr(until)+") hold file '"+f.name+"'."); + return false; + } + log(LOG_INFO, addr+" held until "+system.timestr(until)+"."); + return true; + } + + function check_flavour(wildcard, typename) + { + while (!js.terminated) { + flow_files = directory(dir+wildcard); + if (flow_files.length == 0) + break; + flow_file_loop: + for (i=0; i<flow_files.length; i++) { + try { + addr = FIDO.parse_flo_file_path(flow_files[i], myaddr.zone); + } + catch(addr_e) { + log(LOG_WARNING, addr_e+" when checking '"+flow_files[i]+"' (default zone: "+myaddr.zone+")"); + continue; + } + ext = file_getext(flow_files[i]); + + // Ensure this is the "right" outbound (file case, etc) + if (flow_files[i] !== scfg.outboud.replace(/[\\\/]$/,'')+addr.flo_outbound+ext) { + log(LOG_WARNING, "Unexpected file path '"+flow_files[i]+"' (skipped)"); + continue; + } + + switch(ext.substr(0, 2)) { + case '.h': + log(LOG_DEBUG, "Skipping hold flavourd flow file '"+flow_files[i]+"'."); + continue; + case '.c': + case '.d': + case '.i': + break; + case '.f': + if (wildcard === '*.?lf') + break; + continue; + case '.o': + if (wildcard === '*.?ut') + break; + continue; + default: + log(LOG_WARNING, "Unknown flow file flavour '"+flow_files[i]+"'."); + continue; + } + + if ((lock_files = lock_flow(flow_files[i], true))!==undefined) { + if (check_held(addr)) { + unlock_flow(lock_files); + continue; + } + break; + } } - catch(e) { + if (i<flow_files.length) { + log(LOG_DEBUG, "Attempting callout for file "+flow_files[i]); + // Use a try/catch to ensure we clean up the lock files. + try { + callout(flow_files[i], scfg); + } + catch(callout_e) { + log(LOG_DEBUG, "Unlocking after exception in callout."); + unlock_flow(lock_files); + throw(callout_e); + } unlock_flow(lock_files); - throw(e); } - unlock_flow(lock_files); + else { + log(LOG_DEBUG, "No "+typename+" typed flow files to be processed."); + break; + } } - else - break; } + + // First, look for any node with pending netmail and handle that node. + check_flavour('*.?ut', "netmail"); + log(LOG_DEBUG, "Done checking netmail in "+dir+", checking file references."); + + // Now check for pending pending file reference + check_flavour('*.?lo', "file reference"); + log(LOG_DEBUG, "Done checking file references in "+dir+", checking file references."); } function run_outbound() @@ -155,7 +278,7 @@ function run_outbound() var outbound_dirs=[]; var tmp; - log(LOG_DEBUG, "Running outbound"); + log(LOG_INFO, "Running outbound"); scfg = new SBBSEchoCfg(); if (!scfg.is_flo) { @@ -169,16 +292,22 @@ function run_outbound() tmp.forEach(function(dir) { var pnts; - if (file_getext(dir).search(/^\.[0-9a-fA-F]+$/) == 0) { + if (file_getext(dir).search(/^\.[0-9a-f]+$/) == 0) { if (file_isdir(dir)) { outbound_dirs.push(backslash(dir)); - pnts = directory(backslash(dir)+insense(".pnt"), false); + pnts = directory(backslash(dir)+'.pnt', false); pnts.forEach(function(pdir) { - if (pdir.search(/[0-9a-zA-Z]{8}.[Pp][Nn][Tt]$/) >= 0 && file_isdir(pdir)) + if (pdir.search(/[\\\/][0-9a-z]{8}.pnt$/) >= 0 && file_isdir(pdir)) outbound_dirs.push(backslash(pdir)); + else + log(LOG_WARNING, "Unhandled/Unexpected point path '"+pdir+"'."); }); } + else + log(LOG_WARNING, "Unexpected file in outbound '"+dir+"'."); } + else + log(LOG_WARNING, "Unhandled outbound '"+dir+"'."); }); outbound_dirs.forEach(function(dir) { run_one_outbound_dir(dir, scfg); -- GitLab