diff --git a/ctrl/text.dat b/ctrl/text.dat index 6c14523244d973dca5ed13a31a59773bddd82154..5699f8a9aea973aeaee6a35b24426ebf12610fda 100644 --- a/ctrl/text.dat +++ b/ctrl/text.dat @@ -50,9 +50,9 @@ "\1[\1n\1mPosted on \1h%s\1n\1m %s.\r\n" 037 Posted "\7\1_\1w\1hNode %2d: \1g%s\1n\1g sent you E-mail.\r\n" 038 EmailNodeMsg "\1n\r\nYou can't forward mail.\r\n" 039 R_Forward -"\1n\1m\r\nForwarded by \1h%s\1n\1m on "\ 040 ForwardedFrom +"\1n\1mForwarded by \1h%s\1n\1m on "\ 040 ForwardedFrom "\1h%s\1n\r\n" -"\1n\1m\r\nMail forwarded to \1h%s \1n\1m#%d.\1n\r\n" 041 Forwarded +"\1n\1m\r\nMail forwarded to \1h%s\1n\r\n" 041 Forwarded "\1b\1hAuto message by: \1c%s\1b on %s\1n\r\n\r\n" 042 AutoMsgBy "\r\nAuto Message - ~Read, ~Write, or ~Quit: " 043 AutoMsg "\1n\r\nYou can't write to the auto-message.\r\n" 044 R_AutoMsg diff --git a/exec/emailval.js b/exec/emailval.js index d04927377864fcabc8ef6623b282ac04d2023e73..b9f368bf6b629bd1188e75ddca86839cf0406ea5 100644 --- a/exec/emailval.js +++ b/exec/emailval.js @@ -1,4 +1,4 @@ -// $Id: emailval.js,v 1.7 2019/07/15 04:41:35 rswindell Exp $ + /******************************************************************************* Originally based on: FILE: emailval.js v0.2 @@ -21,7 +21,7 @@ STEP 2: Edit ctrl/modopts.ini in a text editor and edit the following values in the [emailval] section (create if it necessary) to match your pre-validation and post-validation security levels. - level_before_validation (default: 50) + level_before_validation (default: 50) level_after_validation (default: 60) flags1_after_validation (default: no change) flags2_after_validation (default: no change) @@ -31,6 +31,8 @@ post-validation security levels. restrictions_after_validation (default: no change) expiration_after_validation (default: false) expiration_days_after_validation (default: no change) + valid_chars=ACDEFHJKLMNPQRTUVWXY23456789!@#$%&* + code_length=16 Note: the flags, exemptions, and restrictions .ini values support 'A' through 'Z' with the optional '+' (add) and '-' (remove) modifiers. @@ -65,9 +67,10 @@ if(options.level_after_validation === undefined) options.level_after_validation = 60; //other constants, shouldn't need changing. -var cValChars='ACDEFHJKLMNPQRTUVWXY23456789!@#$%&*'; +var cValChars = options.valid_chars !== undefined ? options.valid_chars : + 'ACDEFHJKLMNPQRTUVWXY23456789!@#$%&*'; var cPrevalText = "telvalcode"; -var cValCodeLen = 16; +var cValCodeLen = options.code_length !== undefined ? options.code_length : 16; //include SBBS Definition constants require("sbbsdefs.js", 'NET_NONE'); @@ -214,4 +217,4 @@ function CheckValidation() { } } } -CheckValidation(); \ No newline at end of file +CheckValidation(); diff --git a/exec/init-fidonet.ini b/exec/init-fidonet.ini index 0591376899a7cad042950b37ba3d27a2aaaca894..7c48a208e9915cb6c959f9491de9df5cb2c9b716 100644 --- a/exec/init-fidonet.ini +++ b/exec/init-fidonet.ini @@ -98,6 +98,16 @@ fido = 1:19/37 host = hub.cybernetbbs.net areatag_prefix = CN_ +[zone:42] +name = SFNet +desc = Science Fiction +pack = http://furmenservices.net/sfnet.zip +coord = Dallas Vinson +addr = 42:1/1 +host = furmenservices.net +echolist = sfnet.na +areatag_prefix = SF_ + [zone:44] name = DoRENET desc = BBS modifications, coding, ansi/asci etc diff --git a/exec/install-xtrn.js b/exec/install-xtrn.js index ded2392bca380079860efa02adae69c673d2c8a6..7b5deb824466c7689cc2ed475944197be6903da2 100644 --- a/exec/install-xtrn.js +++ b/exec/install-xtrn.js @@ -25,6 +25,12 @@ // // The .ini sections and keys supported (zero or more of each may be included): // +// [pre-exec:<file>.js [args]] ; execute file.js before installing programs +// startup_dir = directory to make current before execution +// +// [pre-eval:<js-expression>] ; evaluate js-expression before installing progs +// cmd = evaluate this string rather than the js-expression +// // [prog:<code>] // name = program name or description (40 chars max) // cats = additional target installation categories (sections) @@ -61,10 +67,10 @@ // [service:<protocol>] // see ctrl/services.ini // -// [exec:<file>.js [args]] ; execute file.js with these arguments +// [exec:<file>.js [args]] ; execute file.js with these arguments - after // startup_dir = directory to make current before execution // -// [eval:<js-expression>] +// [eval:<js-expression>] ; evaluate js-expression after installing programs // cmd = evaluate this string rather than the js-expression // // [ini:<filename.ini>[:section]] @@ -75,8 +81,9 @@ // Additionally, each section can have the following optional keys that are // only used by this script (i.e. not written to any configuration files): // note = note to sysop displayed before installation +// fail = note to sysop displayed upon failure // prompt = confirmation prompt (or false if no prompting) -// required = if true, this item must be installed to continue +// required = if true, this item must be successful to continue // last = if true, this item will be the last of its type // done = if true, no more installer items will be processed // @@ -91,7 +98,7 @@ "use strict"; -const REVISION = "3.18b"; +const REVISION = "3.18c"; const ini_fname = "install-xtrn.ini"; load("sbbsdefs.js"); @@ -210,6 +217,76 @@ function install_xtrn_item(cnf, type, name, desc, item, cats) return true; } +function install_exec_cmd(ini_file, section, startup_dir) +{ + var list = ini_file.iniGetAllObjects("cmd", section); + for (var i = 0; i < list.length; i++) { + var item = list[i]; + var js_args = item.cmd.split(/\s+/); + var js_file = js_args.shift(); + + if (file_getext(js_file).toLowerCase() != ".js") + return "Only '.js' files may be executed: " + js_file; + + if (item.note) + print(item.note); + + var prompt = "Execute: " + item.cmd; + if (item.prompt !== undefined) + prompt = item.prompt; + + if (prompt && !confirm(prompt)) { + if (item.required == true) + return prompt + " is required to continue"; + continue; + } + + if (item.startup_dir === undefined) + item.startup_dir = startup_dir; + if (!item.args) + item.args = ""; + var result = js.exec.apply(null + ,[js_file, item.startup_dir, {}].concat(js_args)); + if (result !== 0 && item.required) + return item.fail || ("Error " + result + " executing " + item.cmd); + if (item.last === true) + break; + if (item.done) + return false; + } + return true; +} + +function install_eval_cmd(ini_file, section, startup_dir) +{ + var list = ini_file.iniGetAllObjects("str", section); + for (var i = 0; i < list.length; i++) { + var item = list[i]; + if (!item.cmd) + item.cmd = item.str; // the str can't contain [], so allow cmd to override + var prompt = "Evaluate: " + item.cmd; + if (item.prompt !== undefined) + prompt = item.prompt; + if (prompt && !confirm(prompt)) { + if (item.required == true) + return prompt + " is required to continue"; + continue; + } + try { + var result = eval(item.cmd); + } catch(e) { + return e; + } + if (!result && item.required) + return item.fail || ("Truthful evaluation of '" + item.cmd + "' is required to continue"); + if (item.last === true) + break; + if (item.done) + return false; + } + return true; +} + function install(ini_fname) { ini_fname = fullpath(ini_fname); @@ -254,6 +331,14 @@ function install(ini_fname) var startup_dir = ini_fname.substr(0, Math.max(ini_fname.lastIndexOf("/"), ini_fname.lastIndexOf("\\"), 0)); startup_dir = relpath.get(system.ctrl_dir, startup_dir); + var result = install_exec_cmd(ini_file, "pre-exec:", startup_dir); + if(result !== true) + return result; + + result = install_eval_cmd(ini_file, "pre-eval:", startup_dir); + if(result !== true) + return result; + const types = { prog: { desc: "External Program", struct: "xtrn" }, event: { desc: "External Timed Event", struct: "event" }, @@ -276,7 +361,7 @@ function install(ini_fname) return false; if(item.last === true) break; - done = item.done; + done = Boolean(item.done); } } @@ -318,7 +403,7 @@ function install(ini_fname) return false; if(item.last === true) break; - done = item.done; + done = Boolean(item.done); } var services_ini = new File(file_cfgname(system.ctrl_dir, "services.ini")); @@ -358,69 +443,21 @@ function install(ini_fname) return false; if(item.last === true) break; - done = item.done; + done = Boolean(item.done); } - var list = ini_file.iniGetAllObjects("cmd", "exec:"); - for (var i = 0; i < list.length && !done; i++) { - var item = list[i]; - var js_args = item.cmd.split(/\s+/); - var js_file = js_args.shift(); - - if (file_getext(js_file).toLowerCase() != ".js") - return "Only '.js' files may be executed: " + js_file; - - if (item.note) - print(item.note); - - var prompt = "Execute: " + item.cmd; - if (item.prompt !== undefined) - prompt = item.prompt; - - if (prompt && !confirm(prompt)) { - if (item.required == true) - return prompt + " is required to continue"; - continue; - } - - if (item.startup_dir === undefined) - item.startup_dir = startup_dir; - if (!item.args) - item.args = ""; - var result = js.exec.apply(null - ,[js_file, item.startup_dir, {}].concat(js_args)); - if (result !== 0 && item.required) - return "Error " + result + " executing " + item.cmd; - if(item.last === true) - return true; - done = item.done; + if(done === false) { + result = install_exec_cmd(ini_file, "exec:", startup_dir); + if(typeof result !== 'boolean') + return result; + done = !result; } - - var list = ini_file.iniGetAllObjects("str", "eval:"); - for (var i = 0; i < list.length && !done; i++) { - var item = list[i]; - if (!item.cmd) - item.cmd = item.str; // the str can't contain [], so allow cmd to override - var prompt = "Evaluate: " + item.cmd; - if (item.prompt !== undefined) - prompt = item.prompt; - if (prompt && !confirm(prompt)) { - if (item.required == true) - return prompt + " is required to continue"; - continue; - } - try { - var result = eval(item.cmd); - } catch(e) { - return e; - } - if (!result) { - if (item.required == true) - return "Truthful evaluation of '" + item.cmd + "' is required to continue"; - } - if(item.last === true) - return true; - done = item.done; + + if(done === false) { + result = install_eval_cmd(ini_file, "eval:", startup_dir); + if(typeof result !== 'boolean') + return result; + done = !result; } if (installed) { diff --git a/exec/load/binkp.js b/exec/load/binkp.js index 3cf4df88ce5978bfaa4451c2399ea03dfd1487ce..e2a05882cd7353f0e4b6e117343c0da9bc662d19 100644 --- a/exec/load/binkp.js +++ b/exec/load/binkp.js @@ -1,4 +1,4 @@ -const binkp_revision = 3; +const binkp_revision = 4; require('sockdefs.js', 'SOCK_STREAM'); require('fido.js', 'FIDO'); @@ -940,15 +940,18 @@ BinkP.prototype.close = function() this.sendCmd(this.command.M_EOB); } // Attempt a super-duper graceful shutdown to prevent RST... - this.sock.is_writeable = false; - remain = this.timeout; - end = time() + remain; - do { - if (this.sock.recv(2048, remain) == 0) - break; - remain = end - time(); - } while (remain > 0); - this.sock.close(); + if (this.sock !== undefined) { + this.sock.is_writeable = false; + remain = this.timeout; + end = time() + remain; + do { + if (this.sock.recv(2048, remain) == 0) + break; + remain = end - time(); + } while (remain > 0); + this.sock.close(); + this.sock = undefined; + } } this.tx_queue.forEach(function(file) { file.file.close(); @@ -1237,7 +1240,7 @@ BinkP.prototype.recvFrame = function(timeout) log(LOG_WARNING, 'Peer ended their VER with " '+m[2]+'" instead of the required " binkp/1.1", but we\'re assuming binkp 1.1 anyway'); } log(LOG_DEBUG, "Parsed BinkP version: " + binkp_ver); - this.ver_1_1 = binkp_ver >= 1.1; + this.ver1_1 = binkp_ver >= 1.1; } break; case 'ZYZ': diff --git a/exec/load/json-client.js b/exec/load/json-client.js index a4341ba2c286efbbec4d652d73f26f32dd3cdb58..2552a4411c4db2d6de5cdb1ee1f2a6e3d040a704 100644 --- a/exec/load/json-client.js +++ b/exec/load/json-client.js @@ -17,8 +17,9 @@ load("json-sock.js"); - JSONClient.read(scope,location,lock); - JSONClient.pop(scope,location,lock); - JSONClient.shift(scope,location,lock); - - JSONClient.write(scope,location,lock); - - JSONClient.push(scope,location,lock); + - JSONClient.write(scope,location,data,lock); + - JSONClient.push(scope,location,data,lock); + - JSONClient.remove(scope,location,lock); - JSONClient.unshift(scope,location,lock); - JSONClient.splice(scope,location,start,end,data,lock) - JSONClient.slice(scope,location,start,end,lock) diff --git a/exec/load/xtrnmenulib.js b/exec/load/xtrnmenulib.js new file mode 100644 index 0000000000000000000000000000000000000000..e7cff9fab33e45554f2dd4c01a553cac101bcc5d --- /dev/null +++ b/exec/load/xtrnmenulib.js @@ -0,0 +1,410 @@ +/** + * Custom External Program Menu Library for Custom External Program Menus + * by Michael Long mlong innerrealmbbs.us + * + * This provides common functionality for retrieving menus used by + * the loadable module xtrnmenu.js and by the web interface + * 099-xtrnmenu-games.xjs + */ + +"use strict"; + +load("sbbsdefs.js", "K_NONE"); + +/* text.dat entries */ +require("text.js", "XtrnProgLstFmt"); + +function ExternalMenus() { + this.options = {}; + this.xtrn_custommenu_options = {}; + this.menuconfig = {}; + + this.getOptions(); + this.getMenuConfig(); +} + +ExternalMenus.prototype.getMenuConfig = function() { + var config_file = new File(system.ctrl_dir + "xtrnmenu.cfg"); + var config_src; + this.menuconfig = undefined; + if (config_file.open('r+')) { + config_src = config_file.read(); + config_file.close(); + } + + if (typeof config_src !== "undefined") { + this.menuconfig = JSON.parse(config_src.toString()); + } +} + +ExternalMenus.prototype.getOptions = function(menutype, menuid) { + if (typeof menutype === "undefined") { + menutype = 'custommenu'; + } + + // Get xtrn_sec options from modopts.ini [xtrn_sec] + if ((this.options = load({}, "modopts.js", "xtrn_sec")) == null) { + this.options = { multicolumn: true, sort: false }; + } + + // Get xtrn_custommenu options from modopts.ini [xtrn_custommenu] + if ((this.xtrn_custommenu_options = load({}, "modopts.js", "xtrnmenu")) == null) { + this.xtrn_custommenu_options = { }; + } + + // in all cases, we start with the xtrn_sec options as the base and set the defaults + if (this.options.multicolumn === undefined) + this.options.multicolumn = true; + + if (this.options.multicolumn_separator === undefined) + this.options.multicolumn_separator = " "; + + if (this.options.multicolumn_fmt === undefined) + this.options.multicolumn_fmt = system.text(XtrnProgLstFmt); + + if (this.options.singlecolumn_fmt === undefined) + this.options.singlecolumn_fmt = "\x01h\x01c%3u \xb3 \x01n\x01c%s\x01h "; + + if (this.options.singlecolumn_margin == undefined) + this.options.singlecolumn_margin = 7; + + if (typeof bbs !== "undefined") { + if (this.options.singlecolumn_height == undefined) + this.options.singlecolumn_height = console.screen_rows - this.options.singlecolumn_margin; + + // override and turn off multicolumn if terminal width is less than 80 + if (console.screen_columns < 80) + options.multicolumn = false; + } + + if (this.options.restricted_user_msg === undefined) + this.options.restricted_user_msg = system.text(R_ExternalPrograms); + + if (this.options.no_programs_msg === undefined) + this.options.no_programs_msg = system.text(NoXtrnPrograms); + + if (this.options.header_fmt === undefined) + this.options.header_fmt = system.text(XtrnProgLstHdr); + + if (this.options.titles === undefined) + this.options.titles = system.text(XtrnProgLstTitles); + + if (this.options.underline === undefined) + this.options.underline = system.text(XtrnProgLstUnderline); + + if (this.options.which === undefined) + this.options.which = system.text(WhichXtrnProg); + + if (this.options.clear_screen === undefined) + this.options.clear_screen = true; + + if (this.options.section_fmt === undefined) + this.options.section_fmt = "\x01y\x01g%3d:\x01n\x01g %s" + + if (this.options.section_header_fmt === undefined) + this.options.section_header_fmt = "\x01-\x01gSelect \x01hExternal Program Section\x01-\x01g:" + + if (this.options.section_which === undefined) + this.options.section_which = "\r\n\x01-\x01gWhich, \x01w\x01h~Q\x01n\x01guit or [1]: \x01h" + + // if its a custom menu, then override with custommenu_options + if (menutype == 'custommenu') { + if (typeof this.xtrn_custommenu_options.multicolumn_fmt !== "undefined") { + this.options.multicolumn_fmt = this.xtrn_custommenu_options.multicolumn_fmt; + } else { + // cannot default to xtrn_sec multicolumn_fmt due to use of %u instead of %s + this.options.multicolumn_fmt = "\x01h\x01c%3s \xb3 \x01n\x01c%-32.32s\x01h "; + } + + if (typeof this.xtrn_custommenu_options.singlecolumn_fmt !== "undefined") { + this.options.singlecolumn_fmt = this.xtrn_custommenu_options.singlecolumn_fmt; + } else { + // cannot default to xtrn_sec multicolumn_fmt due to use of %u instead of %s + this.options.singlecolumn_fmt = "\x01h\x01c%3s \xb3 \x01n\x01c%s\x01h "; + } + + this.options.header_fmt = (typeof this.xtrn_custommenu_options.header_fmt !== "undefined") + ? this.xtrn_custommenu_options.header_fmt : this.options.header_fmt; + + this.options.titles = (typeof this.xtrn_custommenu_options.titles !== "undefined") + ? this.xtrn_custommenu_options.titles : this.options.titles; + + this.options.which = (typeof this.xtrn_custommenu_options.which !== "undefined") + ? this.xtrn_custommenu_options.which : this.options.which; + + this.options.underline = (typeof this.xtrn_custommenu_options.underline !== "undefined") + ? this.xtrn_custommenu_options.underline : this.options.underline; + + this.options.multicolumn_separator = (typeof this.xtrn_custommenu_options.multicolumn_separator !== "undefined") + ? this.xtrn_custommenu_options.multicolumn_separator : this.options.multicolumn_separator; + + this.options.multicolumn = (typeof this.xtrn_custommenu_options.multicolumn !== "undefined") + ? this.xtrn_custommenu_options.multicolumn : this.options.multicolumn; + + this.options.sort = (typeof this.xtrn_custommenu_options.sort !== "undefined") + ? this.xtrn_custommenu_options.sort : this.options.sort; + + this.options.clear_screen = (typeof this.xtrn_custommenu_options.clear_screen !== "undefined") + ? this.xtrn_custommenu_options.clear_screen : this.options.clear_screen; + + this.options.singlecolumn_margin = (typeof this.xtrn_custommenu_options.singlecolumn_margin !== "undefined") + ? this.xtrn_custommenu_options.singlecolumn_margin : this.options.singlecolumn_margin; + + if (typeof bbs !== "undefined") { + this.options.singlecolumn_height = (typeof this.xtrn_custommenu_options.singlecolumn_height !== "undefined") + ? this.xtrn_custommenu_options.singlecolumn_height : this.options.singlecolumn_height; + } + + // no need to override restricted_user_msg or no_programs_msg - these + // will be the same for both types of menus + + // Allow overriding on a per-menu basis + var menuoptions = load({}, "modopts.js", "xtrnmenu:" + menuid); + if ((typeof menuid !== "undefined") && (menuoptions != null)) { + for (var m in menuoptions) { + this.options[m] = menuoptions[m]; + } + } + } + + // these options only apply to terminals/consoles + + // the intention is to obtain all the mod_opts options for xtrn_sec, and + // override if a custom menu global setting is set + + //// The following are used for the enhanced custom menu functionality + if (this.options.custom_menu_not_found_msg === undefined) { + this.options.custom_menu_not_found_msg = "Menu %MENUID% not found"; + } + + if (this.options.custom_menu_program_not_found_msg === undefined) { + this.options.custom_menu_program_not_found_msg = "Program %PROGRAMID% not found"; + } + + return this.options; +} + +// return a custom menu object +ExternalMenus.prototype.getMenu = function(menuid) { + // grab the specified menu, or get the main menu + if ((typeof menuid === "undefined") || !menuid) { + menuid = "main"; + } else { + menuid = menuid.toLowerCase(); + } + + var menu; + + if ((typeof this.menuconfig !== "undefined") && (typeof this.menuconfig.menus !== "undefined")) { + this.menuconfig.menus.some(function (indmenu) { + if (indmenu.id.toLowerCase() == menuid) { + menu = indmenu; + } + }); + } + + if (!menu && (menuid == "main")) { + // no custom menus defined, make one to mimic old behavior + + var menuitems = []; + + var i; + xtrn_area.sec_list.forEach(function (sec) { + if (sec.can_access) { + menuitems.push({ + "input": i, + "target": sec.code, + "type": "xtrnmenu", + "title": sec.name, + "access_string": sec.ars + }); + i++; + } + }); + + menu = { + "id": "main", + "title": "Main Menu", + "items": menuitems + }; + } + return menu; +} + +// return a section menu object (stock synchronet external door section) +ExternalMenus.prototype.getSectionMenu = function(menuid) { + + // grab the specified menu, or get the main menu + if ((typeof menuid === "undefined") || !menuid) { + return false; + } + + var menuitems = []; + var menu, title; + + xtrn_area.sec_list.some(function (sec) { + + if (sec.code.toLowerCase() == menuid.toLowerCase()) { + title = sec.name; + + if (!sec.can_access || sec.prog_list.length < 1) { + return false; + } + + var i = 1; + sec.prog_list.some(function (prog) { + menuitems.push({ + 'input' : i, + 'target': prog.code, + 'title': prog.name, + 'type': 'xtrnprog', + 'access_string': prog.ars, + 'cost': prog.cost + }); + i++; + }); + + if (menuitems.length > 0) { + menu = { + 'id': menuid, + 'title': title, + 'items': menuitems, + }; + } + return; + } + }); + + return menu; +} + +// Sort the menu items according to options +ExternalMenus.prototype.getSortedItems = function(menuobj) { + var sort_type; + + if ((typeof menuobj.sort_type !== "undefined") && menuobj.sort_type) { + sort_type = menuobj.sort_type; // "name" or "key" + } else { + sort_type = this.options.sort; // bool + } + + // first, build a new menu with only options they have access to + var menuitemsfiltered = []; + for (i in menuobj.items) { + switch (menuobj.items[i].type) { + case 'xtrnmenu': + for (j in xtrn_area.sec_list) { + if (xtrn_area.sec_list[j].code.toUpperCase() == menuobj.items[i].target.toUpperCase()) { + if (xtrn_area.sec_list[j].can_access) { + menuitemsfiltered.push(menuobj.items[i]); + } + } + } + break; + + case 'xtrnprog': + for (j in xtrn_area.sec_list) { + for (var k in xtrn_area.sec_list[j].prog_list) { + if (xtrn_area.sec_list[j].prog_list[k].code.toUpperCase() == menuobj.items[i].target.toUpperCase()) { + if (xtrn_area.sec_list[j].prog_list[k].can_access) { + menuitemsfiltered.push(menuobj.items[i]); + } + } + } + } + break; + + case 'custommenu': + default: + if ((typeof menuobj.items[i].access_string === "undefined") || !menuobj.items[i].access_string) { + // no access string defined, everyone gets access + menuitemsfiltered.push(menuobj.items[i]); + } else { + if (user.compare_ars(menuobj.items[i].access_string)) { + // they have access + menuitemsfiltered.push(menuobj.items[i]); + } + } + break; + } + } + + // if no custom input keys are specified for an input, then assign a numeric input + // this would mimic the built-in external section menu functionality + // but this is only assigned on sort_type key because if the sort type is for + // titles, we need to sort by title first before assigning the sequential numbers + var sortind = 0; + // make sure we only use the next available number for automatic assignment + for (i in menuitemsfiltered) { + if (!isNaN(menuitemsfiltered[i].input)) { + if (menuitemsfiltered[i].input > sortind) { + sortind = menuitemsfiltered[i].input; + } + } + } + sortind++; + + if (sort_type == "key") { + for (i in menuitemsfiltered) { + if (!menuitemsfiltered[i].input) { + menuitemsfiltered[i].input = sortind; + sortind++; + } + } + } + + if (sort_type) { + switch (sort_type) { + case "key": + menuitemsfiltered.sort(this.sort_by_input); + break; + default: + case "title": + case "name": + menuitemsfiltered.sort(this.sort_by_title); + break; + } + } + + // if this is a sort by title and the key is empty, it will be assigned the next available number + // this is to support auto-generated inputs like is done on the built-in section menus + // however, to keep the numeric keys in order, the numbers could not be pre-assigned like it done + // on the sort_type key + for (i in menuitemsfiltered) { + if ((sort_type !== "key") && !menuitemsfiltered[i].input) { + menuitemsfiltered[i].input = sortind; + sortind++; + } + } + + return menuitemsfiltered; +} + +// Original sort function used by external_program_menu and external_section_menu +ExternalMenus.prototype.sort_by_name = function(a, b) { + if(a.name.toLowerCase()>b.name.toLowerCase()) return 1; + if(a.name.toLowerCase()<b.name.toLowerCase()) return -1; + return 0; +} + +// Sort by title - used by external_section_menu_custom +ExternalMenus.prototype.sort_by_title = function(a, b) { + if (a.title.toLowerCase() < b.title.toLowerCase()) { + return -1; + } + if (a.title.toLowerCase() > b.title.toLowerCase()) { + return 1; + } + return 0; +} + +// Sort by input key - used by external_section_menu_custom +ExternalMenus.prototype.sort_by_input = function(a, b) { + if (a.input.toString().toLowerCase() < b.input.toString().toLowerCase()) { + return -1; + } + if (a.input.toString().toLowerCase() > b.input.toString().toLowerCase()) { + return 1; + } + return 0; +} diff --git a/exec/sutils.js b/exec/sutils.js index 1058a94da4cecb89b4eb21cb6375299ad284f840..ce837bf7c712d2ac9cbb921cc32139013481b3c8 100644 --- a/exec/sutils.js +++ b/exec/sutils.js @@ -1,4 +1,3 @@ - "use strict"; const REVISION = "$Revision: 1.0 $".split(' ')[1]; @@ -29,7 +28,7 @@ while((!which || which < 1) && !aborted()) { if (typeof categories !== "undefined") { var x = 1; for (var catid in categories) { - printf("%2d. %s\n", x, categories[catid]); + printf("%2d. %s\r\n", x, categories[catid]); categoryitems[x] = catid; x++; }; @@ -73,7 +72,7 @@ function docategorymenu(catid, catname) { var x = 1; for (var iniid in utilitems) { if (file_exists(js.exec_dir + utilitems[iniid].filename)) { - printf("%2d. %-20s %s\n", x, utilitems[iniid].name, utilitems[iniid].desc); + printf("%2d. %-20s %s\r\n", x, utilitems[iniid].name, utilitems[iniid].desc); menuitems[x] = iniid; x++; } diff --git a/exec/xtrn_sec.js b/exec/xtrn_sec.js index e2dd5b6ef231cdd2313fb63bdad91eb54fb3d917..e2b7540d2160a126b4b5b3d9c4ac86d43b719aae 100644 --- a/exec/xtrn_sec.js +++ b/exec/xtrn_sec.js @@ -121,12 +121,15 @@ function external_program_menu(xsec) if(options.clear_screen) console.clear(LIGHTGRAY); + var show_header = true; var secnum = xtrn_area.sec_list[xsec].number+1; var seccode = xtrn_area.sec_list[xsec].code; if(!bbs.menu("xtrn" + secnum + "_head", P_NOERROR) && !bbs.menu("xtrn" + seccode + "_head", P_NOERROR)) { - bbs.menu("xtrn_head", P_NOERROR); + show_header = !bbs.menu("xtrn_head", P_NOERROR); } + else + show_header = false; if(bbs.menu("xtrn" + secnum, P_NOERROR) || bbs.menu("xtrn" + seccode, P_NOERROR)) { if(!bbs.menu("xtrn" + secnum + "_tail", P_NOERROR) && !bbs.menu("xtrn" + seccode + "_tail", P_NOERROR)) { @@ -135,11 +138,14 @@ function external_program_menu(xsec) } else { var multicolumn = options.multicolumn && prog_list.length > options.singlecolumn_height; + var center = options.center && !multicolumn; + var margin = center ? format("%*s", (console.screen_columns * 0.25) - 1, "") : ""; if(options.sort) prog_list.sort(sort_by_name); - printf(options.header_fmt, xtrn_area.sec_list[xsec].name); + if(show_header) + write(margin, format(options.header_fmt, xtrn_area.sec_list[xsec].name)); if(options.titles.trimRight() != '') - write(options.titles); + write(margin, options.titles); if(multicolumn) { write(options.multicolumn_separator); if (options.titles.trimRight() != '') @@ -147,7 +153,7 @@ function external_program_menu(xsec) } if(options.underline.trimRight() != '') { console.crlf(); - write(options.underline); + write(margin, options.underline); } if(multicolumn) { write(options.multicolumn_separator); @@ -162,6 +168,7 @@ function external_program_menu(xsec) n=prog_list.length; for(i=0;i<n && !console.aborted;i++) { + write(margin); console.add_hotspot(i+1); printf(multicolumn ? options.multicolumn_fmt : options.singlecolumn_fmt ,i+1 @@ -185,7 +192,13 @@ function external_program_menu(xsec) bbs.menu("xtrn_tail", P_NOERROR); } bbs.node_sync(); - console.mnemonics(options.which); + if(margin) { + console.crlf(); + write(margin); + console.mnemonics(options.which.trimLeft()); + } + else + console.mnemonics(options.which); } system.node_list[bbs.node_num-1].aux=0; /* aux is 0, only if at menu */ bbs.node_action=NODE_XTRN; @@ -202,6 +215,10 @@ function external_section_menu() { var i,j; var xsec=0; + var longest = 0; + for(i = 0; i < xtrn_area.sec_list.length; i++) + longest = Math.max(xtrn_area.sec_list[i].name.length, longest); + var margin = options.center ? format("%*s", ((console.screen_columns - longest)/2) - 5, "") : ""; while(bbs.online) { @@ -225,7 +242,7 @@ function external_section_menu() if(options.clear_screen) console.clear(LIGHTGRAY); - bbs.menu("xtrn_sec_head", P_NOERROR); + var show_header = !bbs.menu("xtrn_sec_head", P_NOERROR); if(bbs.menu_exists("xtrn_sec")) { bbs.menu("xtrn_sec"); @@ -235,16 +252,23 @@ function external_section_menu() if(options.sort) sec_list.sort(sort_by_name); - printf(options.section_header_fmt.replace('\x01l', ''), options.section_header_title); + if(show_header) + printf(margin + options.section_header_fmt.replace('\x01l', ''), options.section_header_title); for (i = 0; i < sec_list.length; i++) { console.add_hotspot(i+1); - printf(options.section_fmt, i + 1, sec_list[i].name); + printf(margin + options.section_fmt, i + 1, sec_list[i].name); } bbs.menu("xtrn_sec_tail", P_NOERROR); bbs.node_sync(); - console.mnemonics(format(options.section_which, xsec + 1)); + if(options.center) { + console.crlf(); + write(margin); + console.mnemonics(format(options.section_which, xsec + 1).trimLeft()); + } + else + console.mnemonics(format(options.section_which, xsec + 1)); } bbs.node_sync(); diff --git a/exec/xtrnmenu.js b/exec/xtrnmenu.js new file mode 100644 index 0000000000000000000000000000000000000000..7a32868f43ebfac10e18da3372c0a19a9279786f --- /dev/null +++ b/exec/xtrnmenu.js @@ -0,0 +1,266 @@ +/** + * Xtrn Menu Mod + * Custom External Program Menus + * by Michael Long mlong innerrealmbbs.us + * + * This is the loadable module that displays the custom external menus + * in terminal server (telnet/rlogin/ssh) + * + * To jump to a specific menu, pass the ID as an argument + * + * To set options, add to modopts.ini [xtrnmenu] + * + * See instructions at http://wiki.synchro.net/module:xtrnmenu + */ + +"use strict"; + +require("sbbsdefs.js", "K_NONE"); + +load("xtrnmenulib.js"); + +var options, xsec = -1; + +//// Main +var ExternalMenus = new ExternalMenus(); +const menuconfig = ExternalMenus.menuconfig; + +{ + var i,j; + for(i in argv) { + for(j in xtrn_area.sec_list) { + if(argv[i].toLowerCase()==xtrn_area.sec_list[j].code) + xsec=j; + } + } +} +if (xsec > -1) { + // if its the id of a standard section menu, send it to the + // stock menu + js.exec("xtrn_sec.js", {}, xsec); +} else if (typeof argv[0] !== "undefined") { + // if its not a section menu, assume it is a custom menu + external_section_menu_custom(argv[0]); +} else { + // main custom menu + external_section_menu_custom(); +} + + +// Renders the top-level external menu +function external_section_menu_custom(menuid) +{ + var i, menucheck, menuobj, item_multicolumn_fmt, item_singlecolumn_fmt, + cost, multicolumn, menuitemsfiltered = []; + var validkeys = []; // valid chars on menu selection + var keymax = 0; // max integer allowed on menu selection + + var options = ExternalMenus.getOptions('custommenu', menuid); + + menuobj = ExternalMenus.getMenu(menuid); + + // Allow overriding auto-format on a per-menu basis + var multicolumn_fmt = options.multicolumn_fmt; + var singlecolumn_fmt = options.singlecolumn_fmt; + + while (bbs.online) { + console.aborted = false; + + if (typeof menuobj === "undefined") { + doerror(options.custom_menu_not_found_msg.replace('%MENUID%', menuid)); + break; + } + + if (options.clear_screen) { + console.clear(LIGHTGRAY); + } + + if (user.security.restrictions&UFLAG_X) { + write(options.restricted_user_msg); + break; + } + + if (!xtrn_area.sec_list.length) { + write(options.no_programs_msg); + break; + } + + var keyin; + + system.node_list[bbs.node_num-1].aux = 0; /* aux is 0, only if at menu */ + bbs.node_action = NODE_XTRN; + bbs.node_sync(); + + menuitemsfiltered = ExternalMenus.getSortedItems(menuobj); + + if (!bbs.menu("xtrnmenu_head_" + menuid, P_NOERROR) && !bbs.menu("xtrnmenu_head", P_NOERROR)) { + bbs.menu("xtrn_head", P_NOERROR); + } + + // if file exists text/menu/xtrnmenu_(menuid).[rip|ans|mon|msg|asc], + // then display that, otherwise dynamiic + if (!bbs.menu("xtrnmenu_" + menuid, P_NOERROR)) { + + // if no custom menu file in text/menu, create a dynamic one + multicolumn = options.multicolumn && menuitemsfiltered.length > options.singlecolumn_height; + + printf(options.header_fmt, menuobj.title); + if(options.titles.trimRight() != '') + write(options.titles); + if(multicolumn) { + write(options.multicolumn_separator); + if (options.titles.trimRight() != '') + write(options.titles); + } + if(options.underline.trimRight() != '') { + console.crlf(); + write(options.underline); + } + if(multicolumn) { + write(options.multicolumn_separator); + if (options.underline.trimRight() != '') + write(options.underline); + } + console.crlf(); + + // n is the number of items for the 1st column + var n; + if (multicolumn) { + n = Math.floor(menuitemsfiltered.length / 2) + (menuitemsfiltered.length & 1); + } else { + n = menuitemsfiltered.length; + } + + // j is the index for each menu item on 2nd column + var j = n; // start j at the first item for 2nd column + for (i = 0; i < n && !console.aborted; i++) { + cost = ""; + if (menuitemsfiltered[i].type == "xtrnprog") { + // if its an external program, get the cost + cost = xtrn_area.prog[menuitemsfiltered[i].target.toLowerCase()].cost; + } + + console.add_hotspot(menuitemsfiltered[i].input.toString()); + + validkeys.push(menuitemsfiltered[i].input.toString()); + var intCheck = Number(menuitemsfiltered[i].input); + if (!intCheck.isNaN) { + if (intCheck > keymax) { + keymax = menuitemsfiltered[i].input; + } + } + + // allow overriding format on a per-item basis + // great for featuring a specific game + var checkkey = menuitemsfiltered[i].target + '-multicolumn_fmt'; + checkkey = checkkey.toLowerCase(); + item_multicolumn_fmt = (typeof options[checkkey] !== "undefined") ? + options[checkkey] : options.multicolumn_fmt; + + checkkey = menuitemsfiltered[i].target + '-singlecolumn_fmt' + checkkey = checkkey.toLowerCase(); + item_singlecolumn_fmt = (typeof options[checkkey] !== "undefined") ? + options[checkkey] : options.singlecolumn_fmt; + + printf(multicolumn ? item_multicolumn_fmt : item_singlecolumn_fmt, + menuitemsfiltered[i].input.toString().toUpperCase(), + menuitemsfiltered[i].title, + cost + ); + + if (multicolumn) { + if ((typeof(menuitemsfiltered[j]) !== "undefined")) { + validkeys.push(menuitemsfiltered[j].input.toString()); + + var intCheck = Number(menuitemsfiltered[j].input); + if (!intCheck.isNaN) { + if (intCheck > keymax) { + keymax = menuitemsfiltered[j].input; + } + } + + // allow overriding format on a per-item basis + // great for featuring a specific game + var checkkey = menuitemsfiltered[j].target + '-multicolumn_fmt'; + checkkey = checkkey.toLowerCase(); + item_multicolumn_fmt = (typeof options[checkkey] !== "undefined") ? + options[checkkey] : options.multicolumn_fmt; + + checkkey = menuitemsfiltered[j].target + '-singlecolumn_fmt' + checkkey = checkkey.toLowerCase(); + + write(options.multicolumn_separator); + console.add_hotspot(menuitemsfiltered[j].input.toString()); + printf(item_multicolumn_fmt, + menuitemsfiltered[j].input.toString().toUpperCase(), + menuitemsfiltered[j].title, + cost + ); + } else { + write(options.multicolumn_separator); + } + j++; + } + console.crlf(); + } + + if (!bbs.menu("xtrnmenu_tail_" + menuid, P_NOERROR) && !bbs.menu("xtrnmenu_tail", P_NOERROR)) { + bbs.menu("xtrn_tail", P_NOERROR); + } + + bbs.node_sync(); + console.mnemonics(options.which); + } + + validkeys.push('q'); + keyin = console.getkeys(validkeys, keymax, K_NONE); + keyin = keyin.toString().toLowerCase(); + + if (keyin) { + // q for quit + if (keyin == "q") { + console.clear(); + break; + } + + menuitemsfiltered.some(function (menuitemfiltered) { + var menutarget = menuitemfiltered.target.toLowerCase(); + var menuinput = menuitemfiltered.input.toString().toLowerCase(); + + if (menuinput == keyin) { + switch (menuitemfiltered.type) { + // custom menu, defined in xtrnmenu.json + case 'custommenu': + external_section_menu_custom(menutarget); + return true; + // external program section + case 'xtrnmenu': + js.exec("xtrn_sec.js", {}, menutarget); + //js.exec(js.exec_dir + 'xtrn_sec.js ' + menutarget) + return true; + // external program + case 'xtrnprog': + // run the external program + if (typeof xtrn_area.prog[menutarget] !== "undefined") { + bbs.exec_xtrn(menutarget); + return true; + } else { + doerror(options.custom_menu_program_not_found_msg.replace('%PROGRAMID%', menutarget)); + } + break; + } //switch + } // if menu item matched keyin + }); // foreach menu item + } // if keyin + } // main bbs.online loop +} + + +// Display error message to console and save to log +function doerror(msg) +{ + console.crlf(); + log(LOG_ERR, msg); + console.putmsg('\x01+\x01h\x01r' +msg + ". The sysop has been notified." + '\x01-\r\n'); +} + diff --git a/exec/xtrnmenucfg.js b/exec/xtrnmenucfg.js new file mode 100644 index 0000000000000000000000000000000000000000..21003b399a8234eb332b06e51f0809f76f1d5d2e --- /dev/null +++ b/exec/xtrnmenucfg.js @@ -0,0 +1,828 @@ +"use strict" + +/** + * Menu editor for Custom External Program Menus + * by Michael Long mlong innerrealmbbs.us + * + * This edits the file xtrnmenu.cfg + */ + +load("sbbsdefs.js"); +load("uifcdefs.js"); + +var log = function(msg) { + var f = new File("uifc.log"); + f.open("a"); + f.writeln(system.timestr() + ": " + msg); + f.close(); +} + +/** + * Write the menus out to a file + */ +var saveMenus = function() { + + var filename = system.ctrl_dir + "xtrnmenu.cfg"; + + file_backup(filename, 5); + + var config_file = new File(filename); + if (!config_file.open('w+')) { + uifc.msg("ERROR: Could not write to " + filename); + return; + } + config_file.write(JSON.stringify(menuconfig, null, ' ')); + config_file.close(); + + uifc.msg("Config Saved"); +} + +/** + * Edit a custom menu + * @param menuid + */ +var editMenu = function(menuid) { + var menuindex, menu, menuindex, selection, selection2, editproperty; + var last = 0; + var selections = [], displayoptions = [], displayoptionids = []; + var menusize = menuconfig.menus.length; + + // new menu but no code given, make one + if ((typeof menuid === "undefined") || (!menuid)) { + menuid = time(); + } + + // look for existing menu + for (var i in menuconfig.menus) { + if (menuconfig.menus[i].id == menuid) { + menu = menuconfig.menus[i]; + menuindex = i; + } + } + + if (typeof menu === "undefined") { + menuindex = menusize; + menuconfig.menus[menuindex] = { + 'id': menuid, + 'title': "New Generated Menu " + menuid, + "sort_type": "title", + 'items': [] + }; + menu = menuconfig.menus[menuindex]; + } + + while(1) { + uifc.help_text = word_wrap("This screen allows you to edit the configuration options for the custom menu.\r\n\r\nMost options default or are set in modopts.ini, but here you can define them on a per-menu basis.\r\n\r\nClick Edit Items to edit the individual entries (programs, menus, etc.)"); + + selections = []; + for (var j in menu.items) { + selections.push(menu.items.index); + } + + displayoptions = []; + displayoptionids = []; + + // setup display menu + displayoptions.push(format("%23s: %s", "id", + ("id" in menu ? menu.id : time()))); + displayoptionids.push("id"); + + displayoptions.push(format("%23s: %s", "title", + ("title" in menu ? menu.title : ""))); + displayoptionids.push("title"); + + displayoptions.push(format("%23s: %s", "sort_type", + ("sort_type" in menu ? menu.sort_type : "(default)"))); + displayoptionids.push("sort_type"); + + displayoptions.push(format("%23s: %s", "Edit Items", "[...]")); + displayoptionids.push("items"); + + selection = uifc.list(WIN_ORG|WIN_MID|WIN_ACT|WIN_ESC, 0, 0, 0, last, last, + menu.title + ": Options", displayoptions); + + if (selection < 0) { + // escape key + break; + } + + editproperty = displayoptionids[selection]; + + switch (editproperty) { + case 'id': + uifc.help_text = word_wrap("This is a unique ID for the menu, which can be used as the target to\r\ncall the menu from other menus.\r\n\r\nFor the top-level menu, the id should be 'main'."); + var selection2 = uifc.input(WIN_MID, "Menu ID", menu.id, 50, K_EDIT); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + menu.id = selection2; + break; + case 'title': + uifc.help_text = word_wrap("Title for the menu, to be shown at the top of the menu"); + selection2 = uifc.input(WIN_MID, "Menu Title", menu.title, 255, K_EDIT); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + menu.title = selection2; + break; + + case 'sort_type': + uifc.help_text = word_wrap("How to sort the menu:\r\nby input key\r\nby title\r\nnone (the items remain in the order they are in the config)"); + + switch (uifc.list(WIN_ORG | WIN_MID, "Sort Type", ["key", "title", "none"])) { + case 0: + menu.sort_type = "key"; + break; + case 1: + menu.sort_type = "title"; + break; + case 2: + delete menu.sort_type; + break; + default: + //esc key + break; + } + + case 'items': + editItems(menuid); + break; + + default: + // this isn't supposed to happen + uifc.msg("Unknown option"); + break; + } + + last = Math.max(selection, 0); + } +} + +/** + * Edit menu items in a menu + * @param menuid + */ +var editItems = function(menuid) { + var menuindex, menu, selection, selection2, keyused, items = [], itemids = []; + var i, last = 0; + + // cur bar top left width + var ctxm = new uifc.list.CTX(0, 0, 0, 0, 0); + + if (typeof menuid === "undefined") { + uifc.msg("Menu could not be found"); + return; + } else { + for (i in menuconfig.menus) { + if (menuconfig.menus[i].id == menuid) { + menu = menuconfig.menus[i]; + menuindex = i; + } + } + } + + if ((typeof menu.items == "undefined") || (menu.items.length == 0)) { + // no items, prompt them to make one + editItem(menu.id, 0); + } + + + uifc.help_text = word_wrap("This menu allows editing the various items in this menu.\r\n\r\n" + + "If you leave input key blank, it will use an auto-generated number at display time.\r\n\r\n" + + "Choose a type first and the dropdown to choose tha target will allow you to select your target.\r\n\r\n" + + "Access string only applies to custom menu items. For external sections or external programs, use the access settings in scfg.\r\n\r\n"); + + while(1) { + items = []; + itemids = []; + for(i in menu.items) { + items.push(format( + "%6s %10s %s", + menu.items[i].input ? menu.items[i].input : '(auto)', + menu.items[i].type, + menu.items[i].title + )); + itemids.push(i); + } + // WIN_ORG = original menu + // WIN_MID = centered mid + // WIN_ACT = menu remains active after a selection + // WIN_ESC = screen is active when escape is pressed + // WIN_XTR = blank line to insert + // WIN_INS = insert key + // WIN_DEL = delete + // WIN_CUT = cut ctrl-x + // WIN_COPY = copy ctrl-c + // WIN_PUT = paste ctrl-v + // WIN_SAV = use context/save position + selection = uifc.list( + WIN_ORG|WIN_MID|WIN_ACT|WIN_ESC|WIN_XTR|WIN_INS|WIN_DEL|WIN_CUT|WIN_COPY|WIN_PASTE|WIN_SAV, + menu.title + ": Items", + items, + ctxm + ); + + if (selection == -1) { + // esc key + break; + } + + if ((selection & MSK_ON) == MSK_DEL) { + // delete item + selection &= MSK_OFF; + //renumber array so there are no gaps + var menuitems2 = []; + for (var i in menu.items) { + if (i != itemids[selection]) { + menuitems2.push(menu.items[i]); + } + } + menu.items = menuitems2; + } else if (((selection & MSK_ON) == MSK_INS)) { + // new item from INSERT KEY + editItem(menuid, null); + } else if ((selection & MSK_ON) == MSK_COPY) { + // copy item + selection &= MSK_OFF; + copyitem = JSON.parse(JSON.stringify(menu.items[itemids[selection]])); // make copy + } else if ((selection & MSK_ON) == MSK_CUT) { + // cut item + selection &= MSK_OFF; + copyitem = menu.items[itemids[selection]]; + + //renumber array so there are no gaps + var menuitems2 = []; + for (var i in menu.items) { + if (i != itemids[selection]) { + menuitems2.push(menu.items[i]); + } + } + menu.items = menuitems2; + } else if ((selection & MSK_ON) == MSK_PASTE) { + // paste item + selection &= MSK_OFF; + + var oktopaste = true; + + // only paste if there is an item copied + if ("type" in copyitem) { + // if item already exists in list, modify if since you can't have dupes (except empty input keys) + for (var i in menu.items) { + if ((menu.items[i].input == copyitem.input) && (copyitem.input !== null) && (copyitem.input !== "")) { + oktopaste = true; + while(1) { + selection2 = uifc.input(WIN_MID, "Enter New Input Key", "", 3, K_EDIT); + if ((selection2 == -1) || (selection2 === "") || (selection2 === null)) { + // escape key + copyitem.input = null; + break; + } + if (selection2 || selection2 === 0) { + selection2 = selection2.toUpperCase(); + keyused = false; + for (var j in menu.items) { + if (menu.items[j].input && (menu.items[j].input.toUpperCase() == selection2)) { + keyused = true; + } + } + if (keyused) { + uifc.msg("This input key is alread used for another item"); + oktopaste = false; + } else { + copyitem.input = selection2; + oktopaste = true; + break; + } + } + } + copyitem.input = selection2; + } + } + if ((oktopaste) || (copyitem.input === "null") || (copyitem.input === "")) { + var menuitems2 = []; + for (i in menu.items) { + menuitems2.push(menu.items[i]); + // paste copied item after selected item + if (i == itemids[selection]) { + menuitems2.push(copyitem); + ctxm.cur = i-1; + } + } + menu.items = menuitems2; + } + } + } else if (selection >= menu.items.length) { + // new item from blank line + editItem(menuid, null); + } else { + editItem(menuid, itemids[selection]); + } + last = Math.max(selection, 0); + } +} + +/** + * Edit a specific menu item entry + * @param menuid + * @param itemindex + */ +var editItem = function(menuid, itemindex) { + var menu, menuindex, item; + var keyused, selection, selection2, i, last = 0; + var displayoptions = [], displayoptionids = [], newitems = []; + // used for building target selection + var custommenuitems = [], custommenuitemsids = [], custommenunames = []; + + if (typeof menuid === "undefined") { + uifc.msg("Menu could not be found"); + return; + } else { + for (i in menuconfig.menus) { + if (menuconfig.menus[i].id == menuid) { + menu = menuconfig.menus[i]; + menuindex = i; + } + } + } + + if (typeof menu.items[itemindex] === "undefined") { + // new item + menu.items.push({ + "input": null, + "title": "New Item " + time(), + "type": null, + "target": null, + "access_string": null, + }); + itemindex = menu.items.length - 1; + present_select_targettype(menu.items[itemindex]); + } + item = menu.items[itemindex]; + + var itemctx = new uifc.list.CTX(0,0,0,0,0); + while(1) { + displayoptions = []; + displayoptionids = []; + + // setup display menu + displayoptions.push(format("%23s: %s", "input", + (("input" in item) && (item.input !== null) && (item.input !== "") ? item.input : "(auto)"))); + displayoptionids.push("input"); + + displayoptions.push(format("%23s: %s", "title", + ("title" in item ? item.title : ""))); + displayoptionids.push("title"); + + displayoptions.push(format("%23s: %s", "type", + ("type" in item ? item.type : ""))); + displayoptionids.push("type"); + + displayoptions.push(format("%23s: %s", "target", + ("target" in item ? item.target : ""))); + displayoptionids.push("target"); + + if (item.type == "custommenu") { + displayoptions.push(format("%23s: %s", "access_string", + ("access_string" in item ? item.access_string : "(default)"))); + displayoptionids.push("access_string"); + } + + selection = uifc.list(WIN_ORG | WIN_MID | WIN_ACT | WIN_ESC, + menu.title + ": Item " + itemindex, displayoptions, itemctx); + + if (selection < 0) { + if (!item.title || !item.type || !item.target) { + if (uifc.list(WIN_ORG | WIN_MID, "This item is missing required items.", ["Remove Item", "Edit Item"]) == 0) { + // delete item and continue + newitems = []; + for (i in menu.items) { + if (i != itemindex) { + newitems.push(menu.items[i]); + } + } + menu.items = newitems; + break; + } + } else { + // leave menu + break; + } + } + + switch (displayoptionids[selection]) { + + case 'input': + uifc.help_text = word_wrap("The input key to access this item. Can be anything except Q. Leave blank to auto-generate a number."); + selection2 = uifc.input(WIN_MID, "Input Key", item.input, 3, K_EDIT); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + + if (selection2 !== "") { + selection2 = selection2.toUpperCase(); + + keyused = false; + for (i in menu.items) { + if ((menu.items[i].input === null) || (menu.items[i].input === "")) { + // continue here as toUpperCase would break it, and they don't need to match + // anyway because you can have multiple auto-assigned input items + continue; + } + if ((menu.items[i].input.toUpperCase() == selection2) && (i != itemindex)) { + keyused = true; + } + } + + if (keyused) { + uifc.msg("This input key is already used by another item."); + } else { + item.input = selection2; + } + } else { + // save blank + item.input = selection2; + } + break; + + case 'title': + uifc.help_text = word_wrap("The menu item title."); + selection2 = uifc.input(WIN_MID, "Title", item.title, 255, K_EDIT); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + if (!selection2 && selection2 !== 0) { + uifc.msg("Title is required."); + } else { + item.title = selection2; + } + break; + + case 'type': + present_select_targettype(item); + break; + + case 'target': + present_select_target(item); + break; + + case 'access_string': + uifc.help_text = word_wrap("The access string for the custom menu.\r\n\r\nOnly applies to custom menu items.\r\n\r\nExample: LEVEL 60"); + selection2 = uifc.input(WIN_MID, "Access String", item.access_string, 255, K_EDIT); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + item.access_string = selection2; + break; + + } + last = Math.max(selection, 0); + } +} + +function present_select_targettype(item) +{ + uifc.help_text = word_wrap( + "This is the type of target this item points to.\r\n\r\n" + + "custommenu is a custom menu defined in this tool.\r\n\r\n" + + "xtrnmenu is a standard Syncrhonet External Section Menu (refer to the scfg tool).\r\n\r\n" + + "xtrnprog is a direct link to an external program (refer to the scfg tool)"); + + var targetypectx = uifc.list.CTX(0, 0, 0, 0, 0); + if (typeof item.type !== "undefined") { + switch (item.type) { + case 'custommenu': + targetypectx.cur = 0; + targetypectx.bar = 0; + break; + case 'xtrnmenu': + targetypectx.cur = 1; + targetypectx.bar = 1; + break; + case 'xtrnprog': + targetypectx.cur = 2; + targetypectx.bar = 2; + break; + } + } + switch (uifc.list(WIN_ORG | WIN_MID | WIN_SAV, + "Target Type", ["custommenu", "xtrnmenu", "xtrnprog"], targetypectx)) { + case 0: + item.type = "custommenu"; + break; + case 1: + item.type = "xtrnmenu" + break; + case 2: + item.type = "xtrnprog"; + break; + default: + // includes escape key + break; + } + + // convienence... enter target selection + present_select_target(item) +} + +function present_select_target(item) +{ + uifc.help_text = word_wrap("This is the ID of the custom menu, external program section, or external program to link to."); + + var targetctx = uifc.list.CTX(0, 0, 0, 0, 0); + + var custommenuitems = []; + var custommenuitemsids = []; + var custommenunames = []; + + var selection2; + + switch (item.type) { + case "custommenu": + // present list of custom menus + for (i in menuconfig.menus) { + custommenuitems.push(format("%23s: %s", menuconfig.menus[i].id, menuconfig.menus[i].title)); + custommenuitemsids.push(menuconfig.menus[i].id); + custommenunames.push(menuconfig.menus[i].title); + } + + if ((typeof item.target !== "undefined") && item.target) { + for (var p in custommenuitemsids) { + if (custommenuitemsids[p] == item.target) { + targetctx.cur = p; + targetctx.bar = p; + } + } + } + + selection2 = uifc.list(WIN_ORG | WIN_MID | WIN_SAV, "Target", custommenuitems, targetctx); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + + item.target = custommenuitemsids[selection2]; + + while(1) { + if (uifc.list(WIN_ORG | WIN_MID, "Replace item title with sections's name?", ["Yes", "No"]) == 0) { + item.title = custommenunames[selection2]; // for external program, change title to program name + } + break; + } + break; + + case "xtrnmenu": + // present list of external program sections + var seclist = []; + for (i in xtrn_area.sec_list) { + seclist.push({ code: xtrn_area.sec_list[i].code, name: xtrn_area.sec_list[i].name}); + }; + seclist.sort(sort_by_code); + + for (i in seclist) { + custommenuitems.push(format("%23s: %s", seclist[i].code, seclist[i].name)); + custommenuitemsids.push(seclist[i].code); + custommenunames.push(seclist[i].name); + } + + if ((typeof item.target !== "undefined") && item.target) { + for (var p in custommenuitemsids) { + if (custommenuitemsids[p].toLowerCase() == item.target.toLowerCase()) { + targetctx.cur = p; + targetctx.bar = p; + } + } + } + + selection2 = uifc.list(WIN_ORG | WIN_MID | WIN_SAV, "Target", custommenuitems, targetctx); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + + item.target = custommenuitemsids[selection2]; + + while(1) { + if (uifc.list(WIN_ORG | WIN_MID, "Replace item title with sections's name?", ["Yes", "No"]) == 0) { + item.title = custommenunames[selection2]; // for external program, change title to program name + } + break; + } + break; + + case "xtrnprog": + + // present list of external programs + // create sorted list + var proglist = []; + + for (i in xtrn_area.prog) { + proglist.push({ code: xtrn_area.prog[i].code, name: xtrn_area.prog[i].name}); + }; + proglist.sort(sort_by_code); + for (i in proglist) { + custommenuitems.push(format("%23s: %s", proglist[i].code, proglist[i].name)); + custommenuitemsids.push(proglist[i].code); + custommenunames.push(proglist[i].name); + } + + if ((typeof item.target !== "undefined") && item.target) { + for (var p in custommenuitemsids) { + if (custommenuitemsids[p].toLowerCase() == item.target.toLowerCase()) { + targetctx.cur = p; + targetctx.bar = p; + } + } + } + + selection2 = uifc.list(WIN_ORG | WIN_MID | WIN_SAV, "Target", custommenuitems, targetctx); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + if (selection2 || selection2 === 0) { + item.target = custommenuitemsids[selection2]; + while(1) { + if (uifc.list(WIN_ORG | WIN_MID, "Replace item title with sections's name?", ["Yes", "No"]) == 0) { + item.title = custommenunames[selection2]; // for external program, change title to program name + } + break; + } + } + break; + + default: + selection2 = uifc.input(WIN_ORG | WIN_MID, "Target", item.target, 50, K_EDIT); + if ((selection2 < 0) || (selection2 == null)) { + // escape key + break; + } + + item.target = selection2; + break; + } +} + +function sort_by_name(a, b) +{ + if (a.name.toLowerCase() > b.name.toLowerCase()) return 1; + if (a.name.toLowerCase() < b.name.toLowerCase()) return -1; + return 0; +} + +function sort_by_code(a, b) +{ + if (a.code.toLowerCase() > b.code.toLowerCase()) return 1; + if (a.code.toLowerCase() < b.code.toLowerCase()) return -1; + return 0; +} + +function sort_by_id(a, b) +{ + if (a.id.toLowerCase() > b.id.toLowerCase()) return 1; + if (a.id.toLowerCase() < b.id.toLowerCase()) return -1; + return 0; +} + + +// MAIN +try { + var menuconfig = {}; + var copyitem = {}; // for menu item copy/paste + var config_file = new File(file_cfgname(system.ctrl_dir, "xtrnmenu.cfg")); + if (config_file.open('r+')) { + var config_src = config_file.read(); + try { + menuconfig = JSON.parse(config_src.toString()); + if (!menuconfig) { + writeln("ERROR! Could not parse xtrnmenu.cfg. JSON may be invalid."); + exit(); + } + } catch (e) { + writeln("ERROR! Could not parse xtrnmenu.cfg. JSON may be invalid. " + e.toString()); + exit(); + } + } + config_file.close(); + + if (typeof menuconfig.menus === "undefined") { + menuconfig.menus = []; + } + + uifc.init("Enhanced External Program Menus Configurator"); + uifc.lightbar_color = 120; + uifc.background_color = 21; + uifc.frame_color = 15; + js.on_exit("if (uifc.initialized) uifc.bail()"); + + // cur bar top left width + var ctx = new uifc.list.CTX(0, 0, 0, 0, 0); + + while(1) { + uifc.help_text = word_wrap("This program allows managing the Enhanced External Program Menu feature."); + + // no menus or no main menu + var mainmenufound = false; + for (var m in menuconfig.menus) { + if (menuconfig.menus[m].id.toLowerCase() == "main") { + mainmenufound = true; + } + } + if (!mainmenufound || (menuconfig.menus.length < 1)) { + uifc.msg("No menus defined and/or missing the main menu. Setting up now."); + editMenu("main"); + } + + // menus is array of menuconfig menu ids + var menus = []; + var menuTitles = []; + menuconfig.menus.sort(sort_by_id); + for (var m in menuconfig.menus) { + menus.push(menuconfig.menus[m].id); + menuTitles.push(format("%20s: %s", menuconfig.menus[m].id, menuconfig.menus[m].title)); + } + + menuTitles.push(format("%20s %s", '', "[Save Config Without Exit]")); + + // WIN_ORG = original menu, destroy valid screen area + // WIN_MID = place window in middle of screen + // WIN_XTR = add extra line at end for inserting at end + // WIN_DEL = allow user to use delete key + // WIN_ACT = menu remains active after a selection + // WIN_ESC = screen is active when escape is hit + // WIN_INS = allow user to use insert key + var selection = uifc.list( + WIN_ORG|WIN_MID|WIN_XTR|WIN_DEL|WIN_ACT|WIN_ESC|WIN_INS|WIN_SAV, + "Enhanced External Menus", + menuTitles, + ctx + ); + + if (selection == (menuTitles.length - 1)) { + // last item - save config + saveMenus(); + uifc.pop("Config saved."); + } else if (selection < 0) { + while (1) { + var ret = uifc.list(WIN_ORG | WIN_MID, "Save Changes Before Exit?", ["Yes", "No", "Cancel"]); + if (ret == 0) { + saveMenus(); + exit(); + } else if (ret == 1) { + // no - exit + exit(); + } else { + // cancel + break; + } + } + + } else if ((selection & MSK_ON) == MSK_DEL) { + selection &= MSK_OFF; + var menus2 = []; + for (var m in menuconfig.menus) { + if (menuconfig.menus[m].id != menus[selection]) { + menus2.push(menuconfig.menus[m]); + } + } + menuconfig.menus = menus2; + //selection--; + } else if (((selection & MSK_ON) == MSK_INS) || (selection >= menuconfig.menus.length)) { + // new menu + var newid = uifc.input( + WIN_MID, + "Enter a short unique id for the menu", + "", + 0 + ); + if (typeof newid !== "undefined") { + var menufound = false; + for (var mf in menuconfig.menus) { + if (menuconfig.menus[mf].id == newid) { + menufound = true; + } + } + if (menufound) { + uifc.msg("That ID is already in use. Please choose another."); + } else { + editMenu(newid); + } + } + } else { + editMenu(menuconfig.menus[selection].id); + } + } +} catch(err) { + if ((typeof uifc !== "undefined") && uifc.initialized) { + uifc.bail(); + } + writeln(err); + log(err); + if (typeof console !== "undefined") { + console.pause(); + } +} diff --git a/src/sbbs3/.gitignore b/src/sbbs3/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3467fa0e2eb31f7f349642e563cf3509f4a93a6c --- /dev/null +++ b/src/sbbs3/.gitignore @@ -0,0 +1,2 @@ +git_branch.h +git_hash.h diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp index 971436da8b4ec55fb8f645bab7b93159a71fae61..f4384eaea74f410c31c4a93c25781a4af9b336f1 100644 --- a/src/sbbs3/atcodes.cpp +++ b/src/sbbs3/atcodes.cpp @@ -1,7 +1,4 @@ /* Synchronet "@code" functions */ -// vi: tabstop=4 - -/* $Id: atcodes.cpp,v 1.142 2020/05/10 20:12:35 rswindell Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * @@ -16,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -39,6 +24,7 @@ #include "utf8.h" #include "unicode.h" #include "cp437defs.h" +#include "ver.h" #if defined(_WINSOCKAPI_) extern WSADATA WSAData; @@ -411,6 +397,12 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool return(str); } + if(strcmp(sp, "GIT_HASH") == 0) + return git_hash; + + if(strcmp(sp, "GIT_BRANCH") == 0) + return git_branch; + if(!strcmp(sp,"UPTIME")) { extern volatile time_t uptime; time_t up=0; @@ -1126,6 +1118,21 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool return(str); } + if(strcmp(sp,"FREESPACEM") == 0) { + safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir, 1024 * 1024)); + return(str); + } + + if(strcmp(sp,"FREESPACEG") == 0) { + safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir, 1024 * 1024 * 1024)); + return(str); + } + + if(strcmp(sp,"FREESPACET") == 0) { + safe_snprintf(str,maxlen,"%lu",getfreediskspace(cfg.temp_dir, 1024 * 1024 * 1024) / 1024); + return(str); + } + if(!strcmp(sp,"UPBYTES")) { safe_snprintf(str,maxlen,"%lu",useron.ulb); return(str); @@ -1578,6 +1585,16 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool return(tp); } + if(!strcmp(sp,"MAILR")) { + safe_snprintf(str,maxlen,"%u",getmail(&cfg,useron.number, /* Sent: */FALSE, /* attr: */MSG_READ)); + return(str); + } + + if(!strcmp(sp,"MAILU")) { + safe_snprintf(str,maxlen,"%u",getmail(&cfg,useron.number, /* Sent: */FALSE, /* attr: */~MSG_READ)); + return(str); + } + if(!strcmp(sp,"MAILW")) { safe_snprintf(str,maxlen,"%u",getmail(&cfg,useron.number, /* Sent: */FALSE, /* attr: */0)); return(str); @@ -1593,6 +1610,16 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool return(str); } + if(!strncmp(sp,"MAILR:",6) || !strncmp(sp,"MAILR#",6)) { + safe_snprintf(str,maxlen,"%u",getmail(&cfg,atoi(sp+6), /* Sent: */FALSE, /* attr: */MSG_READ)); + return(str); + } + + if(!strncmp(sp,"MAILU:",6) || !strncmp(sp,"MAILU#",6)) { + safe_snprintf(str,maxlen,"%u",getmail(&cfg,atoi(sp+6), /* Sent: */FALSE, /* attr: */~MSG_READ)); + return(str); + } + if(!strncmp(sp,"MAILW:",6) || !strncmp(sp,"MAILW#",6)) { safe_snprintf(str,maxlen,"%u",getmail(&cfg,atoi(sp+6), /* Sent: */FALSE, /* attr: */0)); return(str); @@ -1821,7 +1848,22 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen, long* pmode, bool return (current_msg != NULL && current_msg->user_voted == 1) ? text[PollAnswerChecked] : nulstr; if(!strcmp(sp,"MSG_DOWNVOTED")) return (current_msg != NULL && current_msg->user_voted == 2) ? text[PollAnswerChecked] : nulstr; - + if(strcmp(sp, "MSG_THREAD_ID") == 0 && current_msg != NULL) { + safe_snprintf(str, maxlen, "%lu", (ulong)current_msg->hdr.thread_id); + return str; + } + if(strcmp(sp, "MSG_THREAD_BACK") == 0 && current_msg != NULL) { + safe_snprintf(str, maxlen, "%lu", (ulong)current_msg->hdr.thread_back); + return str; + } + if(strcmp(sp, "MSG_THREAD_NEXT") == 0 && current_msg != NULL) { + safe_snprintf(str, maxlen, "%lu", (ulong)current_msg->hdr.thread_next); + return str; + } + if(strcmp(sp, "MSG_THREAD_FIRST") == 0 && current_msg != NULL) { + safe_snprintf(str, maxlen, "%lu", (ulong)current_msg->hdr.thread_first); + return str; + } if(!strcmp(sp,"SMB_AREA")) { if(smb.subnum!=INVALID_SUB && smb.subnum<cfg.total_subs) safe_snprintf(str,maxlen,"%s %s" diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp index 0063c660b578461103ef0190c0d8206dd5b0625c..b0e05b9f9988bee062f7870666ca33387c2c5c6d 100644 --- a/src/sbbs3/con_out.cpp +++ b/src/sbbs3/con_out.cpp @@ -787,7 +787,7 @@ void sbbs_t::inc_row(int count) } } -void sbbs_t::center(char *instr, unsigned int columns) +void sbbs_t::center(const char *instr, unsigned int columns) { char str[256]; size_t len; diff --git a/src/sbbs3/data.cpp b/src/sbbs3/data.cpp index 52614df625c0419c8bd34b96c478eb793ca39121..4e4e587a79e6a0c17e29bdd63fc82e29de4f81d9 100644 --- a/src/sbbs3/data.cpp +++ b/src/sbbs3/data.cpp @@ -1,7 +1,5 @@ /* Synchronet (oh, so old) data access routines */ -/* $Id: data.cpp,v 1.32 2020/04/27 07:42:23 rswindell Exp $ */ - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * @@ -15,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -45,7 +31,7 @@ /* Returns the number of the matched user or 0 if unsuccessful */ /* Called from functions main_sec, useredit and readmailw */ /****************************************************************************/ -uint sbbs_t::finduser(char *instr, bool silent_failure) +uint sbbs_t::finduser(const char* instr, bool silent_failure) { int file,i; char str[128],str2[256],str3[256],ynq[25],c,pass=1; diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c index 5460eb5080ec8ee70f2cbb13516f61691c7b8a51..87bf05a99b4375ebf9cd2efcd475d72691a67e94 100644 --- a/src/sbbs3/ftpsrvr.c +++ b/src/sbbs3/ftpsrvr.c @@ -43,6 +43,7 @@ #include "cryptlib.h" #include "xpprintf.h" // vasprintf #include "md5.h" +#include "ver.h" /* Constants */ @@ -82,7 +83,6 @@ static protected_uint32_t thread_count; static volatile time_t uptime=0; static volatile ulong served=0; static volatile BOOL terminate_server=FALSE; -static char revision[16]; static char *text[TOTAL_TEXT]; static str_list_t recycle_semfiles; static str_list_t shutdown_semfiles; @@ -3112,8 +3112,8 @@ static void ctrl_thread(void* arg) } sockprintf(sock,sess,"220-%s (%s)",scfg.sys_name, server_host_name()); - sockprintf(sock,sess," Synchronet FTP Server %s-%s Ready" - ,revision,PLATFORM_DESC); + sockprintf(sock,sess," Synchronet FTP Server %s%c-%s Ready" + ,VERSION, REVISION, PLATFORM_DESC); sprintf(str,"%sftplogin.txt",scfg.text_dir); if((fp=fopen(str,"rb"))!=NULL) { while(!feof(fp)) { @@ -5965,17 +5965,16 @@ const char* DLLCALL ftp_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.501 $", "%*s %s", revision); - - sprintf(ver,"%s %s%s " - "Compiled %s %s with %s" + safe_snprintf(ver, sizeof(ver), "%s %s%c%s " + "Compiled %s/%s %s %s with %s" ,FTP_SERVER - ,revision + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else ,"" #endif + ,git_branch, git_hash ,__DATE__, __TIME__, compiler); return(ver); @@ -5999,8 +5998,6 @@ void DLLCALL ftp_server(void* arg) char client_ip[INET6_ADDRSTRLEN]; CRYPT_SESSION none = -1; - ftp_ver(); - startup=(ftp_startup_t*)arg; SetThreadName("sbbs/ftpServer"); @@ -6024,7 +6021,7 @@ void DLLCALL ftp_server(void* arg) } ZERO_VAR(js_server_props); - SAFEPRINTF2(js_server_props.version,"%s %s",FTP_SERVER,revision); + SAFEPRINTF3(js_server_props.version,"%s %s%c", FTP_SERVER, VERSION, REVISION); js_server_props.version_detail=ftp_ver(); js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; @@ -6061,8 +6058,8 @@ void DLLCALL ftp_server(void* arg) memset(&scfg, 0, sizeof(scfg)); - lprintf(LOG_INFO,"Synchronet FTP Server Revision %s%s" - ,revision + lprintf(LOG_INFO,"Synchronet FTP Server Version %s%c%s" + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else @@ -6072,7 +6069,7 @@ void DLLCALL ftp_server(void* arg) DESCRIBE_COMPILER(compiler); - lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler); + lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", git_branch, git_hash, __DATE__, __TIME__, compiler); sbbs_srand(); /* Seed random number generator */ diff --git a/src/sbbs3/ftpsrvr.vcxproj b/src/sbbs3/ftpsrvr.vcxproj index 84539c12fdf751e01c69ae5e86e8d4877fb329cb..7bcbbde8917079093e640d756a0de47baefb822c 100644 --- a/src/sbbs3/ftpsrvr.vcxproj +++ b/src/sbbs3/ftpsrvr.vcxproj @@ -176,6 +176,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="ver.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\xpdev\xpdev_mt.vcxproj"> diff --git a/src/sbbs3/getmail.c b/src/sbbs3/getmail.c index a38d14390688dadd0182a0a5ad999cf1d4c8af9b..2dc5b48ce1081375dfa37da126d666b5ee5674bb 100644 --- a/src/sbbs3/getmail.c +++ b/src/sbbs3/getmail.c @@ -26,7 +26,7 @@ /* If sent is non-zero, it returns the number of mail sent by usernumber */ /* If usernumber is 0, it returns all mail on the system */ /****************************************************************************/ -int DLLCALL getmail(scfg_t* cfg, int usernumber, BOOL sent, uint16_t attr) +int DLLCALL getmail(scfg_t* cfg, int usernumber, BOOL sent, int attr) { char path[MAX_PATH+1]; int i=0; @@ -55,7 +55,9 @@ int DLLCALL getmail(scfg_t* cfg, int usernumber, BOOL sent, uint16_t attr) continue; if(idx.attr&MSG_DELETE) continue; - if((idx.attr&attr) != attr) + if(attr < 0 && (idx.attr & (~attr)) != 0) + continue; + if(attr > 0 && (idx.attr & attr) != attr) continue; if(usernumber == 0 || (!sent && idx.to==usernumber) diff --git a/src/sbbs3/getmail.h b/src/sbbs3/getmail.h index 949db78e9d9144527fa0cf72926c3950591e7b3c..2a1f4b0e38944d23b79f69693cafc6ce324d3a1f 100644 --- a/src/sbbs3/getmail.h +++ b/src/sbbs3/getmail.h @@ -28,7 +28,7 @@ extern "C" { #endif -DLLEXPORT int getmail(scfg_t* cfg, int usernumber, BOOL sent, uint16_t attr); +DLLEXPORT int getmail(scfg_t* cfg, int usernumber, BOOL sent, int attr); DLLEXPORT mail_t * loadmail(smb_t* smb, uint32_t* msgs, uint usernumber ,int which, long mode); DLLEXPORT void freemail(mail_t* mail); @@ -37,4 +37,4 @@ DLLEXPORT BOOL delfattach(scfg_t*, smbmsg_t*); #ifdef __cplusplus } #endif -#endif /* Don't add anything after this line */ \ No newline at end of file +#endif /* Don't add anything after this line */ diff --git a/src/sbbs3/getmsg.cpp b/src/sbbs3/getmsg.cpp index e12351e8a44f279319e7a7ca6d9528b2554ed255..d0c98915b5a366126d99ca131587b1f8c57d4752 100644 --- a/src/sbbs3/getmsg.cpp +++ b/src/sbbs3/getmsg.cpp @@ -97,7 +97,8 @@ void sbbs_t::show_msgattr(smbmsg_t* msg) uint32_t auxattr = msg->hdr.auxattr; uint32_t netattr = msg->hdr.netattr; - bprintf(text[MsgAttr] + char attr_str[64]; + safe_snprintf(attr_str, sizeof(attr_str), "%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ,attr&MSG_PRIVATE ? "Private " :nulstr ,attr&MSG_SPAM ? "SPAM " :nulstr ,attr&MSG_READ ? "Read " :nulstr @@ -112,11 +113,35 @@ void sbbs_t::show_msgattr(smbmsg_t* msg) ,attr&MSG_NOREPLY ? "NoReply " :nulstr ,poll == MSG_POLL ? "Poll " :nulstr ,poll == MSG_POLL && auxattr&POLL_CLOSED ? "(Closed) " :nulstr - ,auxattr&(MSG_FILEATTACH|MSG_MIMEATTACH) ? "Attach " :nulstr - ,netattr&MSG_SENT ? "Sent " :nulstr - ,netattr&MSG_INTRANSIT ? "InTransit ":nulstr - ,netattr&MSG_KILLSENT ? "KillSent " :nulstr + ); + + char auxattr_str[64]; + safe_snprintf(auxattr_str, sizeof(auxattr_str), "%s%s%s%s%s%s%s" + ,auxattr&MSG_FILEREQUEST? "FileRequest " :nulstr + ,auxattr&MSG_FILEATTACH ? "FileAttach " :nulstr + ,auxattr&MSG_MIMEATTACH ? "MimeAttach " :nulstr + ,auxattr&MSG_KILLFILE ? "KillFile " :nulstr + ,auxattr&MSG_RECEIPTREQ ? "ReceiptReq " :nulstr + ,auxattr&MSG_CONFIRMREQ ? "ConfirmReq " :nulstr + ,auxattr&MSG_NODISP ? "DontDisplay " :nulstr ); + + char netattr_str[64]; + safe_snprintf(netattr_str, sizeof(netattr_str), "%s%s%s%s%s%s%s%s" + ,netattr&MSG_LOCAL ? "Local " :nulstr + ,netattr&MSG_INTRANSIT ? "InTransit " :nulstr + ,netattr&MSG_SENT ? "Sent " :nulstr + ,netattr&MSG_KILLSENT ? "KillSent " :nulstr + ,netattr&MSG_HOLD ? "Hold " :nulstr + ,netattr&MSG_CRASH ? "Crash " :nulstr + ,netattr&MSG_IMMEDIATE ? "Immediate " :nulstr + ,netattr&MSG_DIRECT ? "Direct " :nulstr + ); + + bprintf(text[MsgAttr], attr_str, auxattr_str, netattr_str + ,nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr + ,nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr + ,nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr, nulstr); } /* Returns a CP437 text.dat string converted to UTF-8, when appropriate */ @@ -247,19 +272,19 @@ bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, long p_mode, post_t* post) show_msghdr(smb, msg); + int comments=0; + for(int i = 0; i < msg->total_hfields; i++) + if(msg->hfield[i].type == SMB_COMMENT) { + bprintf("%s\r\n", (char*)msg->hfield_dat[i]); + comments++; + } + if(comments) + CRLF; + if(msg->hdr.type == SMB_MSG_TYPE_POLL && post != NULL && smb->subnum < cfg.total_subs) { char* answer; int longest_answer = 0; - int comments=0; - for(int i = 0; i < msg->total_hfields; i++) - if(msg->hfield[i].type == SMB_COMMENT) { - bprintf("%s\r\n", (char*)msg->hfield_dat[i]); - comments++; - } - if(comments) - CRLF; - for(int i = 0; i < msg->total_hfields; i++) { if(msg->hfield[i].type != SMB_POLL_ANSWER) continue; @@ -307,7 +332,7 @@ bool sbbs_t::show_msg(smb_t* smb, smbmsg_t* msg, long p_mode, post_t* post) mnemonics(text[VoteInThisPollNow]); return true; } - if((txt=smb_getmsgtxt(smb, msg, 0)) == NULL) + if((txt=smb_getmsgtxt(smb, msg, GETMSGTXT_BODY_ONLY)) == NULL) return false; char* p = txt; if(!(console&CON_RAW_IN)) { diff --git a/src/sbbs3/gitinfo.bat b/src/sbbs3/gitinfo.bat new file mode 100644 index 0000000000000000000000000000000000000000..63b9bec24281ecdfdae45c7e7155cf4a95a10f91 --- /dev/null +++ b/src/sbbs3/gitinfo.bat @@ -0,0 +1,4 @@ +@git log -1 HEAD --format="#define GIT_HASH \"%%h\"" > git_hash.h +@echo #define GIT_BRANCH ^"| tr -d "\r\n" > git_branch.h +@git rev-parse --abbrev-ref HEAD | tr -d "\n" >> git_branch.h +@echo ^" >> git_branch.h \ No newline at end of file diff --git a/src/sbbs3/js_system.c b/src/sbbs3/js_system.c index 6dea02881157acfb982f25dd10b6cf18f6bf566d..bd7039212ae503fa3b89a0a971a6b2a175b6785e 100644 --- a/src/sbbs3/js_system.c +++ b/src/sbbs3/js_system.c @@ -21,6 +21,7 @@ #include "sbbs.h" #include "js_request.h" +#include "ver.h" #ifdef JAVASCRIPT @@ -529,6 +530,8 @@ static char* sys_prop_desc[] = { ,"Synchronet version notice (includes version and platform)" ,"Synchronet version number in decimal (e.g. 31301 for v3.13b)" ,"Synchronet version number in hexadecimal (e.g. 0x31301 for v3.13b)" + ,"Synchronet Git repository branch name" + ,"Synchronet Git repository commit hash" ,"platform description (e.g. 'Win32', 'Linux', 'FreeBSD')" ,"architecture description (e.g. 'i386', 'i686', 'x86_64')" ,"message base library version information" @@ -1701,9 +1704,12 @@ js_new_user(JSContext *cx, uintN argc, jsval *arglist) } } if(client!=NULL) { - SAFECOPY(user.modem,client->protocol); - SAFECOPY(user.comp,client->host); - SAFECOPY(user.ipaddr,client->addr); + if(client->protocol != NULL) + SAFECOPY(user.modem,client->protocol); + if(client->host != NULL) + SAFECOPY(user.comp,client->host); + if(client->addr != NULL) + SAFECOPY(user.ipaddr,client->addr); } user.sex=' '; @@ -2472,6 +2478,10 @@ static JSBool js_system_resolve(JSContext *cx, JSObject *obj, jsid id) LAZY_INTEGER("version_num", VERSION_NUM); LAZY_INTEGER("version_hex", VERSION_HEX); + /* Git repo details */ + LAZY_STRING("git_branch", git_branch); + LAZY_STRING("git_hash", git_hash); + LAZY_STRING("platform", PLATFORM_DESC); LAZY_STRING("architecture", ARCHITECTURE_DESC); LAZY_STRFUNC("msgbase_lib", sprintf(str,"SMBLIB %s",smb_lib_ver()), str); diff --git a/src/sbbs3/js_user.c b/src/sbbs3/js_user.c index f961a2b44e0ca449bed7d115ae7d19c3cff1be0f..39900e7d8463f837cc86d7ac8e2fc07a7b22940e 100644 --- a/src/sbbs3/js_user.c +++ b/src/sbbs3/js_user.c @@ -1,7 +1,4 @@ /* Synchronet JavaScript "User" Object */ -// vi: tabstop=4 - -/* $Id: js_user.c,v 1.119 2020/08/11 03:54:58 rswindell Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * @@ -16,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -88,6 +73,9 @@ enum { ,USER_PROP_ETODAY ,USER_PROP_PTODAY ,USER_PROP_MAIL_WAITING + ,USER_PROP_READ_WAITING + ,USER_PROP_UNREAD_WAITING + ,USER_PROP_SPAM_WAITING ,USER_PROP_MAIL_PENDING ,USER_PROP_ULB ,USER_PROP_ULS @@ -391,7 +379,16 @@ static JSBool js_user_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp) val=scfg->level_freecdtperday[p->user->level]; break; case USER_PROP_MAIL_WAITING: - val=getmail(scfg,p->user->number,/* sent? */FALSE, /* SPAM: */FALSE); + val=getmail(scfg,p->user->number,/* sent? */FALSE, /* attr: */0); + break; + case USER_PROP_READ_WAITING: + val=getmail(scfg,p->user->number,/* sent? */FALSE, /* attr: */MSG_READ); + break; + case USER_PROP_UNREAD_WAITING: + val=getmail(scfg,p->user->number,/* sent? */FALSE, /* attr: */~MSG_READ); + break; + case USER_PROP_SPAM_WAITING: + val=getmail(scfg,p->user->number,/* sent? */FALSE, /* attr: */MSG_SPAM); break; case USER_PROP_MAIL_PENDING: val=getmail(scfg,p->user->number,/* sent? */TRUE, /* SPAM: */FALSE); @@ -960,6 +957,9 @@ static jsSyncPropertySpec js_user_stats_properties[] = { { "files_downloaded" ,USER_PROP_DLS ,USER_PROP_FLAGS, 310 }, { "leech_attempts" ,USER_PROP_LEECH ,USER_PROP_FLAGS, 310 }, { "mail_waiting" ,USER_PROP_MAIL_WAITING ,USER_PROP_FLAGS, 312 }, + { "read_mail_waiting" ,USER_PROP_READ_WAITING ,USER_PROP_FLAGS, 31802 }, + { "unread_mail_waiting",USER_PROP_UNREAD_WAITING,USER_PROP_FLAGS, 31802 }, + { "spam_waiting" ,USER_PROP_SPAM_WAITING ,USER_PROP_FLAGS, 31802 }, { "mail_pending" ,USER_PROP_MAIL_PENDING ,USER_PROP_FLAGS, 312 }, {0} }; @@ -984,7 +984,10 @@ static char* user_stats_prop_desc[] = { ,"total bytes downloaded" ,"total files downloaded" ,"suspected leech downloads" - ,"number of e-mail messages currently waiting" + ,"total number of e-mail messages currently waiting in inbox" + ,"number of read e-mail messages currently waiting in inbox" + ,"number of unread e-mail messages currently waiting in inbox" + ,"number of SPAM e-mail messages currently waiting in inbox" ,"number of e-mail messages sent, currently pending deletion" ,NULL }; diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c index b90523cb51015b376861573b241a2b87d8e93cd3..90fbb8ae3f1ba61828af54740eded516379336a8 100644 --- a/src/sbbs3/mailsrvr.c +++ b/src/sbbs3/mailsrvr.c @@ -45,6 +45,7 @@ #include "multisock.h" #include "ssl.h" #include "cryptlib.h" +#include "ver.h" /* Constants */ static const char* server_name="Synchronet Mail Server"; @@ -84,7 +85,6 @@ static volatile BOOL sendmail_running=FALSE; static volatile BOOL terminate_server=FALSE; static volatile BOOL terminate_sendmail=FALSE; static sem_t sendmail_wakeup_sem; -static char revision[16]; static volatile time_t uptime; static str_list_t recycle_semfiles; static str_list_t shutdown_semfiles; @@ -1176,8 +1176,8 @@ static void pop3_thread(void* arg) safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>" ,rand(),socket,(ulong)time(NULL),(ulong)clock(), server_host_name()); - sockprintf(socket,client.protocol,session,"+OK Synchronet %s Server %s-%s Ready %s" - ,client.protocol, revision,PLATFORM_DESC,challenge); + sockprintf(socket,client.protocol,session,"+OK Synchronet %s Server %s%c-%s Ready %s" + ,client.protocol, VERSION, REVISION, PLATFORM_DESC, challenge); /* Requires USER or APOP command first */ for(i=5;i;i--) { @@ -1192,7 +1192,7 @@ static void pop3_thread(void* arg) else if (!stricmp(buf, "CAPA")) { // Capabilities sockprintf(socket,client.protocol,session, "+OK Capability list follows"); - sockprintf(socket,client.protocol,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s-%s\r\n%s.", revision, PLATFORM_DESC, (session != -1 || get_ssl_cert(&scfg, NULL, NULL) == -1) ? "" : "STLS\r\n"); + sockprintf(socket,client.protocol,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s%c-%s\r\n%s.", VERSION, REVISION, PLATFORM_DESC, (session != -1 || get_ssl_cert(&scfg, NULL, NULL) == -1) ? "" : "STLS\r\n"); i++; } else if (!stricmp(buf, "STLS")) { @@ -1408,7 +1408,7 @@ static void pop3_thread(void* arg) if(!stricmp(buf, "CAPA")) { // Capabilities sockprintf(socket,client.protocol,session, "+OK Capability list follows"); - sockprintf(socket,client.protocol,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s-%s\r\n.", revision, PLATFORM_DESC); + sockprintf(socket,client.protocol,session, "TOP\r\nUSER\r\nPIPELINING\r\nUIDL\r\nIMPLEMENTATION Synchronet POP3 Server %s%c-%s\r\n.", VERSION, REVISION, PLATFORM_DESC); continue; } if(!stricmp(buf, "QUIT")) { @@ -3209,8 +3209,8 @@ static void smtp_thread(void* arg) /* SMTP session active: */ - sockprintf(socket,client.protocol,session,"220 %s Synchronet %s Server %s-%s Ready" - ,server_host_name(), client.protocol, revision, PLATFORM_DESC); + sockprintf(socket,client.protocol,session,"220 %s Synchronet %s Server %s%c-%s Ready" + ,server_host_name(), client.protocol, VERSION, REVISION, PLATFORM_DESC); while(1) { rd = sockreadline(socket, client.protocol, session, buf, sizeof(buf)); if(rd<0) @@ -3928,7 +3928,7 @@ static void smtp_thread(void* arg) snprintf(hdrfield,sizeof(hdrfield), "from %s (%s [%s%s])\r\n" - " by %s [%s%s] (%s %s-%s) with %s\r\n" + " by %s [%s%s] (%s %s%c-%s) with %s\r\n" " for %s; %s\r\n" " (envelope-from %s)" ,host_name,hello_name @@ -3938,7 +3938,7 @@ static void smtp_thread(void* arg) ,server_addr.addr.sa_family==AF_INET6?"IPv6: ":"" ,server_ip ,server_name - ,revision,PLATFORM_DESC + ,VERSION, REVISION, PLATFORM_DESC ,with_clauses[with_val] ,forward_path,msgdate(msg.hdr.when_imported,date) ,reverse_path); @@ -6007,18 +6007,16 @@ const char* DLLCALL mail_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.735 $", "%*s %s", revision); - - sprintf(ver,"%s %s%s SMBLIB %s " - "Compiled %s %s with %s" + sprintf(ver,"%s %s%c%s " + "Compiled %s/%s %s %s with %s" ,server_name - ,revision + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else ,"" #endif - ,smb_lib_ver() + ,git_branch, git_hash ,__DATE__, __TIME__, compiler ); @@ -6049,8 +6047,6 @@ void DLLCALL mail_server(void* arg) char* servprot = "N/A"; CRYPT_SESSION session = -1; - mail_ver(); - startup=(mail_startup_t*)arg; #ifdef _THREAD_SUID_BROKEN @@ -6073,7 +6069,7 @@ void DLLCALL mail_server(void* arg) } ZERO_VAR(js_server_props); - SAFEPRINTF2(js_server_props.version,"%s %s",server_name,revision); + SAFEPRINTF3(js_server_props.version,"%s %s%c",server_name, VERSION, REVISION); js_server_props.version_detail=mail_ver(); js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; @@ -6113,9 +6109,9 @@ void DLLCALL mail_server(void* arg) memset(&scfg, 0, sizeof(scfg)); - lprintf(LOG_INFO,"%s Revision %s%s" + lprintf(LOG_INFO,"%s Version %s%c%s" ,server_name - ,revision + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else @@ -6125,9 +6121,7 @@ void DLLCALL mail_server(void* arg) DESCRIBE_COMPILER(compiler); - lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler); - - lprintf(LOG_DEBUG,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff); + lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", git_branch, git_hash, __DATE__, __TIME__, compiler); sbbs_srand(); diff --git a/src/sbbs3/mailsrvr.vcxproj b/src/sbbs3/mailsrvr.vcxproj index c4de8b7065f48a6f581ca237857fab69464fabc8..76235c4c64b250c992d461c3d2cb08a21f1ec27a 100644 --- a/src/sbbs3/mailsrvr.vcxproj +++ b/src/sbbs3/mailsrvr.vcxproj @@ -191,6 +191,7 @@ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> <ClCompile Include="nopen.c" /> + <ClCompile Include="ver.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\xpdev\xpdev_mt.vcxproj"> diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp index ddd9e1e2b057235b58233e4f97bad63e3fedb6a3..d8b5cf5124e6179e61355285244905e70caf6bfc 100644 --- a/src/sbbs3/main.cpp +++ b/src/sbbs3/main.cpp @@ -27,6 +27,7 @@ #include "js_rtpool.h" #include "js_request.h" #include "ssl.h" +#include "ver.h" #include <multisock.h> #include <limits.h> // HOST_NAME_MAX @@ -1289,7 +1290,7 @@ JSContext* sbbs_t::js_init(JSRuntime** runtime, JSObject** glob, const char* des ,uptime, server_host_name(), SOCKLIB_DESC /* system */ ,&js_callback /* js */ ,&startup->js - ,&client, client_socket, -1 /* client */ + ,client_socket == INVALID_SOCKET ? NULL : &client, client_socket, -1 /* client */ ,&js_server_props /* server */ ,glob )) @@ -3532,6 +3533,19 @@ bool sbbs_t::init() } inet_addrtop(&addr, local_addr, sizeof(local_addr)); inet_addrtop(&client_addr, client_ipaddr, sizeof(client_ipaddr)); + SAFEPRINTF(str, "%sclient.ini", cfg.node_dir); + FILE* fp = fopen(str, "wt"); + if(fp != NULL) { + fprintf(fp, "sock=%d\n", client_socket); + fprintf(fp, "addr=%s\n", client.addr); + fprintf(fp, "host=%s\n", client.host); + fprintf(fp, "port=%u\n", (uint)client.port); + fprintf(fp, "time=%lu\n", (ulong)client.time); + fprintf(fp, "prot=%s\n", client.protocol); + fprintf(fp, "local_addr=%s\n", local_addr); + fprintf(fp, "local_port=%u\n", (uint)inet_addrport(&addr)); + fclose(fp); + } lprintf(LOG_INFO,"socket %u attached to local interface %s port %u" ,client_socket, local_addr, inet_addrport(&addr)); spymsg("Connected"); @@ -4453,7 +4467,7 @@ void sbbs_t::logoffstats() void node_thread(void* arg) { - char str[128]; + char str[MAX_PATH + 1]; int file; uint curshell=0; node_t node; @@ -4554,6 +4568,15 @@ void node_thread(void* arg) sbbs->logout(); sbbs->logoffstats(); /* Updates both system and node dsts.dab files */ + SAFEPRINTF(str, "%sclient.ini", sbbs->cfg.node_dir); + FILE* fp = fopen(str, "at"); + if(fp != NULL) { + fprintf(fp, "user=%u\n", sbbs->useron.number); + fprintf(fp, "name=%s\n", sbbs->useron.alias); + fprintf(fp, "done=%lu\n", (ulong)time(NULL)); + fclose(fp); + } + if(sbbs->sys_status&SS_DAILY) { // New day, run daily events/maintenance sbbs->daily_maint(); } @@ -4877,7 +4900,7 @@ const char* DLLCALL bbs_ver(void) if(ver[0]==0) { /* uninitialized */ DESCRIBE_COMPILER(compiler); - safe_snprintf(ver,sizeof(ver),"%s %s%c%s SMBLIB %s Compiled %s %s with %s" + safe_snprintf(ver,sizeof(ver),"%s %s%c%s Compiled %s/%s %s %s with %s" ,TELNET_SERVER ,VERSION, REVISION #ifdef _DEBUG @@ -4885,7 +4908,7 @@ const char* DLLCALL bbs_ver(void) #else ,"" #endif - ,smb_lib_ver() + ,git_branch, git_hash ,__DATE__, __TIME__, compiler ); } @@ -5048,18 +5071,17 @@ void DLLCALL bbs_thread(void* arg) char compiler[32]; DESCRIBE_COMPILER(compiler); - lprintf(LOG_INFO,"%s Version %s Revision %c%s" + lprintf(LOG_INFO,"%s Version %s%c%s" ,TELNET_SERVER ,VERSION - ,toupper(REVISION) + ,REVISION #ifdef _DEBUG ," Debug" #else ,"" #endif ); - lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler); - lprintf(LOG_DEBUG,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff); + lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", git_branch, git_hash, __DATE__, __TIME__, compiler); #ifdef _DEBUG lprintf(LOG_DEBUG, "sizeof: int=%d, long=%d, off_t=%d, time_t=%d" diff --git a/src/sbbs3/msg_id.c b/src/sbbs3/msg_id.c index 26a5ad216f71fae427d8f7a1c3eeb90356dd9702..6dc987f255c29ee227a9cb647f54a1d810de63d7 100644 --- a/src/sbbs3/msg_id.c +++ b/src/sbbs3/msg_id.c @@ -21,6 +21,7 @@ #include "msg_id.h" #include "smblib.h" +#include "ver.h" static ulong msg_number(smbmsg_t* msg) { @@ -242,8 +243,9 @@ char* DLLCALL msg_program_id(char* pid, size_t maxlen) char compiler[64]; DESCRIBE_COMPILER(compiler); - snprintf(pid, maxlen, "%.10s %s%c-%s %s %s" + snprintf(pid, maxlen, "%.10s %s%c-%s %s/%s %s %s" ,VERSION_NOTICE,VERSION,REVISION,PLATFORM_DESC + ,git_branch, git_hash ,__DATE__,compiler); return pid; } diff --git a/src/sbbs3/netmail.cpp b/src/sbbs3/netmail.cpp index eab9d211cddb2a309b1e3123f133de1841ad7a84..457ca30eaa02980ff84a712be10c3b0bf248d2c4 100644 --- a/src/sbbs3/netmail.cpp +++ b/src/sbbs3/netmail.cpp @@ -480,7 +480,7 @@ void sbbs_t::qwktonetmail(FILE *rep, char *block, char *into, uchar fromhub) l+=strlen(str)+1; cp=str; while(*cp && *cp<=' ') cp++; - sprintf(senderaddr,"%s/%s",sender_id,cp); + safe_snprintf(senderaddr, sizeof(senderaddr), "%s/%s",sender_id,cp); strupr(senderaddr); smb_hfield(&msg,SENDERNETADDR,strlen(senderaddr),senderaddr); } diff --git a/src/sbbs3/objects.mk b/src/sbbs3/objects.mk index 1b4850a8924906d03b5546c72c2d99ce09adb823..e88c6d743488d9e3a29ff2054eb75978f297d2d5 100644 --- a/src/sbbs3/objects.mk +++ b/src/sbbs3/objects.mk @@ -174,6 +174,7 @@ SMBUTIL_OBJS = \ SBBSECHO_OBJS = \ $(OBJODIR)$(DIRSEP)sbbsecho$(OFILE) \ + $(OBJODIR)$(DIRSEP)ver$(OFILE) \ $(OBJODIR)$(DIRSEP)ars$(OFILE) \ $(OBJODIR)$(DIRSEP)date_str$(OFILE) \ $(OBJODIR)$(DIRSEP)load_cfg$(OFILE) \ diff --git a/src/sbbs3/prntfile.cpp b/src/sbbs3/prntfile.cpp index 9419fcd00f810fd7604c905f0a31934eae41a4f1..d20aaa0d35afa4e21c7aa30e315e5e7355116a12 100644 --- a/src/sbbs3/prntfile.cpp +++ b/src/sbbs3/prntfile.cpp @@ -84,10 +84,6 @@ bool sbbs_t::printfile(const char* fname, long mode, long org_cols, JSObject* ob sys_status&=~SS_ABORT; } - if(!(mode&P_NOCRLF) && row > 0 && !rip) { - newline(); - } - if((stream=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) { if(!(mode&P_NOERROR)) { lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s" @@ -109,6 +105,10 @@ bool sbbs_t::printfile(const char* fname, long mode, long org_cols, JSObject* ob return true; } + if(!(mode&P_NOCRLF) && row > 0 && !rip) { + newline(); + } + if((mode&P_OPENCLOSE) && length <= PRINTFILE_MAX_FILE_LEN) { if((buf=(char*)malloc(length+1L))==NULL) { fclose(stream); @@ -196,9 +196,6 @@ bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols, J } sys_status&=~SS_ABORT; } - if(!(mode&P_NOCRLF) && row > 0) { - newline(); - } if((fp=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) { if(!(mode&P_NOERROR)) { lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s" @@ -209,6 +206,9 @@ bool sbbs_t::printtail(const char* fname, int lines, long mode, long org_cols, J } return false; } + if(!(mode&P_NOCRLF) && row > 0) { + newline(); + } length=(long)filelength(file); if(length<0) { fclose(fp); diff --git a/src/sbbs3/readmail.cpp b/src/sbbs3/readmail.cpp index eb0dbfbacd38d07c5dc56540ea4c9c767106461b..92c437fb6a1f4b14ee17f4d81b5f2859a3a0f55d 100644 --- a/src/sbbs3/readmail.cpp +++ b/src/sbbs3/readmail.cpp @@ -1,9 +1,5 @@ -/* readmail.cpp */ - /* Synchronet private mail reading function */ -/* $Id: readmail.cpp,v 1.101 2020/05/11 05:01:01 rswindell Exp $ */ - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * @@ -17,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -435,24 +419,20 @@ void sbbs_t::readmail(uint usernumber, int which, long lm_mode) case 'F': /* Forward last piece */ domsg=0; bputs(text[ForwardMailTo]); - if(!getstr(str,LEN_ALIAS,cfg.uq&UQ_NOUPRLWR ? K_NONE:K_UPRLWR)) + if(!getstr(str, sizeof(str) - 1, K_TRIM)) break; - i=finduser(str); - if(!i) + smb_getmsgidx(&smb,&msg); + if(!forwardmail(&msg, str)) break; domsg=1; if(smb.curmsg<smb.msgs-1) smb.curmsg++; else done=1; - smb_getmsgidx(&smb,&msg); - forwardmail(&msg,i); - if(msg.hdr.attr&MSG_PERMANENT) + if(msg.hdr.attr&(MSG_PERMANENT | MSG_DELETE)) break; SAFEPRINTF(str2,text[DeleteMailQ],msghdr_field(&msg, msg.from)); if(!yesno(str2)) break; - if(msg.total_hfields) - smb_freemsgmem(&msg); - msg.total_hfields=0; + smb_freemsgmem(&msg); msg.idx.offset=0; if(smb_locksmbhdr(&smb)==SMB_SUCCESS) { /* Lock the entire base */ if(loadmsg(&msg,msg.idx.number) >= 0) { @@ -465,7 +445,6 @@ void sbbs_t::readmail(uint usernumber, int which, long lm_mode) } smb_unlocksmbhdr(&smb); } - break; case 'H': domsg=0; diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h index 5b754d2c953907161f05aeddcfc17d2100aa026a..f12b8945a5999e4f2eb26f0d5ed7743e1b2af428 100644 --- a/src/sbbs3/sbbs.h +++ b/src/sbbs3/sbbs.h @@ -623,7 +623,7 @@ public: void reset_logon_vars(void); - uint finduser(char *str, bool silent_failure = false); + uint finduser(const char* str, bool silent_failure = false); int sub_op(uint subnum); @@ -681,7 +681,7 @@ public: bool msgabort(void); bool email(int usernumber, const char *top = NULL, const char *title = NULL , long mode = WM_NONE, smb_t* resmb = NULL, smbmsg_t* remsg = NULL); - void forwardmail(smbmsg_t* msg, int usernum); + bool forwardmail(smbmsg_t* msg, const char* to, const char* subject = NULL, const char* comment = NULL); void removeline(char *str, char *str2, char num, char skip); ulong msgeditor(char *buf, const char *top, char *title); bool editfile(char *path, bool msg=false); @@ -690,7 +690,7 @@ public: void editmsg(smbmsg_t* msg, uint subnum); void editor_inf(int xeditnum, const char *to, const char* from, const char *subj, long mode ,uint subnum, const char* tagfile); - void copyfattach(uint to, uint from, char *title); + bool copyfattach(uint to, uint from, const char* subj); bool movemsg(smbmsg_t* msg, uint subnum); int process_edited_text(char* buf, FILE* stream, long mode, unsigned* lines, unsigned maxlines); int process_edited_file(const char* src, const char* dest, long mode, unsigned* lines, unsigned maxlines); @@ -759,7 +759,7 @@ public: int outchar(enum unicode_codepoint, const char* cp437_fallback = NULL); void inc_row(int count); void inc_column(int count); - void center(char *str, unsigned int columns = 0); + void center(const char *str, unsigned int columns = 0); void wide(const char*); void clearscreen(long term); void clearline(void); @@ -1439,12 +1439,6 @@ extern char lastuseron[LEN_ALIAS+1]; /* Name of user last online */ } #endif -extern -#ifdef __cplusplus - "C" -#endif - const char* beta_version; - /* Global data */ /* ToDo: These should be hunted down and killed */ diff --git a/src/sbbs3/sbbs.vcxproj b/src/sbbs3/sbbs.vcxproj index e2f04731b4d8d16f830318bcecaaf25e379b4b9f..d71e501bee654dd794ee3e3d579668f54195658f 100644 --- a/src/sbbs3/sbbs.vcxproj +++ b/src/sbbs3/sbbs.vcxproj @@ -114,6 +114,9 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <OutputFile>.\msvc.win32.dll.debug/sbbs.bsc</OutputFile> </Bscmake> + <PreBuildEvent> + <Command>gitinfo.bat</Command> + </PreBuildEvent> </ItemDefinitionGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <Midl> @@ -163,6 +166,9 @@ <SuppressStartupBanner>true</SuppressStartupBanner> <OutputFile>.\msvc.win32.dll.release/sbbs.bsc</OutputFile> </Bscmake> + <PreBuildEvent> + <Command>gitinfo.bat</Command> + </PreBuildEvent> </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\comio\comio.c" /> diff --git a/src/sbbs3/sbbscon.c b/src/sbbs3/sbbscon.c index 205c69dff3798fc8693abc811bbd73fffc0914c6..4741e715a84c466a3654e1788a5e9657bf801950 100644 --- a/src/sbbs3/sbbscon.c +++ b/src/sbbs3/sbbscon.c @@ -1,8 +1,5 @@ /* Synchronet vanilla/console-mode "front-end" */ -/* $Id: sbbscon.c,v 1.282 2020/08/17 00:48:28 rswindell Exp $ */ -// vi: tabstop=4 - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * @@ -16,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -55,6 +40,7 @@ #include "ftpsrvr.h" /* ftp_startup_t, ftp_server */ #include "mailsrvr.h" /* mail_startup_t, mail_server */ #include "services.h" /* services_startup_t, services_thread */ +#include "ver.h" /* XPDEV headers */ #include "conwrap.h" /* kbhit/getch */ @@ -1570,8 +1556,6 @@ int main(int argc, char** argv) continue; } if(stricmp(arg, "version") == 0) { - char revision[16]; - sscanf("$Revision: 1.282 $", "%*s %s", revision); char compiler[32]; DESCRIBE_COMPILER(compiler); printf("%s\n", bbs_ver()); @@ -1579,13 +1563,14 @@ int main(int argc, char** argv) printf("%s\n", ftp_ver()); printf("%s\n", web_ver()); printf("%s\n", services_ver()); - printf("Synchronet Console %s%s Compiled %s %s with %s\n" - ,revision + printf("Synchronet Console %s%c%s Compiled %s/%s %s %s with %s\n" + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else ,"" #endif + ,git_branch, git_hash ,__DATE__, __TIME__, compiler); return EXIT_SUCCESS; } diff --git a/src/sbbs3/sbbscon.vcxproj b/src/sbbs3/sbbscon.vcxproj index 1f7ae09789f045d0b67dc576cfb2d9d94d1d6a5f..68e559e5c2726fc7e2509e66cdb0787b5c16a543 100644 --- a/src/sbbs3/sbbscon.vcxproj +++ b/src/sbbs3/sbbscon.vcxproj @@ -159,6 +159,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="ver.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\xpdev\xpdev_mt.vcxproj"> diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c index 83ee49abc685ba263123500d9c0383467c5180d7..ef34fff7211754133e0cbcaa9d9c1c7999b8541c 100644 --- a/src/sbbs3/sbbsecho.c +++ b/src/sbbs3/sbbsecho.c @@ -53,6 +53,7 @@ #include "msg_id.h" #include "scfgsave.h" #include "getmail.h" +#include "ver.h" #define MAX_OPEN_SMBS 10 @@ -91,7 +92,6 @@ str_list_t bad_areas; fidoaddr_t sys_faddr = {1,1,1,0}; /* Default system address: 1:1/1.0 */ sbbsecho_cfg_t cfg; scfg_t scfg; -char revision[16]; char compiler[32]; bool pause_on_exit=false; @@ -116,8 +116,8 @@ const char* sbbsecho_pid(void) { static char str[256]; - sprintf(str, "SBBSecho %u.%02u-%s r%s %s %s" - ,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,revision,__DATE__,compiler); + sprintf(str, "SBBSecho %u.%02u-%s %s/%s %s %s" + ,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,git_branch,git_hash,__DATE__,compiler); return str; } @@ -198,7 +198,7 @@ int fwrite_via_control_line(FILE* fp, fidoaddr_t* addr) time_t t = time(NULL); struct tm* tm = gmtime(&t); return fprintf(fp,"\1Via %s @%04u%02u%02u.%02u%02u%02u.UTC " - "SBBSecho %u.%02u-%s r%s\r" + "SBBSecho %u.%02u-%s %s/%s\r" ,smb_faddrtoa(addr, NULL) ,tm->tm_year+1900 ,tm->tm_mon+1 @@ -206,7 +206,7 @@ int fwrite_via_control_line(FILE* fp, fidoaddr_t* addr) ,tm->tm_hour ,tm->tm_min ,tm->tm_sec - ,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,revision); + ,SBBSECHO_VERSION_MAJOR,SBBSECHO_VERSION_MINOR,PLATFORM_DESC,git_branch,git_hash); } int fwrite_intl_control_line(FILE* fp, fmsghdr_t* hdr) @@ -1262,10 +1262,6 @@ int create_netmail(const char *to, const smbmsg_t* msg, const char *subject, con fprintf(fp, "\1CHRS: %s\r", charset); if(msg->editor != NULL) fprintf(fp, "\1NOTE: %s\r", msg->editor); - /* comment headers are part of text */ - for(i=0; i<msg->total_hfields; i++) - if(msg->hfield[i].type == SMB_COMMENT) - fprintf(fp, "%s\r", (char*)msg->hfield_dat[i]); if(subject != msg->subj) fprintf(fp, "Subject: %s\r\r", msg->subj); } @@ -5142,7 +5138,7 @@ bool retoss_bad_echomail(void) continue; } - char* body = smb_getmsgtxt(&badsmb, &badmsg, GETMSGTXT_BODY_ONLY); + char* body = smb_getmsgtxt(&badsmb, &badmsg, GETMSGTXT_NO_TAILS); if(body == NULL) { smb_unlockmsghdr(&badsmb,&badmsg); smb_freemsgmem(&badmsg); @@ -6122,14 +6118,12 @@ int main(int argc, char **argv) memset(&smb[i],0,sizeof(smb_t)); memset(&cfg,0,sizeof(cfg)); - sscanf("$Revision: 3.179 $", "%*s %s", revision); - DESCRIBE_COMPILER(compiler); - printf("\nSBBSecho v%u.%02u-%s (rev %s) - Synchronet FidoNet EchoMail Tosser\n" + printf("\nSBBSecho v%u.%02u-%s (%s/%s) - Synchronet FidoNet EchoMail Tosser\n" ,SBBSECHO_VERSION_MAJOR, SBBSECHO_VERSION_MINOR ,PLATFORM_DESC - ,revision + ,git_branch, git_hash ); cmdline[0]=0; diff --git a/src/sbbs3/sbbsecho.vcxproj b/src/sbbs3/sbbsecho.vcxproj index 9ddf9f7968d976c22da77f01a2a2004b6b9ebd0d..d78e6e0ad913ae0169c2b043cdc79db4b29eba20 100644 --- a/src/sbbs3/sbbsecho.vcxproj +++ b/src/sbbs3/sbbsecho.vcxproj @@ -187,6 +187,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="ver.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\smblib\smblib.vcxproj"> diff --git a/src/sbbs3/scfg/scfgsub.c b/src/sbbs3/scfg/scfgsub.c index c4e5663c98da6f23f84def6232cc67b0b8efad66..5e14e49d0c6072b8ec26171cfe8adca09343e222 100644 --- a/src/sbbs3/scfg/scfgsub.c +++ b/src/sbbs3/scfg/scfgsub.c @@ -166,6 +166,7 @@ void sub_cfg(uint grpnum) if(uifc.changes && cfg.grp[grpnum]->sort) sort_subs(grpnum); int maxlen = 0; + bool template_shown = false; for(i=0,j=0;i<cfg.total_subs && j<MAX_OPTS;i++) if(cfg.sub[i]->grp==grpnum) { subnum[j]=i; @@ -187,7 +188,9 @@ void sub_cfg(uint grpnum) default: /* Defeat stupid GCC warning */ break; } - sprintf(str, "%-*s %c", name_len, name, cfg.sub[i]->misc&SUB_TEMPLATE ? '*' : ' '); + sprintf(str, "%-*s %c", name_len, name, (cfg.sub[i]->misc&SUB_TEMPLATE && !template_shown) ? '*' : ' '); + if(cfg.sub[i]->misc&SUB_TEMPLATE) + template_shown = true; truncsp(str); len += sprintf(opt[j] + strlen(opt[j]), "%s", str); if(len > maxlen) @@ -1370,8 +1373,10 @@ void sub_cfg(uint grpnum) } opt[n][0]=0; n = uifc.list(WIN_RHT|WIN_SAV|WIN_ACT|WIN_INSACT, 0, 0, 0, &k, NULL, "FidoNet Address", opt); - if(n >= 0 && n < cfg.total_faddrs) + if(n >= 0 && n < cfg.total_faddrs) { cfg.sub[i]->faddr = cfg.faddr[n]; + uifc.changes = TRUE; + } break; } case 8: diff --git a/src/sbbs3/scfg/scfgxfr2.c b/src/sbbs3/scfg/scfgxfr2.c index c85037852b64966b5001d61f0ecbe16be11350ed..380df8906906bf6b9cb0f0e7254ad41d49f891a7 100644 --- a/src/sbbs3/scfg/scfgxfr2.c +++ b/src/sbbs3/scfg/scfgxfr2.c @@ -979,6 +979,7 @@ void dir_cfg(uint libnum) if(uifc.changes && cfg.lib[libnum]->sort) sort_dirs(libnum); int maxlen = 0; + bool template_shown = false; for(i=0,j=0;i<cfg.total_dirs && j<MAX_OPTS;i++) { if(cfg.dir[i]->lib != libnum) continue; @@ -996,7 +997,9 @@ void dir_cfg(uint libnum) default: /* Defeat stupid GCC warning */ break; } - sprintf(str, "%-*s %c", name_len, name, cfg.dir[i]->misc&DIR_TEMPLATE ? '*' : ' '); + sprintf(str, "%-*s %c", name_len, name, (cfg.dir[i]->misc&DIR_TEMPLATE && !template_shown) ? '*' : ' '); + if(cfg.dir[i]->misc&DIR_TEMPLATE) + template_shown = true; truncsp(str); int len = sprintf(opt[j], "%s", str); if(len > maxlen) diff --git a/src/sbbs3/scfg/scfgxtrn.c b/src/sbbs3/scfg/scfgxtrn.c index 6f788b5861392407f6a41d8dce17e2e88222450f..5bca78ce45b573eda7019641b120626c6e17bc28 100644 --- a/src/sbbs3/scfg/scfgxtrn.c +++ b/src/sbbs3/scfg/scfgxtrn.c @@ -2242,7 +2242,7 @@ int natvpgm_cfg() void xtrnsec_cfg() { - static int xtrnsec_dflt,xtrnsec_opt; + static int xtrnsec_dflt,xtrnsec_bar,xtrnsec_opt; char str[128],code[128],done=0; int j,k; uint i; @@ -2271,7 +2271,7 @@ void xtrnsec_cfg() "\n" "To configure an online program section, select it and hit ~ ENTER ~.\n" ; - i=uifc.list(j,0,0,45,&xtrnsec_dflt,0,"Online Program Sections",opt); + i=uifc.list(j,0,0,45,&xtrnsec_dflt,&xtrnsec_bar,"Online Program Sections",opt); if((signed)i==-1) return; int msk = i & MSK_ON; diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c index bf6ef2e11452d58f12f18db9a73851e0140c3923..8f99438eb9c8af4c5ca6317735d03f011908bfd4 100644 --- a/src/sbbs3/services.c +++ b/src/sbbs3/services.c @@ -47,6 +47,7 @@ #include "js_socket.h" #include "multisock.h" #include "ssl.h" +#include "ver.h" /* Constants */ @@ -59,7 +60,6 @@ static char* text[TOTAL_TEXT]; static volatile BOOL terminated=FALSE; static time_t uptime=0; static ulong served=0; -static char revision[16]; static str_list_t recycle_semfiles; static str_list_t shutdown_semfiles; static protected_uint32_t threads_pending_start; @@ -817,8 +817,8 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, break; if(service_client->service->js_server_props.version[0]==0) { - SAFEPRINTF(service_client->service->js_server_props.version - ,"Synchronet Services %s",revision); + SAFEPRINTF2(service_client->service->js_server_props.version + ,"Synchronet Services %s%c", VERSION, REVISION); service_client->service->js_server_props.version_detail= services_ver(); service_client->service->js_server_props.clients= @@ -1661,16 +1661,15 @@ const char* DLLCALL services_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.336 $", "%*s %s", revision); - - sprintf(ver,"Synchronet Services %s%s " - "Compiled %s %s with %s" - ,revision + sprintf(ver,"Synchronet Services %s%c%s " + "Compiled %s/%s %s %s with %s" + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else ,"" #endif + ,git_branch, git_hash ,__DATE__, __TIME__, compiler ); @@ -1735,8 +1734,6 @@ void DLLCALL services_thread(void* arg) int level; BOOL need_cert = FALSE; - services_ver(); - startup=(services_startup_t*)arg; if(startup==NULL) { @@ -1776,8 +1773,8 @@ void DLLCALL services_thread(void* arg) memset(&scfg, 0, sizeof(scfg)); - lprintf(LOG_INFO,"Synchronet Services Revision %s%s" - ,revision + lprintf(LOG_INFO,"Synchronet Services Version %s%c%s" + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else @@ -1787,7 +1784,7 @@ void DLLCALL services_thread(void* arg) DESCRIBE_COMPILER(compiler); - lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler); + lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", git_branch, git_hash, __DATE__, __TIME__, compiler); protected_uint32_init(&threads_pending_start,0); diff --git a/src/sbbs3/services.vcxproj b/src/sbbs3/services.vcxproj index b84a464865002755a5cb4da625dedd9ded129b70..57beac0b879c45d42777eafddc1313bed0a962f5 100644 --- a/src/sbbs3/services.vcxproj +++ b/src/sbbs3/services.vcxproj @@ -176,6 +176,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="ver.cpp" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\xpdev\xpdev_mt.vcxproj"> diff --git a/src/sbbs3/sexyz.c b/src/sbbs3/sexyz.c old mode 100644 new mode 100755 index 67e2339efbcf097f9f14d4d0ab9c0d4f3f34f0f2..92fe2eaa2d9f58cd1626addb4c1852a2fbdfe93c --- a/src/sbbs3/sexyz.c +++ b/src/sbbs3/sexyz.c @@ -18,7 +18,7 @@ * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ -/* +/* * ZMODEM code based on zmtx/zmrx v1.02 (C) Mattheij Computer Service 1994 * by Jacques Mattheij * @@ -87,7 +87,7 @@ /* Global Vars */ /***************/ long mode=0; /* Program mode */ -long zmode=0L; /* Zmodem mode */ +long zmode=0L; /* ZMODEM mode */ uchar block[XMODEM_MAX_BLOCK_SIZE]; /* Block buffer */ ulong block_num; /* Block number */ char* dszlog; @@ -106,7 +106,7 @@ FILE* errfp; FILE* statfp; FILE* logfp=NULL; -char revision[16]; +const char* revision = "3.0"; SOCKET sock=INVALID_SOCKET; @@ -604,7 +604,7 @@ int send_byte(void* unused, uchar ch, unsigned timeout) fprintf(statfp,"\b\b\b\b \b\b\b\b"); if(result!=WAIT_OBJECT_0) { lprintf(LOG_WARNING - ,"!TIMEOUT (%d) waiting for output buffer to flush (%u seconds, %u bytes)\n" + ,"TIMEOUT (%d) waiting for output buffer to flush (%u seconds, %u bytes)" ,result, timeout, RingBufFull(&outbuf)); fprintf(statfp ,"\n!TIMEOUT (%d) waiting for output buffer to flush (%u seconds, %u bytes)\n" @@ -972,7 +972,7 @@ static int send_files(char** fname, uint fnames) if(mode&ZMODEM) success=zmodem_send_file(&zm, path, fp, /* ZRQINIT? */fnum==0, &startfile, &sent_bytes); - else /* X/Ymodem */ + else /* X/YMODEM */ success=xmodem_send_file(&xm, path, fp, &startfile, &sent_bytes); fclose(fp); @@ -1138,7 +1138,7 @@ static int receive_files(char** fname_list, int fnames) lprintf(LOG_DEBUG,"YMODEM header (%u fields): %s", i, block+strlen((char*)block)+1); SAFECOPY(fname,(char*)block); - } else { /* Zmodem */ + } else { /* ZMODEM */ lprintf(LOG_INFO,"Waiting for ZMODEM sender..."); i=zmodem_recv_init(&zm); @@ -1415,17 +1415,18 @@ static const char* usage= #endif "\n" "opts = -y allow overwriting of existing files when receiving\n" - " -o disable Zmodem CRC-32 mode (use CRC-16)\n" - " -s disable Zmodem streaming (Slow Zmodem)\n" - " -k enable X/Ymodem-1K send mode\n" - " -c enable Xmodem-CRC receive mode\n" - " -g enable X/Ymodem-G receive mode (no error recovery)\n" - " -2 set maximum Zmodem block size to 2K\n" - " -4 set maximum Zmodem block size to 4K\n" - " -8 set maximum Zmodem block size to 8K (ZedZap)\n" - " -m# set maximum receive file size to # bytes (0=unlimited, default=%u)\n" - " -! to pause after abnormal exit (error)\n" + " -k enable X/YMODEM-1K send mode\n" + " -c enable XMODEM-CRC receive mode\n" + " -g enable X/YMODEM-G receive mode (no error recovery)\n" + " -o disable ZMODEM CRC-32 mode (use CRC-16)\n" + " -s use segmented ZMODEM (disable streaming)\n" + " -2 set maximum ZMODEM block size to 2K\n" + " -4 set maximum ZMODEM block size to 4K\n" + " -8 set maximum ZMODEM block size to 8K (ZedZap)\n" + " -w# set maximum ZMODEM transmit window size (default=0, unlimited)\n" + " -m# set maximum receive file size to # bytes (default=0, unlimited)\n" " -l lowercase received filenames\n" + " -! to pause after abnormal exit (error)\n" #ifdef __unix__ " -telnet to enable Telnet mode (the default except in stdio mode)\n" #else @@ -1434,11 +1435,11 @@ static const char* usage= " -rlogin or -ssh or -raw to disable Telnet mode\n" "\n" "cmd = v to display detailed version information\n" - " sx to send Xmodem rx to receive Xmodem\n" - " sX to send Xmodem-1K rc to receive Xmodem-CRC\n" - " sy to send Ymodem ry to receive Ymodem\n" - " sY to send Ymodem-1K rg to receive Ymodem-G\n" - " sz to send Zmodem rz to receive Zmodem\n" + " sx to send XMODEM rx to receive XMODEM\n" + " sX to send XMODEM-1K rc to receive XMODEM-CRC\n" + " sy to send YMODEM ry to receive YMODEM\n" + " sY to send YMODEM-1K rg to receive YMODEM-G\n" + " sz to send ZMODEM rz to receive ZMODEM\n" "\n" "file = filename to send or receive\n" "path = directory to receive files into\n" @@ -1508,7 +1509,6 @@ int main(int argc, char **argv) int retval; uint fnames=0; FILE* fp; - BOOL tcp_nodelay; char compiler[32]; BOOL telnet_requested=FALSE; str_list_t ini = strListInit(); @@ -1525,13 +1525,10 @@ int main(int argc, char **argv) statfp=stdout; #endif - sscanf("$Revision: 2.10 $", "%*s %s", revision); - fprintf(statfp,"\nSynchronet External X/Y/ZMODEM v%s-%s" - " Copyright %s Rob Swindell\n\n" + " Copyright Rob Swindell\n\n" ,revision ,PLATFORM_DESC - ,&__DATE__[7] ); xmodem_init(&xm,NULL,&mode,lputs,xmodem_progress,send_byte,recv_byte,is_connected,NULL,flush); @@ -1555,8 +1552,6 @@ int main(int argc, char **argv) fclose(fp); } - tcp_nodelay =iniGetBool(ini, ROOT_SECTION,"TCP_NODELAY",TRUE); - telnet =iniGetBool(ini, ROOT_SECTION,"Telnet",TRUE); debug_tx =iniGetBool(ini, ROOT_SECTION,"DebugTx",FALSE); debug_rx =iniGetBool(ini, ROOT_SECTION,"DebugRx",FALSE); @@ -1578,36 +1573,42 @@ int main(int argc, char **argv) if(iniGetBool(ini, ROOT_SECTION,"Debug",FALSE)) log_level=LOG_DEBUG; - xm.send_timeout =iniGetInteger(ini, "Xmodem","SendTimeout",xm.send_timeout); /* seconds */ - xm.recv_timeout =iniGetInteger(ini, "Xmodem","RecvTimeout",xm.recv_timeout); /* seconds */ - xm.byte_timeout =iniGetInteger(ini, "Xmodem","ByteTimeout",xm.byte_timeout); /* seconds */ - xm.ack_timeout =iniGetInteger(ini, "Xmodem","AckTimeout",xm.ack_timeout); /* seconds */ - xm.block_size =(ulong)iniGetBytes(ini, "Xmodem","BlockSize",1,xm.block_size); /* 128 or 1024 */ - xm.max_block_size =(ulong)iniGetBytes(ini, "Xmodem","MaxBlockSize",1,xm.max_block_size); /* 128 or 1024 */ - xm.max_errors =iniGetInteger(ini, "Xmodem","MaxErrors",xm.max_errors); - xm.g_delay =iniGetInteger(ini, "Xmodem","G_Delay",xm.g_delay); - xm.crc_mode_supported =iniGetBool(ini, "Xmodem","SendCRC",xm.crc_mode_supported); - xm.g_mode_supported =iniGetBool(ini, "Xmodem","SendG",xm.g_mode_supported); - - xm.fallback_to_xmodem =iniGetInteger(ini, "Ymodem","FallbackToXmodem", xm.fallback_to_xmodem); - - zm.init_timeout =iniGetInteger(ini, "Zmodem","InitTimeout",zm.init_timeout); /* seconds */ - zm.send_timeout =iniGetInteger(ini, "Zmodem","SendTimeout",zm.send_timeout); /* seconds */ - zm.recv_timeout =iniGetInteger(ini, "Zmodem","RecvTimeout",zm.recv_timeout); /* seconds */ - zm.crc_timeout =iniGetInteger(ini, "Zmodem","CrcTimeout",zm.crc_timeout); /* seconds */ - zm.block_size =(ulong)iniGetBytes(ini, "Zmodem","BlockSize",1,zm.block_size); /* 1024 */ - zm.max_block_size =(ulong)iniGetBytes(ini, "Zmodem","MaxBlockSize",1,zm.max_block_size); /* 1024 or 8192 */ - zm.max_errors =iniGetInteger(ini, "Zmodem","MaxErrors",zm.max_errors); - zm.recv_bufsize =(ulong)iniGetBytes(ini, "Zmodem","RecvBufSize",1,0); - zm.no_streaming =!iniGetBool(ini, "Zmodem","Streaming",TRUE); - zm.want_fcs_16 =!iniGetBool(ini, "Zmodem","CRC32",TRUE); - zm.escape_telnet_iac =iniGetBool(ini, "Zmodem","EscapeTelnetIAC",TRUE); - zm.escape_8th_bit =iniGetBool(ini, "Zmodem","Escape8thBit",FALSE); - zm.escape_ctrl_chars =iniGetBool(ini, "Zmodem","EscapeCtrlChars",FALSE); - - dszlog_path =iniGetBool(ini, "DSZLOG","Path",TRUE); - dszlog_short =iniGetBool(ini, "DSZLOG","Short",FALSE); - dszlog_quotes =iniGetBool(ini, "DSZLOG","Quotes",FALSE); + const char* section = "XMODEM"; + xm.send_timeout =iniGetInteger(ini, section,"SendTimeout",xm.send_timeout); /* seconds */ + xm.recv_timeout =iniGetInteger(ini, section,"RecvTimeout",xm.recv_timeout); /* seconds */ + xm.byte_timeout =iniGetInteger(ini, section,"ByteTimeout",xm.byte_timeout); /* seconds */ + xm.ack_timeout =iniGetInteger(ini, section,"AckTimeout",xm.ack_timeout); /* seconds */ + xm.block_size =(ulong)iniGetBytes(ini, section,"BlockSize",1,xm.block_size); /* 128 or 1024 */ + xm.max_block_size =(ulong)iniGetBytes(ini, section,"MaxBlockSize",1,xm.max_block_size); /* 128 or 1024 */ + xm.max_errors =iniGetInteger(ini, section,"MaxErrors",xm.max_errors); + xm.g_delay =iniGetInteger(ini, section,"G_Delay",xm.g_delay); + xm.crc_mode_supported =iniGetBool(ini, section,"SendCRC",xm.crc_mode_supported); + xm.g_mode_supported =iniGetBool(ini, section,"SendG",xm.g_mode_supported); + + xm.fallback_to_xmodem =iniGetInteger(ini, "YMODEM","FallbackToXmodem", xm.fallback_to_xmodem); + + section = "ZMODEM"; + zm.init_timeout =iniGetInteger(ini, section,"InitTimeout",zm.init_timeout); /* seconds */ + zm.send_timeout =iniGetInteger(ini, section,"SendTimeout",zm.send_timeout); /* seconds */ + zm.recv_timeout =iniGetInteger(ini, section,"RecvTimeout",zm.recv_timeout); /* seconds */ + zm.crc_timeout =iniGetInteger(ini, section,"CrcTimeout",zm.crc_timeout); /* seconds */ + zm.block_size =(ulong)iniGetBytes(ini, section,"BlockSize",1,zm.block_size); /* 1024 */ + zm.max_block_size =(ulong)iniGetBytes(ini, section,"MaxBlockSize",1,zm.max_block_size); /* 1024 or 8192 */ + zm.max_errors =iniGetInteger(ini, section,"MaxErrors",zm.max_errors); + zm.recv_bufsize =(ulong)iniGetBytes(ini, section,"RecvBufSize",1,0); + zm.no_streaming =!iniGetBool(ini, section,"Streaming",TRUE); + zm.want_fcs_16 =!iniGetBool(ini, section,"CRC32",TRUE); + zm.can_full_duplex =iniGetBool(ini, section,"FullDuplex",TRUE); + zm.escape_telnet_iac =iniGetBool(ini, section,"EscapeTelnetIAC",TRUE); + zm.escape_8th_bit =iniGetBool(ini, section,"Escape8thBit",FALSE); + zm.escape_ctrl_chars =iniGetBool(ini, section,"EscapeCtrlChars",FALSE); + zm.max_window_size =(uint32_t)iniGetBytes(ini, section,"MaxWindowSize",1,0); + zm.target_window_size =(unsigned)iniGetDuration(ini, section,"TargetWindowSize",0); + + section = "DSZLOG"; + dszlog_path =iniGetBool(ini, section, "Path",TRUE); + dszlog_short =iniGetBool(ini, section, "Short",FALSE); + dszlog_quotes =iniGetBool(ini, section, "Quotes",FALSE); if(zm.recv_bufsize > 0xffff) zm.recv_bufsize = 0xffff; @@ -1669,7 +1670,7 @@ int main(int argc, char **argv) case 'Y': mode|=(YMODEM|CRC); break; - case 'k': /* Ymodem-Checksum for debug/test purposes only */ + case 'k': /* YMODEM-Checksum for debug/test purposes only */ mode|=YMODEM; break; case 'g': @@ -1682,7 +1683,7 @@ int main(int argc, char **argv) break; default: fprintf(statfp,"Unrecognized command '%s'\n\n",argv[i]); - fprintf(statfp,usage,MAX_FILE_SIZE); + fprintf(statfp,usage); bail(1); return -1; } @@ -1729,11 +1730,11 @@ int main(int argc, char **argv) dszlog_quotes=TRUE; continue; } - switch(toupper(*arg)) { - case 'K': /* sz/rz compatible */ + switch(*arg) { + case 'k': /* sz/rz compatible */ xm.block_size=XMODEM_MAX_BLOCK_SIZE; break; - case 'C': /* sz/rz compatible */ + case 'c': /* sz/rz compatible */ mode|=CRC; break; case '2': @@ -1745,25 +1746,28 @@ int main(int argc, char **argv) case '8': /* ZedZap */ zm.max_block_size=8192; break; - case 'O': /* disable Zmodem CRC-32 */ + case 'o': /* disable ZMODEM CRC-32 */ zm.want_fcs_16=TRUE; break; - case 'S': /* disable Zmodem streaming */ + case 's': /* disable ZMODEM streaming */ zm.no_streaming=TRUE; break; - case 'G': /* Ymodem-G or Xmodem-G (a.k.a. Qmodem-G) */ + case 'w': /* Max ZMODEM Transmit Window Size */ + zm.max_window_size = parse_byte_count(arg + 1, /* units: */1); + break; + case 'g': /* YMODEM-G or XMODEM-G (a.k.a. Qmodem-G) */ mode|=(GMODE|CRC); break; - case 'Y': + case 'y': mode|=OVERWRITE; break; case '!': pause_on_abend=TRUE; break; - case 'M': /* MaxFileSize */ - max_file_size=strtoul(arg + 1,NULL,0); /* TODO: use strtoull() ? */ + case 'm': /* MaxFileSize */ + max_file_size = parse_byte_count(arg + 1, /* units: */1); break; - case 'L': /* Lowercase received filenames */ + case 'l': /* Lowercase received filenames */ lc_filenames=TRUE; break; } @@ -1819,14 +1823,14 @@ int main(int argc, char **argv) if(!(mode&(SEND|RECV))) { fprintf(statfp,"!No command specified\n\n"); - fprintf(statfp,usage,MAX_FILE_SIZE); + fprintf(statfp,usage); bail(1); return -1; } - if(mode&(SEND|XMODEM) && !fnames) { /* Sending with any or recv w/Xmodem */ + if(mode&(SEND|XMODEM) && !fnames) { /* Sending with any or recv w/XMODEM */ fprintf(statfp,"!Must specify filename or filelist\n\n"); - fprintf(statfp,usage,MAX_FILE_SIZE); + fprintf(statfp,usage); bail(1); return -1; } @@ -1845,7 +1849,7 @@ int main(int argc, char **argv) init_stdio(); #else fprintf(statfp,"!No socket descriptor specified\n\n"); - fprintf(errfp,usage,MAX_FILE_SIZE); + fprintf(errfp,usage); bail(1); return -1; #endif @@ -1875,6 +1879,19 @@ int main(int argc, char **argv) lprintf(LOG_DEBUG, "Setting socket options"); if(iniGetSocketOptions(ini, "sockopts", sock, error, sizeof(error)) != 0) lprintf(LOG_ERR, "ERROR %s", error); + int value = 0; + socklen_t len = sizeof(value); + if(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&value, &len) == 0) + lprintf(LOG_DEBUG, "Socket send buffer length: %d bytes", value); + value = 0; + len = sizeof(value); + if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&value, &len) == 0) + lprintf(LOG_DEBUG, "Socket receive buffer length: %d bytes", value); + value = 0; + len = sizeof(value); + if(getsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&value, &len) == 0) + lprintf(LOG_DEBUG, "Socket TCP_NODELAY: %d", value); + #ifdef __unix__ } #endif diff --git a/src/sbbs3/targets.mk b/src/sbbs3/targets.mk index 7453e7044f3f18e1d7c9f90370bc645b26eb73c4..43a3aef500d2e3c7b67bccc0d0f0b9e81159115b 100644 --- a/src/sbbs3/targets.mk +++ b/src/sbbs3/targets.mk @@ -51,7 +51,9 @@ UTILS = $(FIXSMB) $(CHKSMB) \ $(SEXYZ) $(DSTSEDIT) $(READSAUCE) $(SHOWSTAT) \ $(PKTDUMP) $(FMSGDUMP) -all: dlls utils console scfg uedit umonitor +GIT_INFO = git_hash.h git_branch.h + +all: $(GIT_INFO) dlls utils console scfg uedit umonitor console: $(JS_DEPS) xpdev-mt smblib \ $(MTOBJODIR) $(LIBODIR) $(EXEODIR) \ @@ -114,6 +116,21 @@ symlinks: all ln -sfr */$(EXEODIR)/* $(SBBSEXEC) endif +.PHONY: FORCE +FORCE: + +ifneq ($(GIT), NO) +git_hash.h: FORCE ../../.git + $(QUIET)echo '#define GIT_HASH "'`git log -1 HEAD --format=%h`\" > $@.tmp + $(QUIET)diff $@.tmp $@ || cp $@.tmp $@ + $(QUIET)rm -f $@.tmp + +git_branch.h: FORCE ../../.git + $(QUIET)echo '#define GIT_BRANCH "'`git rev-parse --abbrev-ref HEAD`\" > $@.tmp + $(QUIET)diff $@.tmp $@ || cp $@.tmp $@ + $(QUIET)rm -f $@.tmp +endif + ifeq ($(os),linux) .PHONY: setcap setcap: all @@ -124,10 +141,10 @@ endif sexyz: $(SEXYZ) .PHONY: jsdoor -jsdoor: $(JS_DEPS) $(CRYPT_DEPS) $(XPDEV-MT_LIB) $(SMBLIB) $(UIFCLIB-MT) $(CIOLIB-MT) $(JSDOOR) +jsdoor: $(GIT_INFO) $(JS_DEPS) $(CRYPT_DEPS) $(XPDEV-MT_LIB) $(SMBLIB) $(UIFCLIB-MT) $(CIOLIB-MT) $(JSDOOR) # Library dependencies -$(SBBS): +$(SBBS): $(FTPSRVR): $(WEBSRVR): $(MAILSRVR): diff --git a/src/sbbs3/text_defaults.c b/src/sbbs3/text_defaults.c index af7b5f1f0ee197272501e14a883df924fe3921b3..b6e55e09d86297de0ff613fd319639638f0a3ef2 100644 --- a/src/sbbs3/text_defaults.c +++ b/src/sbbs3/text_defaults.c @@ -66,10 +66,10 @@ const char * const text_defaults[TOTAL_TEXT]={ ,"\x07\x01\x5f\x01\x77\x01\x68\x4e\x6f\x64\x65\x20\x25\x32\x64\x3a\x20\x01\x67\x25\x73\x01\x6e\x01\x67\x20\x73\x65\x6e\x74\x20\x79" "\x6f\x75\x20\x45\x2d\x6d\x61\x69\x6c\x2e\x0d\x0a" // 038 EmailNodeMsg ,"\x01\x6e\x0d\x0a\x59\x6f\x75\x20\x63\x61\x6e\x27\x74\x20\x66\x6f\x72\x77\x61\x72\x64\x20\x6d\x61\x69\x6c\x2e\x0d\x0a" // 039 R_Forward - ,"\x01\x6e\x01\x6d\x0d\x0a\x46\x6f\x72\x77\x61\x72\x64\x65\x64\x20\x62\x79\x20\x01\x68\x25\x73\x01\x6e\x01\x6d\x20\x6f\x6e\x20\x01" - "\x68\x25\x73\x01\x6e\x0d\x0a" // 040 ForwardedFrom - ,"\x01\x6e\x01\x6d\x0d\x0a\x4d\x61\x69\x6c\x20\x66\x6f\x72\x77\x61\x72\x64\x65\x64\x20\x74\x6f\x20\x01\x68\x25\x73\x20\x01\x6e\x01" - "\x6d\x23\x25\x64\x2e\x01\x6e\x0d\x0a" // 041 Forwarded + ,"\x01\x6e\x01\x6d\x46\x6f\x72\x77\x61\x72\x64\x65\x64\x20\x62\x79\x20\x01\x68\x25\x73\x01\x6e\x01\x6d\x20\x6f\x6e\x20\x01\x68\x25" + "\x73\x01\x6e\x0d\x0a" // 040 ForwardedFrom + ,"\x01\x6e\x01\x6d\x0d\x0a\x4d\x61\x69\x6c\x20\x66\x6f\x72\x77\x61\x72\x64\x65\x64\x20\x74\x6f\x20\x01\x68\x25\x73\x01\x6e\x0d\x0a" + "" // 041 Forwarded ,"\x01\x62\x01\x68\x41\x75\x74\x6f\x20\x6d\x65\x73\x73\x61\x67\x65\x20\x62\x79\x3a\x20\x01\x63\x25\x73\x01\x62\x20\x6f\x6e\x20\x25" "\x73\x01\x6e\x0d\x0a\x0d\x0a" // 042 AutoMsgBy ,"\x0d\x0a\x41\x75\x74\x6f\x20\x4d\x65\x73\x73\x61\x67\x65\x20\x2d\x20\x7e\x52\x65\x61\x64\x2c\x20\x7e\x57\x72\x69\x74\x65\x2c\x20" diff --git a/src/sbbs3/umonitor/umonitor.c b/src/sbbs3/umonitor/umonitor.c index bf9de1e8f00cc1df722ee9d69dd136ab10b3f216..8a5b9d81487ce88063b15bfad1a452076ffff03f 100644 --- a/src/sbbs3/umonitor/umonitor.c +++ b/src/sbbs3/umonitor/umonitor.c @@ -66,7 +66,7 @@ uifcapi_t uifc; /* User Interface (UIFC) Library API */ const char *YesStr="Yes"; const char *NoStr="No"; -char* app_title = "Synchronet UNIX Monitor v" VERSION " " PLATFORM_DESC; +char app_title[128]; int ciolib_mode=CIOLIB_MODE_AUTO; int lprintf(char *fmt, ...) @@ -323,10 +323,12 @@ int drawstats(scfg_t *cfg, int nodenum, node_t *node, int *curp, int *barp) { stats_t nstats; char statbuf[6*78]; /* Buffer to hold the stats for passing to uifc.showbuf() */ char str[4][4][12]; + char heading[128]; char usrname[128]; + char tmp[128]; ulong free; uint i,l,m; - time_t t; + time_t t, now; int shownode=1; if(getnodedat(cfg,nodenum,node,FALSE,NULL)) { @@ -338,16 +340,25 @@ int drawstats(scfg_t *cfg, int nodenum, node_t *node, int *curp, int *barp) { username(cfg,node->useron,usrname); getstats(cfg, 0, &sstats); - t=time(NULL); - strftime(str[0][0],12,"%b %e",localtime(&t)); - free=getfreediskspace(cfg->temp_dir,1024); - if(free<1000) { - free=getfreediskspace(cfg->temp_dir,0); - getsizestr(str[0][1],free,TRUE); - } - else - getsizestr(str[0][1],free,FALSE); + now = time(NULL); if(shownode) { + static client_t client; + time_t hangup = now; + getnodeclient(cfg, nodenum, &client, &hangup); + t = client.time ? client.time : now; + if(node->status != NODE_WFC && node->status != NODE_OFFLINE) + safe_snprintf(heading, sizeof(heading), "`Node #`%-3d %s `%s`: %s" + ,nodenum + ,sectostr(now - t, tmp) + ,client.protocol + ,client.addr); + else + safe_snprintf(heading, sizeof(heading), "`Node #`%-3d %s `%s`: %s `on` %.12s" + ,nodenum + ,sectostr(hangup - t, tmp) + ,client.protocol + ,client.addr + ,ctime(&hangup) + 4); snprintf(str[1][0],12,"%s/%s",getnumstr(str[3][2],nstats.ltoday),getnumstr(str[3][3],sstats.ltoday)); getnumstr(str[1][1],sstats.logons); snprintf(str[1][2],12,"%s/%s",getnumstr(str[3][2],nstats.ttoday),getnumstr(str[3][3],sstats.ttoday)); @@ -367,6 +378,14 @@ int drawstats(scfg_t *cfg, int nodenum, node_t *node, int *curp, int *barp) { getnumstr(str[3][3],sstats.dls); } else { + free=getfreediskspace(cfg->data_dir,1024); + if(free<1000) { + free=getfreediskspace(cfg->data_dir,0); + getsizestr(str[0][0],free,TRUE); + } + else + getsizestr(str[0][0],free,FALSE); + sprintf(heading, "`Space`: %s `in` %s", str[0][0], cfg->data_dir); snprintf(str[1][0],12,"%s",getnumstr(str[3][3],sstats.ltoday)); getnumstr(str[1][1],sstats.logons); snprintf(str[1][2],12,"%s",getnumstr(str[3][3],sstats.ttoday)); @@ -385,11 +404,11 @@ int drawstats(scfg_t *cfg, int nodenum, node_t *node, int *curp, int *barp) { getsizestr(str[3][2],sstats.dlb,TRUE); getnumstr(str[3][3],sstats.dls); } - snprintf(statbuf,sizeof(statbuf),"`Node #`: %-3d %6s `Space`: %s" + snprintf(statbuf,sizeof(statbuf),"%s" "\n`Logons`: %-11s `Total`: %-11s `Timeon`: %-11s `Total`: %-11s" "\n`Emails`: %-11s `Posts`: %-11s `Fbacks`: %-11s `Users`: %-11s" "\n`Uloads`: %-11s `Files`: %-11s `Dloads`: %-11s `Files`: %-11s", - nodenum,str[0][0],str[0][1], + heading, str[1][0],str[1][1],str[1][2],str[1][3], str[2][0],str[2][1],str[2][2],str[2][3], str[3][0],str[3][1],str[3][2],str[3][3]); @@ -823,6 +842,8 @@ int main(int argc, char** argv) { time_t last_semfile_check = time(NULL); int idle_sleep=100; + SAFEPRINTF2(app_title, "Synchronet UNIX Monitor v%s%c", VERSION, REVISION); + /******************/ /* Ini file stuff */ /******************/ @@ -875,7 +896,7 @@ int main(int argc, char** argv) { printf("ERROR! %s\n",str); exit(1); } - prep_dir(cfg.data_dir, cfg.temp_dir, sizeof(cfg.temp_dir)); + prep_dir(cfg.ctrl_dir, cfg.temp_dir, sizeof(cfg.temp_dir)); memset(&uifc,0,sizeof(uifc)); uifc.mode|=UIFC_NOCTRL; diff --git a/src/sbbs3/userdat.c b/src/sbbs3/userdat.c index 883b5d7c1e6cb965f4116173f19f7ad8371264a3..44f6079cef58e0b753a7dc5541d7e7c10506ab84 100644 --- a/src/sbbs3/userdat.c +++ b/src/sbbs3/userdat.c @@ -1538,6 +1538,44 @@ int putnmsg(scfg_t* cfg, int num, char *strin) return(0); } +/* Return node's client's socket descriptor or negative on error */ +int getnodeclient(scfg_t* cfg, uint number, client_t* client, time_t* done) +{ + SOCKET sock = INVALID_SOCKET; + char path[MAX_PATH + 1]; + char value[INI_MAX_VALUE_LEN]; + char* p; + FILE* fp; + + if(!VALID_CFG(cfg) + || client == NULL || number < 1 || number > cfg->sys_nodes) + return -1; + + if(client->size == sizeof(client)) { + free((char*)client->protocol); + free((char*)client->user); + } + memset(client, 0, sizeof(*client)); + client->size = sizeof(client); + SAFEPRINTF(path, "%sclient.ini", cfg->node_path[number - 1]); + fp = iniOpenFile(path, /* create: */FALSE); + if(fp == NULL) + return -2; + sock = iniReadShortInt(fp, ROOT_SECTION, "sock", 0); + client->port = iniReadShortInt(fp, ROOT_SECTION, "port", 0); + client->time = iniReadInteger(fp, ROOT_SECTION, "time", 0); + client->usernum = iniReadInteger(fp, ROOT_SECTION, "user", 0); + SAFECOPY(client->addr, iniReadString(fp, ROOT_SECTION, "addr", "<none>", value)); + SAFECOPY(client->host, iniReadString(fp, ROOT_SECTION, "host", "<none>", value)); + if((p = iniReadString(fp, ROOT_SECTION, "prot", NULL, value)) != NULL) + client->protocol = strdup(p); + if((p = iniReadString(fp, ROOT_SECTION, "name", NULL, value)) != NULL) + client->user = strdup(p); + *done = iniReadInteger(fp, ROOT_SECTION, "done", client->time); + fclose(fp); + return sock; +} + static int getdirnum(scfg_t* cfg, char* code) { size_t i; diff --git a/src/sbbs3/userdat.h b/src/sbbs3/userdat.h index 36c46244028272515a82ccd82081b6b460be3aab..3d78be70fee126a278b332544473294eda98c73f 100644 --- a/src/sbbs3/userdat.h +++ b/src/sbbs3/userdat.h @@ -76,6 +76,7 @@ DLLEXPORT char* getsmsg(scfg_t*, int usernumber); DLLEXPORT int putsmsg(scfg_t*, int usernumber, char *strin); DLLEXPORT char* getnmsg(scfg_t*, int node_num); DLLEXPORT int putnmsg(scfg_t*, int num, char *strin); +DLLEXPORT int getnodeclient(scfg_t*, uint number, client_t*, time_t*); DLLEXPORT uint userdatdupe(scfg_t*, uint usernumber, uint offset, uint datlen, char *dat ,BOOL del, BOOL next, void (*progress)(void*, int, int), void* cbdata); diff --git a/src/sbbs3/ver.cpp b/src/sbbs3/ver.cpp index bc6e5d753c9cc6166dabdf5a0da1a7bb30d89b8e..594e9147104fabbc68499098d53b76ebdee9c96e 100644 --- a/src/sbbs3/ver.cpp +++ b/src/sbbs3/ver.cpp @@ -1,9 +1,4 @@ -/* ver.cpp */ -// vi: tabstop=4 - -/* Synchronet version display */ - -/* $Id: ver.cpp,v 1.31 2019/10/08 02:07:26 rswindell Exp $ */ +/* Synchronet version info */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * @@ -18,29 +13,32 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ #include "sbbs.h" +#ifdef SBBS #include "ssl.h" +#endif +#include "git_hash.h" +#include "git_branch.h" +#include "ver.h" +#ifdef __cplusplus +extern "C" { +#endif + +const char* git_hash = GIT_HASH; +const char* git_branch = GIT_BRANCH; const char* beta_version = " "; /* Space if non-beta, " beta" otherwise */ +#ifdef __cplusplus +} +#endif + #if defined(_WINSOCKAPI_) extern WSADATA WSAData; #define SOCKLIB_DESC WSAData.szDescription @@ -71,7 +69,7 @@ char* socklib_version(char* str, char* winsock_ver) return(str); } -#ifndef JSDOOR +#if defined(SBBS) && !defined(JSDOOR) void sbbs_t::ver() { char str[128],compiler[32]; @@ -96,7 +94,10 @@ void sbbs_t::ver() center(str); CRLF; - sprintf(str,"%s - http://www.synchro.net", COPYRIGHT_NOTICE); + center("https://gitlab.synchro.net - " GIT_BRANCH "/" GIT_HASH); + CRLF; + + sprintf(str,"%s - http://synchro.net", COPYRIGHT_NOTICE); center(str); CRLF; diff --git a/src/sbbs3/ver.h b/src/sbbs3/ver.h new file mode 100644 index 0000000000000000000000000000000000000000..288605929394d995049c524bd3b57766f985e89b --- /dev/null +++ b/src/sbbs3/ver.h @@ -0,0 +1,36 @@ +/* Synchronet version info */ + +/**************************************************************************** + * @format.tab-size 4 (Plain Text/Source Code File Header) * + * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * + * * + * Copyright Rob Swindell - http://www.synchro.net/copyright.html * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * See the GNU General Public License for more details: gpl.txt or * + * http://www.fsf.org/copyleft/gpl.html * + * * + * For Synchronet coding style and modification guidelines, see * + * http://www.synchro.net/source.html * + * * + * Note: If this box doesn't appear square, then you need to fix your tabs. * + ****************************************************************************/ + +#ifndef _VER_H_ +#define _VER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char* git_hash; +extern const char* git_branch; +extern const char* beta_version; + +#ifdef __cplusplus +} +#endif +#endif /* Don't add anything after this line */ \ No newline at end of file diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c index 0a8cc52360bd689475db758847f6d06a70a90ad3..f34f9879d9d1eb5b0d589d54b387032d3eceb53e 100644 --- a/src/sbbs3/websrvr.c +++ b/src/sbbs3/websrvr.c @@ -65,6 +65,7 @@ #include "xpprintf.h" #include "ssl.h" #include "fastcgi.h" +#include "ver.h" static const char* server_name="Synchronet Web Server"; static const char* newline="\r\n"; @@ -106,7 +107,6 @@ static volatile BOOL terminate_server=FALSE; static volatile BOOL terminated=FALSE; static volatile BOOL terminate_http_logging_thread=FALSE; static struct xpms_set *ws_set=NULL; -static char revision[16]; static char root_dir[MAX_PATH+1]; static char error_dir[MAX_PATH+1]; static char cgi_dir[MAX_PATH+1]; @@ -6758,17 +6758,16 @@ const char* DLLCALL web_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.720 $", "%*s %s", revision); - - sprintf(ver,"%s %s%s " - "Compiled %s %s with %s" + sprintf(ver,"%s %s%c%s " + "Compiled %s/%s %s %s with %s" ,server_name - ,revision + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else ,"" #endif + ,git_branch, git_hash ,__DATE__, __TIME__, compiler); return(ver); @@ -6904,7 +6903,6 @@ void DLLCALL web_server(void* arg) startup=(web_startup_t*)arg; SetThreadName("sbbs/webServer"); - web_ver(); /* get CVS revision */ if(startup==NULL) { sbbs_beep(100,500); @@ -6926,7 +6924,7 @@ void DLLCALL web_server(void* arg) #endif ZERO_VAR(js_server_props); - SAFEPRINTF2(js_server_props.version,"%s %s",server_name,revision); + SAFEPRINTF3(js_server_props.version,"%s %s%c",server_name, VERSION, REVISION); js_server_props.version_detail=web_ver(); js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; @@ -6976,9 +6974,9 @@ void DLLCALL web_server(void* arg) memset(&scfg, 0, sizeof(scfg)); - lprintf(LOG_INFO,"%s Revision %s%s" + lprintf(LOG_INFO,"%s Version %s%c%s" ,server_name - ,revision + ,VERSION, REVISION #ifdef _DEBUG ," Debug" #else @@ -6988,7 +6986,7 @@ void DLLCALL web_server(void* arg) DESCRIBE_COMPILER(compiler); - lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler); + lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", git_branch, git_hash, __DATE__, __TIME__, compiler); if(!winsock_startup()) { cleanup(1); diff --git a/src/sbbs3/websrvr.vcxproj b/src/sbbs3/websrvr.vcxproj index 931bfdd3b13b51df583a0edf8598369faf04749b..94519cae70b333f6d7750821c019c15abdefb1f2 100644 --- a/src/sbbs3/websrvr.vcxproj +++ b/src/sbbs3/websrvr.vcxproj @@ -176,6 +176,7 @@ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> + <ClCompile Include="ver.cpp" /> <ClCompile Include="websrvr.c"> <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">%(PreprocessorDefinitions)</PreprocessorDefinitions> diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp index 2e3ae1a6d2c271d1b4b96b825798d41cb8851a01..189f32109ccafce6de8cc97ac68b63a049c61150 100644 --- a/src/sbbs3/writemsg.cpp +++ b/src/sbbs3/writemsg.cpp @@ -1,7 +1,4 @@ /* Synchronet message creation routines */ -// vi: tabstop=4 - -/* $Id: writemsg.cpp,v 1.175 2020/05/24 19:34:02 rswindell Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * @@ -16,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -758,11 +743,8 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *subj, long mode, void sbbs_t::editor_info_to_msg(smbmsg_t* msg, const char* editor, const char* charset) { - if(editor != NULL) - smb_hfield_str(msg, SMB_EDITOR, editor); - - if(charset != NULL) - smb_hfield_str(msg, FIDOCHARSET, charset); + smb_hfield_string(msg, SMB_EDITOR, editor); + smb_hfield_string(msg, FIDOCHARSET, charset); ushort useron_xedit = useron.xedit; @@ -1312,11 +1294,12 @@ bool sbbs_t::editfile(char *fname, bool msg) /*************************/ /* Copy file attachments */ /*************************/ -void sbbs_t::copyfattach(uint to, uint from, char *title) +bool sbbs_t::copyfattach(uint to, uint from, const char* subj) { char str[128],str2[128],str3[128],*tp,*sp,*p; + bool result = false; - strcpy(str,title); + strcpy(str, subj); tp=str; while(1) { p=strchr(tp,' '); @@ -1328,96 +1311,219 @@ void sbbs_t::copyfattach(uint to, uint from, char *title) ,cfg.data_dir,to,tp); SAFEPRINTF3(str3,"%sfile/%04u.in/%s" /* str2 is path/fname */ ,cfg.data_dir,from,tp); - if(strcmp(str2,str3)) - mv(str3,str2,1); + if(strcmp(str2,str3)) { + if(mv(str3, str2, /* copy */true) != 0) + return false; + result = true; + } if(!p) break; tp=p+1; } + return result; } - /****************************************************************************/ -/* Forwards mail (fname) to usernumber */ -/* Called from function readmail */ +/* Forwards mail 'orgmsg' to 'to' with optional 'comment'. */ +/* If comment is NULL, comment lines will be prompted for. */ +/* If comment is a zero-length string, no comments will be included. */ /****************************************************************************/ -void sbbs_t::forwardmail(smbmsg_t *msg, int usernumber) +bool sbbs_t::forwardmail(smbmsg_t* orgmsg, const char* to, const char* subject, const char* comment) { char str[256],touser[128]; char tmp[512]; - int i; + char subj[LEN_TITLE + 1]; + int result; + smbmsg_t msg; node_t node; - msghdr_t hdr=msg->hdr; - idxrec_t idx=msg->idx; - time32_t now32; + uint usernumber = 0; + + if(to == NULL) + return false; + + uint16_t net_type = NET_NONE; + if(strchr(to, '@') != NULL) + net_type = smb_netaddr_type(to); + if(net_type == NET_NONE) { + usernumber = finduser(to); + if(usernumber < 1) + return false; + } else if(!is_supported_netmail_addr(&cfg, to)) { + bprintf(text[InvalidNetMailAddr], to); + return false; + } if(useron.etoday>=cfg.level_emailperday[useron.level] && !SYSOP && !(useron.exempt&FLAG('M'))) { bputs(text[TooManyEmailsToday]); - return; + return false; } if(useron.rest&FLAG('F')) { bputs(text[R_Forward]); - return; + return false; } if(usernumber==1 && useron.rest&FLAG('S')) { bprintf(text[R_Feedback],cfg.sys_op); - return; + return false; } if(usernumber!=1 && useron.rest&FLAG('E')) { bputs(text[R_Email]); - return; + return false; + } + + if(subject == NULL) { + subject = subj; + SAFEPRINTF(subj, "Fwd: %s", orgmsg->subj); + bputs(text[SubjectPrompt]); + if(!getstr(subj, sizeof(subj) - 1, K_LINE | K_EDIT | K_AUTODEL | K_TRIM)) + return false; } - msg->idx.attr&=~(MSG_READ|MSG_DELETE); - msg->hdr.attr=msg->idx.attr; + memset(&msg, 0, sizeof(msg)); + msg.hdr.auxattr = orgmsg->hdr.auxattr & (MSG_HFIELDS_UTF8 | MSG_MIMEATTACH); + msg.hdr.when_imported.time = time32(NULL); + msg.hdr.when_imported.zone = sys_timezone(&cfg); + msg.hdr.when_written = msg.hdr.when_imported; + smb_hfield_str(&msg, SUBJECT, subject); + add_msg_ids(&cfg, &smb, &msg, orgmsg); - smb_hfield_str(msg,SENDER,useron.alias); + smb_hfield_str(&msg,SENDER,useron.alias); SAFEPRINTF(str,"%u",useron.number); - smb_hfield_str(msg,SENDEREXT,str); + smb_hfield_str(&msg,SENDEREXT,str); /* Security logging */ - msg_client_hfields(msg,&client); - smb_hfield_str(msg,SENDERSERVER, server_host_name()); - - username(&cfg,usernumber,touser); - smb_hfield_str(msg,RECIPIENT,touser); - SAFEPRINTF(str,"%u",usernumber); - smb_hfield_str(msg,RECIPIENTEXT,str); - msg->idx.to=usernumber; - - now32=time32(NULL); - smb_hfield(msg,FORWARDED,sizeof(time32_t),&now32); - - - if((i=smb_open_da(&smb))!=SMB_SUCCESS) { - errormsg(WHERE,ERR_OPEN,smb.file,i,smb.last_error); - return; + msg_client_hfields(&msg,&client); + smb_hfield_str(&msg,SENDERSERVER, server_host_name()); + + if(usernumber > 0) { + username(&cfg,usernumber,touser); + smb_hfield_str(&msg, RECIPIENT,touser); + SAFEPRINTF(str,"%u",usernumber); + smb_hfield_str(&msg, RECIPIENTEXT,str); + } else { + SAFECOPY(touser, to); + char* p; + if((p = strchr(touser, '@')) != NULL) + *p = '\0'; + smb_hfield_str(&msg, RECIPIENT, touser); + SAFECOPY(touser, to); + const char* addr = touser; + if(net_type != NET_INTERNET && p != NULL) + addr = p + 1; + char fulladdr[128]; + if(net_type == NET_QWK) { + usernumber = qwk_route(&cfg, addr, fulladdr, sizeof(fulladdr) - 1); + if(*fulladdr == '\0') { + bprintf(text[InvalidNetMailAddr], addr); + smb_freemsgmem(&msg); + return false; + } + addr = fulladdr; + SAFEPRINTF(str, "%u", usernumber); + smb_hfield_str(&msg, RECIPIENTEXT, str); + usernumber = 0; + } + smb_hfield_bin(&msg, RECIPIENTNETTYPE, net_type); + smb_hfield_netaddr(&msg, RECIPIENTNETADDR, addr, &net_type); + } + if(orgmsg->mime_version != NULL) { + safe_snprintf(str, sizeof(str), "MIME-Version: %s", orgmsg->mime_version); + smb_hfield_str(&msg, RFC822HEADER, str); + } + if(orgmsg->content_type != NULL) { + safe_snprintf(str, sizeof(str), "Content-type: %s", orgmsg->content_type); + smb_hfield_str(&msg, RFC822HEADER, str); + } + // This header field not strictly required any more: + time32_t now32 = time32(NULL); + smb_hfield(&msg, FORWARDED, sizeof(now32), &now32); + + const char* br = NULL; + const char* pg = nulstr; + const char* lt = "<"; + const char* gt = ">"; + if(orgmsg->text_subtype != NULL && stricmp(orgmsg->text_subtype, "html") == 0) { + lt = "<"; + gt = ">"; + br = "<br>"; + pg = "<p>"; + } + + if(comment == NULL) { + while(online && !msgabort()) { + bputs(text[UeditComment]); + if(!getstr(str, 70, K_WRAP)) + break; + smb_hfield_string(&msg, SMB_COMMENT, str); + smb_hfield_string(&msg, SMB_COMMENT, br); + } + if(!online || msgabort()) { + smb_freemsgmem(&msg); + return false; + } + } else { + if(*comment) + smb_hfield_string(&msg, SMB_COMMENT, comment); + } + if(smb_get_hfield(&msg, SMB_COMMENT, NULL) != NULL) + smb_hfield_string(&msg, SMB_COMMENT, pg); + smb_hfield_string(&msg, SMB_COMMENT, "-----Forwarded Message-----"); + smb_hfield_string(&msg, SMB_COMMENT, br); + if(orgmsg->from_net.addr != NULL) + safe_snprintf(str, sizeof(str), "From: %s %s%s%s" + ,orgmsg->from, lt, smb_netaddrstr(&orgmsg->from_net, tmp), gt); + else + safe_snprintf(str, sizeof(str), "From: %s", orgmsg->from); + smb_hfield_string(&msg, SMB_COMMENT, str); + smb_hfield_string(&msg, SMB_COMMENT, br); + safe_snprintf(str, sizeof(str), "Date: %s", msgdate(orgmsg->hdr.when_written, tmp)); + smb_hfield_string(&msg, SMB_COMMENT, str); + smb_hfield_string(&msg, SMB_COMMENT, br); + if(orgmsg->to_net.addr != NULL) + safe_snprintf(str, sizeof(str), "To: %s %s%s%s" + ,orgmsg->to, lt, smb_netaddrstr(&orgmsg->to_net, tmp), gt); + else + safe_snprintf(str, sizeof(str), "To: %s", orgmsg->to); + smb_hfield_string(&msg, SMB_COMMENT, str); + smb_hfield_string(&msg, SMB_COMMENT, br); + safe_snprintf(str, sizeof(str), "Subject: %s", orgmsg->subj); + smb_hfield_string(&msg, SMB_COMMENT, str); + smb_hfield_string(&msg, SMB_COMMENT, pg); + + // Re-use the original message's data + if((result = smb_open_da(&smb)) != SMB_SUCCESS) { + smb_freemsgmem(&msg); + errormsg(WHERE, ERR_OPEN, smb.file, result, smb.last_error); + return false; } - if((i=smb_incmsg_dfields(&smb,msg,1))!=SMB_SUCCESS) { - errormsg(WHERE,ERR_WRITE,smb.file,i); - return; + if((result = smb_incmsg_dfields(&smb, orgmsg, 1)) != SMB_SUCCESS) { + smb_freemsgmem(&msg); + errormsg(WHERE, ERR_WRITE, smb.file, result, smb.last_error); + return false; } smb_close_da(&smb); + msg.dfield = orgmsg->dfield; + msg.hdr.offset = orgmsg->hdr.offset; + msg.hdr.total_dfields = orgmsg->hdr.total_dfields; - if((i=smb_addmsghdr(&smb,msg,smb_storage_mode(&cfg, &smb)))!=SMB_SUCCESS) { - errormsg(WHERE,ERR_WRITE,smb.file,i,smb.last_error); - smb_freemsg_dfields(&smb,msg,1); - return; + if(orgmsg->hdr.auxattr&MSG_FILEATTACH) { + copyfattach(usernumber, useron.number, orgmsg->subj); + msg.hdr.auxattr |= MSG_FILEATTACH; } - if(msg->hdr.auxattr&MSG_FILEATTACH) - copyfattach(usernumber,useron.number,msg->subj); + result = smb_addmsghdr(&smb, &msg, smb_storage_mode(&cfg, &smb)); + msg.dfield = NULL; + smb_freemsgmem(&msg); + if(result != SMB_SUCCESS) { + errormsg(WHERE, ERR_WRITE, smb.file, result, smb.last_error); + smb_freemsg_dfields(&smb, orgmsg, 1); + return false; + } - bprintf(text[Forwarded],username(&cfg,usernumber,str),usernumber); - SAFEPRINTF2(str,"forwarded mail to %s #%d" - ,username(&cfg,usernumber,tmp) - ,usernumber); + bprintf(text[Forwarded], touser, usernumber); + SAFEPRINTF(str, "forwarded mail to %s", touser); logline("E+",str); - msg->idx=idx; - msg->hdr=hdr; - if(usernumber==1) { useron.fbacks++; @@ -1432,19 +1538,26 @@ void sbbs_t::forwardmail(smbmsg_t *msg, int usernumber) useron.etoday++; putuserrec(&cfg,useron.number,U_ETODAY,5,ultoa(useron.etoday,tmp,10)); - for(i=1;i<=cfg.sys_nodes;i++) { /* Tell user, if online */ - getnodedat(i,&node,0); - if(node.useron==usernumber && !(node.misc&NODE_POFF) - && (node.status==NODE_INUSE || node.status==NODE_QUIET)) { - SAFEPRINTF2(str,text[EmailNodeMsg],cfg.node_num,useron.alias); - putnmsg(&cfg,i,str); - break; - } - } - if(i>cfg.sys_nodes) { /* User wasn't online, so leave short msg */ - SAFEPRINTF(str,text[UserSentYouMail],useron.alias); - putsmsg(&cfg,usernumber,str); + if(usernumber > 0) { + int i; + for(i=1;i<=cfg.sys_nodes;i++) { /* Tell user, if online */ + getnodedat(i,&node,0); + if(node.useron==usernumber && !(node.misc&NODE_POFF) + && (node.status==NODE_INUSE || node.status==NODE_QUIET)) { + SAFEPRINTF2(str,text[EmailNodeMsg],cfg.node_num,useron.alias); + putnmsg(&cfg,i,str); + break; + } + } + if(i>cfg.sys_nodes) { /* User wasn't online, so leave short msg */ + SAFEPRINTF(str,text[UserSentYouMail],useron.alias); + putsmsg(&cfg,usernumber,str); + } + } else { + if(net_type == NET_FIDO && cfg.netmail_sem[0]) + ftouch(cmdstr(cfg.netmail_sem, nulstr, nulstr, NULL)); } + return true; } /****************************************************************************/ diff --git a/src/sbbs3/xmodem.c b/src/sbbs3/xmodem.c index bbf6ba1e13f45f995f76ff1b97a174142a1b1a36..794002990cb811e0c130df87abc3a2aa4adbcd9c 100755 --- a/src/sbbs3/xmodem.c +++ b/src/sbbs3/xmodem.c @@ -1,8 +1,5 @@ -/* xmodem.c */ - /* Synchronet X/YMODEM Functions */ - -/* $Id: xmodem.c,v 1.52 2019/08/31 22:39:24 rswindell Exp $ */ +/* Synchronet X/YMODEM Functions */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * @@ -17,21 +14,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -622,9 +607,7 @@ const char* xmodem_source(void) char* xmodem_ver(char *buf) { - sscanf("$Revision: 1.52 $", "%*s %s", buf); - - return(buf); + return strcpy(buf, "2.0"); } void xmodem_init(xmodem_t* xm, void* cbdata, long* mode diff --git a/src/sbbs3/xtrn_sec.cpp b/src/sbbs3/xtrn_sec.cpp index 60afb07da64bd285cc9eafbc6b26c1674dd03d5d..859e5e2b69626a8178037c95306e634964e2a415 100644 --- a/src/sbbs3/xtrn_sec.cpp +++ b/src/sbbs3/xtrn_sec.cpp @@ -151,6 +151,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl struct tm tm; struct tm tl; stats_t stats; + long term = term_supports(); char node_dir[MAX_PATH+1]; char ctrl_dir[MAX_PATH+1]; @@ -212,8 +213,8 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,cfg.sys_nodes /* Total system nodes */ ,cfg.node_num /* Current node */ ,tleft /* User Timeleft in seconds */ - ,term_supports(ANSI) /* User ANSI ? (Yes/Mono/No) */ - ? term_supports(COLOR) + ,(term & ANSI) /* User ANSI ? (Yes/Mono/No) */ + ? (term & COLOR) ? "Yes":"Mono":"No" ,rows /* User Screen lines */ ,useron.cdt+useron.freecdt); /* User Credits */ @@ -326,7 +327,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl lfexpand(str,misc); write(file,str,strlen(str)); - safe_snprintf(str, sizeof(str), "%lu\n%s\n%lu\n%ld\n%u\n%u\n%u\n%ld\n%u\n" + safe_snprintf(str, sizeof(str), "%lu\n%s\n%lu\n%ld\n%u\n%u\n%u\n%d\n%u\n" ,useron.cdt+useron.freecdt /* Gold */ ,unixtodstr(&cfg,useron.laston,tmp) /* User last on date */ ,cols /* User screen width */ @@ -334,7 +335,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,useron.level /* User SL */ ,0 /* Cosysop? */ ,SYSOP /* Sysop? (1/0) */ - ,term_supports(ANSI) /* ANSI ? (1/0) */ + ,INT_TO_BOOL(term & ANSI) /* ANSI ? (1/0) */ ,online==ON_REMOTE); /* Remote (1/0) */ lfexpand(str,misc); write(file,str,strlen(str)); @@ -416,8 +417,8 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,unixtodstr(&cfg,useron.laston,tmp) /* 17: User last on date */ ,tleft /* 18: User time left in sec */ ,tleft/60 /* 19: User time left in min */ - ,useron.misc&NO_EXASCII /* 20: GR if COLOR ANSI */ - ? "7E" : (useron.misc&(ANSI|COLOR))==(ANSI|COLOR) ? "GR" : "NG"); + ,(term & NO_EXASCII) /* 20: GR if COLOR ANSI */ + ? "7E" : (term & (ANSI|COLOR)) == (ANSI|COLOR) ? "GR" : "NG"); lfexpand(str,misc); write(file,str,strlen(str)); @@ -451,7 +452,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl localtime_r(&ns_time,&tm); safe_snprintf(str, sizeof(str), "%c\n%c\n%u\n%lu\n%02d/%02d/%02d\n" - ,(useron.misc&(NO_EXASCII|ANSI|COLOR))==ANSI + ,(term & (NO_EXASCII|ANSI|COLOR)) == ANSI ? 'Y':'N' /* 39: ANSI supported but NG mode */ ,'Y' /* 40: Use record locking */ ,cfg.color[clr_external] /* 41: BBS default color */ @@ -522,11 +523,11 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl *(p++)=0; else p=nulstr; - safe_snprintf(str, sizeof(str), "%s\n%s\n%s\n%ld\n%u\n%lu\n" + safe_snprintf(str, sizeof(str), "%s\n%s\n%s\n%d\n%u\n%lu\n" ,tmp /* User's firstname */ ,p /* User's lastname */ ,useron.location /* User's city */ - ,term_supports(ANSI) /* 1=ANSI 0=ASCII */ + ,INT_TO_BOOL(term & ANSI) /* 1=ANSI 0=ASCII */ ,useron.level /* Security level */ ,tleft/60); /* Time left in minutes */ strupr(str); @@ -577,7 +578,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl if(useron.misc&DELETED) c|=(1<<0); if(useron.misc&CLRSCRN) c|=(1<<1); if(useron.misc&UPAUSE) c|=(1<<2); - if(term_supports(ANSI)) c|=(1<<3); + if(term & ANSI) c|=(1<<3); if(useron.sex=='F') c|=(1<<7); write(file,&c,1); /* Attrib */ write(file,&useron.flags1,4); /* Flags */ @@ -655,7 +656,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl write(file,&c,1); /* ScreenClear */ c=useron.misc&UPAUSE ? 1:0; write(file,&c,1); /* MorePrompts */ - c=useron.misc&NO_EXASCII ? 0:1; + c=(term & NO_EXASCII) ? 0:1; write(file,&c,1); /* GraphicsMode */ c=useron.xedit ? 1:0; write(file,&c,1); /* ExternEdit */ @@ -666,7 +667,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl write(file,str,49); /* ChatReason */ c=0; write(file,&c,1); /* ExternLogoff */ - c=(char)term_supports(ANSI); + c=(char)INT_TO_BOOL(term & ANSI); write(file,&c,1); /* ANSI_Capable */ close(file); } @@ -713,7 +714,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,useron.location /* User location */ ,useron.level /* Security level */ ,tleft/60 /* Time left in min */ - ,term_supports(ANSI) ? "COLOR":"MONO" /* ANSI ??? */ + ,(term & ANSI) ? "COLOR":"MONO" /* ANSI ??? */ ,useron.pass /* Password */ ,useron.number); /* User number */ lfexpand(str,misc); @@ -801,8 +802,8 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,startup->answer_sound[0] ? -1:0 /* Caller Alarm on/off */ ,' ' /* Sysop next flag */ ,0 /* Error corrected */ - ,useron.misc&NO_EXASCII ? '7' /* Graphics mode */ - : (useron.misc&(COLOR|ANSI))==(COLOR|ANSI) ? 'Y':'N' + ,(term & NO_EXASCII) ? '7' /* Graphics mode */ + : (term & (COLOR|ANSI)) == (COLOR|ANSI) ? 'Y':'N' ,'A' /* Node chat status */ ,(uint)dte_rate /* DTE Port Speed */ ,connection /* Connection description */ @@ -869,11 +870,11 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl l=0L; write(file,&l,4); /* Memorized message number */ - safe_snprintf(str, sizeof(str), "%d%c%c%ld%s%c%c%d%d%d%c%c" + safe_snprintf(str, sizeof(str), "%d%c%c%d%s%c%c%d%d%d%c%c" ,cfg.com_port /* COM Port number */ ,' ' /* Reserved */ ,' ' /* "" */ - ,term_supports(ANSI) /* 1=ANSI 0=NO ANSI */ + ,INT_TO_BOOL(term & ANSI) /* 1=ANSI 0=NO ANSI */ ,"01-01-80" /* last event date */ ,0,0 /* last event minute */ ,0 /* caller exited to dos */ @@ -1036,7 +1037,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl "%s\n%s\n%lu\n%s\n%u\n%u\n%u\n%u\n%u\n%lu\n%u\n" "%lu\n%lu\n%s\n%s\n" ,dropdir - ,term_supports(ANSI) ? "TRUE":"FALSE" /* ANSI ? True or False */ + ,(term & ANSI) ? "TRUE":"FALSE" /* ANSI ? True or False */ ,useron.level /* Security level */ ,useron.uls /* Total uploads */ ,useron.dls /* Total downloads */ @@ -1102,10 +1103,10 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl return; } - safe_snprintf(str, sizeof(str), "%s\n%ld\n%d\n%lu\n%lu\n%u\n%lu\n" + safe_snprintf(str, sizeof(str), "%s\n%d\n%d\n%lu\n%lu\n%u\n%lu\n" ,name /* Complete name of user */ - ,term_supports(ANSI) /* ANSI ? */ - ,term_supports(NO_EXASCII) ? 0:1 /* IBM characters ? */ + ,INT_TO_BOOL(term & ANSI) /* ANSI ? */ + ,!INT_TO_BOOL(term & NO_EXASCII) /* IBM characters ? */ ,rows /* Page length */ ,dte_rate /* Baud rate */ ,online==ON_LOCAL ? 0:cfg.com_port /* COM port */ @@ -1133,7 +1134,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,useron.pass /* User's password */ ,useron.level /* User's level */ ,useron.misc&EXPERT ? 'Y':'N' /* Expert? */ - ,term_supports(ANSI) ? 'Y':'N' /* ANSI? */ + ,(term & ANSI) ? 'Y':'N' /* ANSI? */ ,tleft/60 /* Minutes left */ ,useron.phone /* User's phone number */ ,useron.location /* User's city and state */ @@ -1170,7 +1171,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl } safe_snprintf(str, sizeof(str), "%d\n%d\n%lu\n%s%c\n%d\n%s\n%s\n%d\n%ld\n" - "%ld\n%d\n" + "%d\n%d\n" ,misc&(XTRN_STDIO|XTRN_CONIO) ? 0 /* Local */ : 2 /* Telnet */ ,misc&(XTRN_STDIO|XTRN_CONIO) ? INVALID_SOCKET : client_socket_dup ,dte_rate @@ -1180,7 +1181,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl ,name ,useron.level ,tleft/60 - ,term_supports(ANSI) + ,INT_TO_BOOL(term & ANSI) ,cfg.node_num); lfexpand(str,misc); write(file,str,strlen(str)); diff --git a/src/sbbs3/zmodem.c b/src/sbbs3/zmodem.c index 1270f77d2ab23971e1906c9cb43bc391873cfabf..cbde82b0977700538e3160ec2f91ff32381e1c13 100755 --- a/src/sbbs3/zmodem.c +++ b/src/sbbs3/zmodem.c @@ -2,8 +2,6 @@ /* Synchronet ZMODEM Functions */ -/* $Id: zmodem.c,v 1.124 2019/08/25 03:05:34 rswindell Exp $ */ - /******************************************************************************/ /* Project : Unite! File : zmodem general Version : 1.02 */ /* */ @@ -62,6 +60,8 @@ #define BADSUBPKT 0x80 +#define SEND_SUCCESS 0 + #define HDRLEN 5 /* size of a zmodem header */ static int lprintf(zmodem_t* zm, int level, const char *fmt, ...) @@ -96,7 +96,7 @@ static BOOL is_cancelled(zmodem_t* zm) return(zm->cancelled); } -int zmodem_data_waiting(zmodem_t* zm, unsigned timeout) +static BOOL is_data_waiting(zmodem_t* zm, unsigned timeout) { if(zm->data_waiting) return(zm->data_waiting(zm->cbdata, timeout)); @@ -111,7 +111,7 @@ static char *chr(int ch) case TIMEOUT: return("TIMEOUT"); case ABORTED: return("ABORTED"); case SUBPKTOVERFLOW: return "Subpacket Overflow"; - case CRCFAILED: return "CRC Failure"; + case CRCFAILED: return "CRC ERROR"; case INVALIDSUBPKT: return "Invalid Subpacket"; case ZRQINIT: return("ZRQINIT"); case ZRINIT: return("ZRINIT"); @@ -241,7 +241,7 @@ int zmodem_send_raw(zmodem_t* zm, unsigned char ch) { int result; - if((result = zm->send_byte(zm->cbdata, ch, zm->send_timeout)) != 0) + if((result = zm->send_byte(zm->cbdata, ch, zm->send_timeout)) != SEND_SUCCESS) lprintf(zm, LOG_ERR, "%s ERROR: %d", __FUNCTION__, result); else zm->last_sent = ch; @@ -257,7 +257,7 @@ int zmodem_send_esc(zmodem_t* zm, unsigned char c) { int result; - if((result = zmodem_send_raw(zm, ZDLE)) != 0) { + if((result = zmodem_send_raw(zm, ZDLE)) != SEND_SUCCESS) { lprintf(zm, LOG_ERR, "%s ERROR: %d", __FUNCTION__, result); return result; } @@ -291,7 +291,7 @@ int zmodem_tx(zmodem_t* zm, unsigned char c) break; case TELNET_IAC: if(zm->escape_telnet_iac) { - if((result=zmodem_send_raw(zm, ZDLE))!=0) + if((result=zmodem_send_raw(zm, ZDLE)) != SEND_SUCCESS) return(result); return zmodem_send_raw(zm, ZRUB1); } @@ -317,7 +317,7 @@ int zmodem_send_hex(zmodem_t* zm, uchar val) // lprintf(zm, LOG_DEBUG, __FUNCTION__ " %02X",val); - if((result=zmodem_send_raw(zm, xdigit[val>>4]))!=0) + if((result=zmodem_send_raw(zm, xdigit[val>>4])) != SEND_SUCCESS) return result; return zmodem_send_raw(zm, xdigit[val&0xf]); } @@ -326,9 +326,9 @@ int zmodem_send_padded_zdle(zmodem_t* zm) { int result; - if((result=zmodem_send_raw(zm, ZPAD))!=0) + if((result=zmodem_send_raw(zm, ZPAD)) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, ZPAD))!=0) + if((result=zmodem_send_raw(zm, ZPAD)) != SEND_SUCCESS) return result; return zmodem_send_raw(zm, ZDLE); } @@ -347,10 +347,10 @@ int zmodem_send_hex_header(zmodem_t* zm, unsigned char * p) // lprintf(zm, LOG_DEBUG, __FUNCTION__ " %s", chr(type)); - if((result=zmodem_send_padded_zdle(zm))!=0) + if((result=zmodem_send_padded_zdle(zm)) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, ZHEX))!=0) + if((result=zmodem_send_raw(zm, ZHEX)) != SEND_SUCCESS) return result; /* @@ -364,7 +364,7 @@ int zmodem_send_hex_header(zmodem_t* zm, unsigned char * p) */ for(i=0;i<HDRLEN;i++) { - if((result=zmodem_send_hex(zm, *p))!=0) + if((result=zmodem_send_hex(zm, *p)) != SEND_SUCCESS) return result; crc = ucrc16(*p, crc); p++; @@ -378,18 +378,18 @@ int zmodem_send_hex_header(zmodem_t* zm, unsigned char * p) * transmit the crc */ - if((result=zmodem_send_hex(zm, (uchar)(crc>>8)))!=0) + if((result=zmodem_send_hex(zm, (uchar)(crc>>8))) != SEND_SUCCESS) return result; - if((result=zmodem_send_hex(zm, (uchar)(crc&0xff)))!=0) + if((result=zmodem_send_hex(zm, (uchar)(crc&0xff))) != SEND_SUCCESS) return result; /* * end of line sequence */ - if((result=zmodem_send_raw(zm, '\r'))!=0) + if((result=zmodem_send_raw(zm, '\r')) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, '\n'))!=0) /* FDSZ sends 0x8a instead of 0x0a */ + if((result=zmodem_send_raw(zm, '\n')) != SEND_SUCCESS) /* FDSZ sends 0x8a instead of 0x0a */ return result; if(type!=ZACK && type!=ZFIN) @@ -412,27 +412,27 @@ int zmodem_send_bin32_header(zmodem_t* zm, unsigned char * p) // lprintf(zm, LOG_DEBUG, __FUNCTION__ " %s", chr(*p)); - if((result=zmodem_send_padded_zdle(zm))!=0) + if((result=zmodem_send_padded_zdle(zm)) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, ZBIN32))!=0) + if((result=zmodem_send_raw(zm, ZBIN32)) != SEND_SUCCESS) return result; crc = 0xffffffffL; for(i=0;i<HDRLEN;i++) { crc = ucrc32(*p,crc); - if((result=zmodem_tx(zm, *p++))!=0) + if((result=zmodem_tx(zm, *p++)) != SEND_SUCCESS) return result; } crc = ~crc; - if((result= zmodem_tx(zm, (uchar)((crc ) & 0xff)))!=0) + if((result= zmodem_tx(zm, (uchar)((crc ) & 0xff))) != SEND_SUCCESS) return result; - if((result= zmodem_tx(zm, (uchar)((crc >> 8) & 0xff)))!=0) + if((result= zmodem_tx(zm, (uchar)((crc >> 8) & 0xff))) != SEND_SUCCESS) return result; - if((result= zmodem_tx(zm, (uchar)((crc >> 16) & 0xff)))!=0) + if((result= zmodem_tx(zm, (uchar)((crc >> 16) & 0xff))) != SEND_SUCCESS) return result; return zmodem_tx(zm, (uchar)((crc >> 24) & 0xff)); } @@ -445,21 +445,21 @@ int zmodem_send_bin16_header(zmodem_t* zm, unsigned char * p) // lprintf(zm, LOG_DEBUG, __FUNCTION__ " %s", chr(*p)); - if((result=zmodem_send_padded_zdle(zm))!=0) + if((result=zmodem_send_padded_zdle(zm)) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, ZBIN))!=0) + if((result=zmodem_send_raw(zm, ZBIN)) != SEND_SUCCESS) return result; crc = 0; for(i=0;i<HDRLEN;i++) { crc = ucrc16(*p,crc); - if((result=zmodem_tx(zm, *p++))!=0) + if((result=zmodem_tx(zm, *p++)) != SEND_SUCCESS) return result; } - if((result= zmodem_tx(zm, (uchar)(crc >> 8)))!=0) + if((result= zmodem_tx(zm, (uchar)(crc >> 8))) != SEND_SUCCESS) return result; return zmodem_tx(zm, (uchar)(crc&0xff)); } @@ -494,25 +494,25 @@ int zmodem_send_data32(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_ while(l > 0) { crc = ucrc32(*p,crc); - if((result=zmodem_tx(zm, *p++))!=0) + if((result=zmodem_tx(zm, *p++)) != SEND_SUCCESS) return result; l--; } crc = ucrc32(subpkt_type, crc); - if((result=zmodem_send_raw(zm, ZDLE))!=0) + if((result=zmodem_send_raw(zm, ZDLE)) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, subpkt_type))!=0) + if((result=zmodem_send_raw(zm, subpkt_type)) != SEND_SUCCESS) return result; crc = ~crc; - if((result= zmodem_tx(zm, (uchar) ((crc ) & 0xff)))!=0) + if((result= zmodem_tx(zm, (uchar) ((crc ) & 0xff))) != SEND_SUCCESS) return result; - if((result= zmodem_tx(zm, (uchar) ((crc >> 8 ) & 0xff)))!=0) + if((result= zmodem_tx(zm, (uchar) ((crc >> 8 ) & 0xff))) != SEND_SUCCESS) return result; - if((result= zmodem_tx(zm, (uchar) ((crc >> 16) & 0xff)))!=0) + if((result= zmodem_tx(zm, (uchar) ((crc >> 16) & 0xff))) != SEND_SUCCESS) return result; return zmodem_tx(zm, (uchar) ((crc >> 24) & 0xff)); } @@ -528,42 +528,47 @@ int zmodem_send_data16(zmodem_t* zm, uchar subpkt_type,unsigned char * p, size_t while(l > 0) { crc = ucrc16(*p,crc); - if((result=zmodem_tx(zm, *p++))!=0) + if((result=zmodem_tx(zm, *p++)) != SEND_SUCCESS) return result; l--; } crc = ucrc16(subpkt_type,crc); - if((result=zmodem_send_raw(zm, ZDLE))!=0) + if((result=zmodem_send_raw(zm, ZDLE)) != SEND_SUCCESS) return result; - if((result=zmodem_send_raw(zm, subpkt_type))!=0) + if((result=zmodem_send_raw(zm, subpkt_type)) != SEND_SUCCESS) return result; - if((result= zmodem_tx(zm, (uchar)(crc >> 8)))!=0) + if((result= zmodem_tx(zm, (uchar)(crc >> 8))) != SEND_SUCCESS) return result; return zmodem_tx(zm, (uchar)(crc&0xff)); } +BOOL zmodem_end_of_frame(int subpkt_type) +{ + return subpkt_type == ZCRCW || subpkt_type == ZCRCE; +} + /* * send a data subpacket using crc 16 or crc 32 as desired by the receiver */ -int zmodem_send_data_subpkt(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t l) +int zmodem_send_data_subpkt(zmodem_t* zm, uchar subpkt_type, unsigned char* data, size_t len) { int result; - if(subpkt_type == ZCRCW || subpkt_type == ZCRCE) /* subpacket indicating 'end-of-frame' */ + if(zmodem_end_of_frame(subpkt_type)) zm->frame_in_transit=FALSE; else /* other subpacket (mid-frame) */ zm->frame_in_transit=TRUE; if(!zm->want_fcs_16 && zm->can_fcs_32) { - if((result=zmodem_send_data32(zm, subpkt_type,p,l))!=0) + if((result=zmodem_send_data32(zm, subpkt_type, data, len)) != SEND_SUCCESS) return result; } else { - if((result=zmodem_send_data16(zm, subpkt_type,p,l))!=0) + if((result=zmodem_send_data16(zm, subpkt_type, data, len)) != SEND_SUCCESS) return result; } @@ -575,7 +580,7 @@ int zmodem_send_data_subpkt(zmodem_t* zm, uchar subpkt_type, unsigned char * p, return result; } -int zmodem_send_data(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t len) +int zmodem_send_data(zmodem_t* zm, uchar subpkt_type, unsigned char* data, size_t len) { if(!zm->frame_in_transit) { /* Start of frame, include ZDATA header */ lprintf(zm, LOG_DEBUG, "%lu %s Start of frame: %s" @@ -583,7 +588,7 @@ int zmodem_send_data(zmodem_t* zm, uchar subpkt_type, unsigned char * p, size_t zmodem_send_pos_header(zm, ZDATA, (uint32_t)zm->current_file_pos, /* Hex? */ FALSE); } - return zmodem_send_data_subpkt(zm, subpkt_type, p, len); + return zmodem_send_data_subpkt(zm, subpkt_type, data, len); } int zmodem_send_pos_header(zmodem_t* zm, int type, int32_t pos, BOOL hex) @@ -862,7 +867,7 @@ int zmodem_recv_data32(zmodem_t* zm, unsigned char * p, unsigned maxlen, unsigne rxd_crc |= zmodem_rx(zm) << 24; if(rxd_crc != crc) { - lprintf(zm,LOG_WARNING, "%lu %s CRC ERROR (%08lX, expected: %08lX) Bytes=%u, subpacket type=%s" + lprintf(zm, LOG_DEBUG, "%lu %s CRC ERROR (%08lX, expected: %08lX) Bytes=%u, subpacket type=%s" ,(ulong)zm->ack_file_pos, __FUNCTION__, rxd_crc, crc, *len, chr(subpkt_type)); return CRCFAILED; } @@ -911,7 +916,7 @@ int zmodem_recv_data16(zmodem_t* zm, register unsigned char* p, unsigned maxlen, rxd_crc |= zmodem_rx(zm); if(rxd_crc != crc) { - lprintf(zm,LOG_WARNING, "%lu %s CRC ERROR (%04hX, expected: %04hX) Bytes=%u, subpacket type=%s" + lprintf(zm, LOG_DEBUG, "%lu %s CRC ERROR (%04hX, expected: %04hX) Bytes=%u, subpacket type=%s" ,(ulong)zm->ack_file_pos, __FUNCTION__, rxd_crc, crc, *len, chr(subpkt_type)); return CRCFAILED; } @@ -923,7 +928,7 @@ int zmodem_recv_data16(zmodem_t* zm, register unsigned char* p, unsigned maxlen, return subpkt_type; } -int zmodem_recv_data(zmodem_t* zm, unsigned char* p, size_t maxlen, unsigned* len, BOOL ack, int* type) +int zmodem_recv_data(zmodem_t* zm, unsigned char* buf, size_t maxlen, unsigned* len, BOOL ack, int* type) { int subpkt_type; unsigned n=0; @@ -940,15 +945,15 @@ int zmodem_recv_data(zmodem_t* zm, unsigned char* p, size_t maxlen, unsigned* le *len = 0; if(zm->receive_32bit_data) { - subpkt_type = zmodem_recv_data32(zm, p, maxlen, len, type); + subpkt_type = zmodem_recv_data32(zm, buf, maxlen, len, type); } else { - subpkt_type = zmodem_recv_data16(zm, p, maxlen, len, type); + subpkt_type = zmodem_recv_data16(zm, buf, maxlen, len, type); } if(subpkt_type <= 0) { /* e.g. TIMEOUT, SUBPKTOVERFLOW, CRCFAILED */ - lprintf(zm, LOG_WARNING, "%lu %s ERROR: %s (after %u bytes)" - ,(ulong)zm->ack_file_pos, __FUNCTION__, chr(subpkt_type), *len); + lprintf(zm, LOG_WARNING, "%s data subpacket (%u bytes) %s" + ,chr(*type), *len, chr(subpkt_type)); return(subpkt_type); } @@ -995,7 +1000,7 @@ BOOL zmodem_recv_subpacket(zmodem_t* zm, BOOL ack, int* type) result = zmodem_recv_data(zm,zm->rx_data_subpacket, sizeof(zm->rx_data_subpacket), &len, ack, type); if(result != FRAMEOK && result != ENDOFFRAME) { - lprintf(zm, LOG_ERR, "%lu %s ERROR: %s (subpacket type: %s, %u bytes)" + lprintf(zm, LOG_DEBUG, "%lu %s ERROR: %s (subpacket type: %s, %u bytes)" ,(ulong)zm->ack_file_pos, __FUNCTION__, chr(result), chr(*type), len); zmodem_send_znak(zm); return(FALSE); @@ -1100,7 +1105,7 @@ BOOL zmodem_recv_bin16_header(zmodem_t* zm) // lprintf(zm,LOG_DEBUG, "%lu %s GOOD CRC: %04hX", __FUNCTION__ // ,(ulong)zm->ack_file_pos, __FUNCTION__, crc); - zm->rxd_header_len = 5; + zm->rxd_header_len = n; return(TRUE); } @@ -1143,7 +1148,7 @@ BOOL zmodem_recv_hex_header(zmodem_t* zm) if(rxd_crc == crc) { // lprintf(zm,LOG_DEBUG, "%s GOOD CRC: %04hX", __FUNCTION__, crc); - zm->rxd_header_len = 5; + zm->rxd_header_len = i; } else { lprintf(zm,LOG_WARNING, "%s CRC ERROR: 0x%hX, expected: 0x%hX" @@ -1203,7 +1208,7 @@ BOOL zmodem_recv_bin32_header(zmodem_t* zm) } // lprintf(zm,LOG_DEBUG, "%lu %s GOOD CRC: %08lX", (ulong)zm->ack_file_pos, __FUNCTION__, crc); - zm->rxd_header_len = 5; + zm->rxd_header_len = n; return(TRUE); } @@ -1369,7 +1374,7 @@ BOOL zmodem_recv_crc(zmodem_t* zm, uint32_t* crc) { int type; - if(!zmodem_data_waiting(zm,zm->crc_timeout)) { + if(!is_data_waiting(zm,zm->crc_timeout)) { lprintf(zm,LOG_ERR, "%lu %s Timeout waiting for response (%u seconds)" ,(ulong)zm->current_file_pos, __FUNCTION__, zm->crc_timeout); return(FALSE); @@ -1425,7 +1430,7 @@ int zmodem_get_zrinit(zmodem_t* zm) zmodem_send_raw(zm,'\r'); zmodem_send_hex_header(zm,zrqinit_header); - if(!zmodem_data_waiting(zm,zm->init_timeout)) + if(!is_data_waiting(zm,zm->init_timeout)) return(TIMEOUT); return zmodem_recv_header(zm); } @@ -1434,7 +1439,8 @@ int zmodem_send_zrinit(zmodem_t* zm) { unsigned char zrinit_header[] = { ZRINIT, 0, 0, 0, 0 }; - zrinit_header[ZF0] = ZF0_CANFDX; + if(zm->can_full_duplex) + zrinit_header[ZF0] = ZF0_CANFDX; if(!zm->no_streaming) zrinit_header[ZF0] |= ZF0_CANOVIO; @@ -1452,7 +1458,7 @@ int zmodem_send_zrinit(zmodem_t* zm) zrinit_header[ZF0] |= ZF0_ESC8; if(zm->no_streaming && zm->recv_bufsize==0) - zm->recv_bufsize = sizeof(zm->rx_data_subpacket); + zm->recv_bufsize = zm->max_block_size; zrinit_header[ZP0] = zm->recv_bufsize & 0xff; zrinit_header[ZP1] = zm->recv_bufsize >> 8; @@ -1474,7 +1480,7 @@ int zmodem_get_zfin(zmodem_t* zm) result = zmodem_send_zabort(zm); else result = zmodem_send_zfin(zm); - if(result != 0) + if(result != SEND_SUCCESS) return result; if((type = zmodem_recv_header(zm)) == ZFIN) break; @@ -1496,9 +1502,10 @@ int zmodem_get_zfin(zmodem_t* zm) BOOL zmodem_handle_zrpos(zmodem_t* zm, uint64_t* pos) { - if(zm->rxd_header_pos <= zm->current_file_size) { + if(zm->rxd_header_pos < zm->current_file_size) { if(*pos != zm->rxd_header_pos) { *pos = zm->rxd_header_pos; + zm->ack_file_pos = (int32_t)*pos; lprintf(zm, LOG_INFO, "%lu Resuming transfer from offset: %"PRIu64 ,(ulong)zm->current_file_pos, *pos); } @@ -1510,10 +1517,11 @@ BOOL zmodem_handle_zrpos(zmodem_t* zm, uint64_t* pos) return FALSE; } -BOOL zmodem_handle_zack(zmodem_t* zm) +BOOL zmodem_handle_zack(zmodem_t* zm, uint32_t min, uint32_t max) { - if(zm->rxd_header_pos == zm->current_file_pos) { + if(zm->rxd_header_pos >= min && zm->rxd_header_pos <= max) { lprintf(zm, LOG_DEBUG, "%lu Received valid ZACK", zm->rxd_header_pos); + zm->ack_file_pos = zm->rxd_header_pos; return TRUE; } lprintf(zm, LOG_WARNING, "%lu Received INVALID ZACK, offset: %lu" @@ -1521,6 +1529,17 @@ BOOL zmodem_handle_zack(zmodem_t* zm) return FALSE; } +static unsigned new_window_size(zmodem_t* zm, time_t start, unsigned pos) +{ + time_t elapsed = time(NULL) - start; + if(elapsed < 1) + elapsed = 1; + unsigned cps = (unsigned)(pos / elapsed); + if(cps < 1) + cps = 1; + return cps * zm->target_window_size; +} + /* * send from the current position in the file * all the way to end of file or until something goes wrong. @@ -1530,22 +1549,24 @@ BOOL zmodem_handle_zack(zmodem_t* zm) int zmodem_send_from(zmodem_t* zm, FILE* fp, uint64_t pos, uint64_t* sent) { - size_t n; - uchar type; - unsigned buf_sent=0; - unsigned subpkts_sent=0; - + size_t len; + uchar tx_type; + unsigned buf_sent = 0; + unsigned subpkts_sent = 0; + unsigned backchannel_wait = 0; + time_t start = time(NULL); + + lprintf(zm, LOG_DEBUG, "%lu %s", (ulong)pos, __FUNCTION__); if(sent!=NULL) *sent=0; - if(fseeko(fp,(off_t)pos,SEEK_SET)!=0) { + if(fseeko(fp,(off_t)pos,SEEK_SET) != 0) { lprintf(zm, LOG_ERR, "%s ERROR %d seeking to file offset %"PRIu64 ,__FUNCTION__, errno, pos); zmodem_send_pos_header(zm, ZFERR, (uint32_t)pos, /* Hex? */ TRUE); return ZFERR; } - zm->current_file_pos=pos; - + zm->current_file_pos = pos; /* * send the data in the file @@ -1553,16 +1574,61 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint64_t pos, uint64_t* sent) while(is_connected(zm)) { + /* + * characters from the other side + * check out that header + */ + if(zm->consecutive_errors && backchannel_wait == 0) + backchannel_wait = 1; + while(is_data_waiting(zm, backchannel_wait) && !is_cancelled(zm) && is_connected(zm)) { + int rx_type; + int c; + lprintf(zm, LOG_DEBUG, "Back-channel traffic detected"); + if((c = zmodem_recv_raw(zm)) < 0) { + lprintf(zm, LOG_ERR, "Back-channel receive ERROR: %s", chr(c)); + return(c); + } + if(c == ZPAD) { + rx_type = zmodem_recv_header(zm); + lprintf(zm,LOG_DEBUG, "Received back-channel data: %s", chr(rx_type)); + if(rx_type == ZACK && zmodem_handle_zack(zm, zm->ack_file_pos, (uint32_t)zm->current_file_pos)) { + zm->current_window_size = zm->current_file_pos - zm->ack_file_pos; + lprintf(zm, LOG_DEBUG, "%lu Asynchronous acknowledgment (ZACK) of %lu bytes, new window: %lu" + ,(ulong)zm->current_file_pos, (ulong)zm->ack_file_pos, (ulong)zm->current_window_size); + if(zm->max_window_size && zm->target_window_size) { + zm->max_window_size = new_window_size(zm, start, zm->rxd_header_pos); + lprintf(zm, LOG_DEBUG, "%lu New window size: %lu (%u seconds of data)" + ,(ulong)zm->current_file_pos, (ulong)zm->max_window_size, zm->target_window_size); + } + continue; + } + else if(rx_type >= 0) { + zmodem_send_data(zm, ZCRCE, /* data: */NULL, /* len: */0); + return rx_type; + } + } else + lprintf(zm,LOG_INFO, "Unexpected back-channel data received: %s", chr(c)); + } + if(is_cancelled(zm)) + return(ZCAN); + /* * read a block from the file */ pos = zm->current_file_pos; - n = fread(zm->tx_data_subpacket,sizeof(BYTE),zm->block_size,fp); + if(zm->max_window_size && zm->current_window_size >= zm->max_window_size) { + lprintf(zm, LOG_WARNING, "%lu Transmit-Window management: %lu >= %lu" + ,(ulong)zm->current_file_pos, (ulong)zm->current_window_size, (ulong)zm->max_window_size); + backchannel_wait = 1; + continue; + } + + len = fread(zm->tx_data_subpacket,sizeof(BYTE),zm->block_size,fp); if(zm->progress!=NULL) zm->progress(zm->cbdata, ftello(fp)); - type = ZCRCW; + tx_type = ZCRCW; /** ZMODEM.DOC: ZCRCW data subpackets expect a response before the next frame is sent. @@ -1571,36 +1637,43 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint64_t pos, uint64_t* sent) the receiver to write its buffer before sending more data. ***/ /* Note: we always use ZCRCW for the first frame */ - if(subpkts_sent || n < zm->block_size) { + if(subpkts_sent || len < zm->block_size) { /* ZMODEM.DOC: In the absence of fatal error, the sender eventually encounters end of file. If the end of file is encountered within a frame, the frame is closed with a ZCRCE data subpacket which does not elicit a response except in case of error. */ - if(n < zm->block_size) - type = ZCRCE; + if(len < zm->block_size || zm->consecutive_errors) + tx_type = ZCRCE; else { - if(zm->can_overlap_io && !zm->no_streaming && (zm->recv_bufsize==0 || buf_sent+n < zm->recv_bufsize)) - type = ZCRCG; + if(zm->can_overlap_io && !zm->no_streaming && (zm->recv_bufsize==0 || buf_sent + len < zm->recv_bufsize)) { + if(zm->can_full_duplex && zm->max_window_size) + tx_type = (subpkts_sent % (zm->max_window_size / zm->block_size / 4)) == 0 ? ZCRCQ : ZCRCG; + else + tx_type = ZCRCG; + } else /* Send a ZCRCW frame */ buf_sent = 0; } } - /* Note: No support for sending ZCRCQ data subpackets here */ - - if(zmodem_send_data(zm, type, zm->tx_data_subpacket, n) != 0) - return(TIMEOUT); + lprintf(zm, LOG_DEBUG, "%lu Sending %s data subpacket (%u bytes) window: %lu / %lu" + ,(ulong)pos, chr(tx_type), len, (ulong)zm->current_window_size, (ulong)zm->max_window_size); + if(zmodem_send_data(zm, tx_type, zm->tx_data_subpacket, len) != SEND_SUCCESS) { + zm->consecutive_errors++; + continue; + } - zm->current_file_pos += n; + zm->current_window_size += len; + zm->current_file_pos += len; if(zm->current_file_pos > zm->current_file_size) zm->current_file_size = zm->current_file_pos; subpkts_sent++; - if(type == ZCRCW || type == ZCRCE) { - lprintf(zm, LOG_DEBUG, "%lu Sent end-of-frame (%s subpacket)",(ulong)pos, chr(type)); - if(type == ZCRCW) { /* ZACK expected */ + if(zmodem_end_of_frame(tx_type)) { + lprintf(zm, LOG_DEBUG, "%lu Sent end-of-frame (%s subpacket)", (ulong)pos, chr(tx_type)); + if(tx_type == ZCRCW) { /* ZACK expected */ lprintf(zm, LOG_DEBUG, "%lu Waiting for ZACK", (ulong)pos); while(is_connected(zm)) { int ack; @@ -1610,54 +1683,25 @@ int zmodem_send_from(zmodem_t* zm, FILE* fp, uint64_t pos, uint64_t* sent) if(is_cancelled(zm)) return(ZCAN); - if(zmodem_handle_zack(zm)) + if(zmodem_handle_zack(zm, (uint32_t)zm->current_file_pos, (uint32_t)zm->current_file_pos)) { + zm->current_window_size = 0; break; + } } } } if(sent!=NULL) - *sent+=n; + *sent += len; - buf_sent+=n; + buf_sent += len; - if(n < zm->block_size) { - lprintf(zm,LOG_DEBUG, "%lu End of file (or read error) reached", (ulong)zm->current_file_pos); + if(len < zm->block_size) { + lprintf(zm, LOG_DEBUG, "%lu End of file (or read error) reached", (ulong)zm->current_file_pos); zmodem_send_zeof(zm); return zmodem_recv_header(zm); /* If this is ZRINIT, Success */ } - - /* - * characters from the other side - * check out that header - */ - - while(zmodem_data_waiting(zm, zm->consecutive_errors ? 1:0) - && !is_cancelled(zm) && is_connected(zm)) { - int rx_type; - int c; - lprintf(zm, LOG_DEBUG, "Back-channel traffic detected"); - if((c = zmodem_recv_raw(zm)) < 0) { - lprintf(zm, LOG_ERR, "Back-channel receive ERROR: %s", chr(c)); - return(c); - } - if(c == ZPAD) { - /* ZMODEM.DOC: - FULL STREAMING WITH SAMPLING - If one of these characters (CAN or ZPAD) is seen, an - empty ZCRCE data subpacket is sent. - */ - zmodem_send_data(zm, ZCRCE, NULL, 0); - rx_type = zmodem_recv_header(zm); - lprintf(zm,LOG_DEBUG, "Received back-channel data: %s", chr(rx_type)); - if(rx_type >= 0) { - return rx_type; - } - } else - lprintf(zm,LOG_INFO, "Unexpected back-channel data received: %s", chr(c)); - } - if(is_cancelled(zm)) - return(ZCAN); + backchannel_wait = 0; zm->consecutive_errors = 0; @@ -1820,11 +1864,11 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti lprintf(zm,LOG_DEBUG,"Sending ZFILE frame: '%s'" ,zm->tx_data_subpacket+strlen((char*)zm->tx_data_subpacket)+1); - if((i=zmodem_send_bin_header(zm,zfile_frame))!=0) { + if((i=zmodem_send_bin_header(zm,zfile_frame)) != SEND_SUCCESS) { lprintf(zm,LOG_DEBUG,"zmodem_send_bin_header returned %d",i); continue; } - if((i=zmodem_send_data_subpkt(zm,ZCRCW,zm->tx_data_subpacket,p - zm->tx_data_subpacket))!=0) { + if((i=zmodem_send_data_subpkt(zm,ZCRCW,zm->tx_data_subpacket,p - zm->tx_data_subpacket)) != SEND_SUCCESS) { lprintf(zm,LOG_DEBUG,"zmodem_send_data_subpkt returned %d",i); continue; } @@ -1905,15 +1949,16 @@ BOOL zmodem_send_file(zmodem_t* zm, char* fname, FILE* fp, BOOL request_init, ti if(type == ZRINIT) return(TRUE); /* Success */ - if(type == ZACK && zmodem_handle_zack(zm)) { - pos += sent_bytes; + pos = zm->ack_file_pos; + + if(type == ZACK && zmodem_handle_zack(zm, (uint32_t)pos, (uint32_t)zm->current_file_pos)) { continue; } /* Error of some kind */ zm->errors++; - lprintf(zm, LOG_ERR, "%lu ERROR #%d: Received %s", (ulong)zm->current_file_pos, zm->errors, chr(type)); + lprintf(zm, LOG_ERR, "%lu ERROR #%d: %s", (ulong)zm->current_file_pos, zm->errors, chr(type)); if(zm->block_size == zm->max_block_size && zm->max_block_size > ZBLOCKLEN) zm->max_block_size /= 2; @@ -2202,7 +2247,7 @@ unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, int64_t offset) zm->transfer_start_pos=offset; zm->transfer_start_time=time(NULL); - if(fseeko(fp, pos, SEEK_SET)!=0) { + if(fseeko(fp, pos, SEEK_SET) != 0) { lprintf(zm,LOG_ERR, "%s ERROR %d seeking to file offset %"PRId64 ,__FUNCTION__, errno, offset); zmodem_send_pos_header(zm, ZFERR, (uint32_t)pos, /* Hex? */ TRUE); @@ -2224,7 +2269,7 @@ unsigned zmodem_recv_file_data(zmodem_t* zm, FILE* fp, int64_t offset) if(pos > zm->current_file_size) zm->current_file_size = pos; - if(zm->max_file_size!=0 && pos >= zm->max_file_size) { + if(zm->max_file_size != 0 && pos >= zm->max_file_size) { lprintf(zm, LOG_ERR, "%lu Specified maximum file size (%"PRId64" bytes) reached" ,(ulong)pos, zm->max_file_size); zmodem_send_pos_header(zm, ZFERR, (uint32_t)pos, /* Hex? */ TRUE); @@ -2338,9 +2383,7 @@ const char* zmodem_source(void) char* zmodem_ver(char *buf) { - sscanf("$Revision: 1.124 $", "%*s %s", buf); - - return(buf); + return strcpy(buf, "2.0"); } void zmodem_init(zmodem_t* zm, void* cbdata @@ -2363,6 +2406,7 @@ void zmodem_init(zmodem_t* zm, void* cbdata zm->block_size=ZBLOCKLEN; zm->max_block_size=ZBLOCKLEN; zm->max_errors=9; + zm->can_full_duplex=TRUE; zm->cbdata=cbdata; zm->lputs=lputs; diff --git a/src/sbbs3/zmodem.h b/src/sbbs3/zmodem.h index 6549e12f32c4721e1c31c7f2cbc3cb95eb839465..e4c46773ed1795bdac142c966deb7eae16d58067 100755 --- a/src/sbbs3/zmodem.h +++ b/src/sbbs3/zmodem.h @@ -281,6 +281,7 @@ typedef struct { int32_t crc_request; unsigned errors; unsigned consecutive_errors; + int64_t current_window_size; /* "current transmitter file offset - last reported receiver file offset" */ /* Configuration */ BOOL escape_telnet_iac; @@ -292,6 +293,8 @@ typedef struct { unsigned block_size; unsigned max_block_size; int64_t max_file_size; /* 0 = unlimited */ + int64_t max_window_size; /* 0 = unlimited */ + unsigned target_window_size; /* Target Transmit Window Size, in Seconds, 0 = no-auto-adjustment of window size */ int *log_level; /* Callbacks */ diff --git a/src/smblib/smbdump.c b/src/smblib/smbdump.c index ff1a86cb049108d3190fa27729048454603c3f2f..107bd66615deb190cb8f75dea4e9a064d17239b0 100644 --- a/src/smblib/smbdump.c +++ b/src/smblib/smbdump.c @@ -1,41 +1,25 @@ -/* smbdump.c */ - /* Synchronet message base (SMB) message header dumper */ -/* $Id: smbdump.c,v 1.19 2020/05/25 00:39:47 rswindell Exp $ */ - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * - * See the GNU General Public License for more details: gpl.txt or * - * http://www.fsf.org/copyleft/gpl.html * - * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * + * See the GNU Lesser General Public License for more details: lgpl.txt or * + * http://www.fsf.org/copyleft/lesser.html * * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ -#include <time.h> /* ctime */ +#include "datewrap.h" /* ctime_r */ #include <string.h> /* strcat */ #include "smblib.h" @@ -104,6 +88,12 @@ str_list_t SMBCALL smb_msghdr_str_list(smbmsg_t* msg) ,smb_hfieldtype(msg->hfield[i].type) ,smb_netaddr(&msg->replyto_net)); break; + case FORWARDED: + tt = *(time32_t*)msg->hfield_dat[i]; + strListAppendFormat(&list, HFIELD_NAME_FMT "%.24s" + ,smb_hfieldtype(msg->hfield[i].type) + ,ctime_r(&tt, tmp)); + break; default: strListAppendFormat(&list, HFIELD_NAME_FMT "%s" ,smb_hfieldtype(msg->hfield[i].type) @@ -116,12 +106,12 @@ str_list_t SMBCALL smb_msghdr_str_list(smbmsg_t* msg) tt=msg->hdr.when_written.time; strListAppendFormat(&list, HFIELD_NAME_FMT "%08X %04hX %.24s %s" ,"when_written" ,msg->hdr.when_written.time, msg->hdr.when_written.zone - ,ctime(&tt) + ,ctime_r(&tt, tmp) ,smb_zonestr(msg->hdr.when_written.zone,NULL)); tt=msg->hdr.when_imported.time; strListAppendFormat(&list, HFIELD_NAME_FMT "%08X %04hX %.24s %s" ,"when_imported" ,msg->hdr.when_imported.time, msg->hdr.when_imported.zone - ,ctime(&tt) + ,ctime_r(&tt, tmp) ,smb_zonestr(msg->hdr.when_imported.zone,NULL)); strListAppendFormat(&list, HFIELD_NAME_FMT "%04Xh" ,"type" ,msg->hdr.type); strListAppendFormat(&list, HFIELD_NAME_FMT "%04Xh" ,"version" ,msg->hdr.version); @@ -154,14 +144,14 @@ str_list_t SMBCALL smb_msghdr_str_list(smbmsg_t* msg) strListAppendFormat(&list, HFIELD_NAME_FMT "%"PRIu32,"times_downloaded" ,msg->hdr.times_downloaded); if(msg->hdr.last_downloaded) { tt=msg->hdr.last_downloaded; - strListAppendFormat(&list, HFIELD_NAME_FMT "%.24s" ,"last_downloaded" ,ctime(&tt)); + strListAppendFormat(&list, HFIELD_NAME_FMT "%.24s" ,"last_downloaded" ,ctime_r(&tt, tmp)); } } /* convenience integers */ if(msg->expiration) { tt=msg->expiration; - strListAppendFormat(&list, HFIELD_NAME_FMT "%.24s", "expiration", ctime(&tt)); + strListAppendFormat(&list, HFIELD_NAME_FMT "%.24s", "expiration", ctime_r(&tt, tmp)); } if(msg->cost) strListAppendFormat(&list, HFIELD_NAME_FMT "%u", "cost", msg->cost); diff --git a/src/smblib/smblib.c b/src/smblib/smblib.c index b12d59177b1159ab49d257090ec78e26cd8889a4..9f77bbeb88ad2ad8064f2713c76e0545a978001b 100644 --- a/src/smblib/smblib.c +++ b/src/smblib/smblib.c @@ -1,8 +1,5 @@ /* Synchronet message base (SMB) library routines */ -/* $Id: smblib.c,v 1.209 2020/05/07 19:30:22 rswindell Exp $ */ -// vi: tabstop=4 - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * @@ -16,21 +13,9 @@ * See the GNU Lesser General Public License for more details: lgpl.txt or * * http://www.fsf.org/copyleft/lesser.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -728,32 +713,25 @@ static void set_convenience_ptr(smbmsg_t* msg, uint16_t hfield_type, void* hfiel { switch(hfield_type) { /* convenience variables */ case SENDER: - if(msg->from==NULL || *(msg->from)==0) { - msg->from=(char*)hfield_dat; - break; - } - case FORWARDED: /* fall through */ - msg->forwarded=TRUE; + msg->from=(char*)hfield_dat; + break; + case FORWARDED: + msg->forwarded = TRUE; break; case SENDERAGENT: - if(!msg->forwarded) - msg->from_agent=*(uint16_t *)hfield_dat; + msg->from_agent=*(uint16_t *)hfield_dat; break; case SENDEREXT: - if(!msg->forwarded) - msg->from_ext=(char*)hfield_dat; + msg->from_ext=(char*)hfield_dat; break; case SENDERORG: - if(!msg->forwarded) - msg->from_org=(char*)hfield_dat; + msg->from_org=(char*)hfield_dat; break; case SENDERNETTYPE: - if(!msg->forwarded) - msg->from_net.type=*(uint16_t *)hfield_dat; + msg->from_net.type=*(uint16_t *)hfield_dat; break; case SENDERNETADDR: - if(!msg->forwarded) - msg->from_net.addr=(char*)hfield_dat; + msg->from_net.addr=(char*)hfield_dat; break; case SENDERIPADDR: msg->from_ip=(char*)hfield_dat; @@ -1240,13 +1218,23 @@ int SMBCALL smb_hfield_add_list(smbmsg_t* msg, hfield_t** hfield_list, void** hf } /****************************************************************************/ -/* Convenience function to add an ASCIIZ string header field */ +/* Convenience function to add an ASCIIZ string header field (or blank) */ /****************************************************************************/ int SMBCALL smb_hfield_add_str(smbmsg_t* msg, uint16_t type, const char* str, BOOL insert) { return smb_hfield_add(msg, type, str==NULL ? 0:strlen(str), (void*)str, insert); } +/****************************************************************************/ +/* Convenience function to add an ASCIIZ string header field (NULL ignored) */ +/****************************************************************************/ +int SMBCALL smb_hfield_string(smbmsg_t* msg, uint16_t type, const char* str) +{ + if(str == NULL) + return SMB_ERR_HDR_FIELD; + return smb_hfield_add(msg, type, strlen(str), (void*)str, /* insert */FALSE); +} + /****************************************************************************/ /* Convenience function to a network address header field to msg */ /* Pass NULL for net_type to have the auto-detected net_type hfield added */ diff --git a/src/smblib/smblib.h b/src/smblib/smblib.h index 5c1c55e7a9340843ca0d8c69690c6ec1cd6d6309..fbe37ffbf99c2fc1101c72c210673328e7ba8063 100644 --- a/src/smblib/smblib.h +++ b/src/smblib/smblib.h @@ -160,6 +160,7 @@ SMBEXPORT int SMBCALL smb_hfield_append(smbmsg_t* msg, uint16_t type, size_t le SMBEXPORT int SMBCALL smb_hfield_append_str(smbmsg_t* msg, uint16_t type, const char* data); SMBEXPORT int SMBCALL smb_hfield_add_list(smbmsg_t* msg, hfield_t** hfield_list, void** hfield_dat, BOOL insert); SMBEXPORT int SMBCALL smb_hfield_add_netaddr(smbmsg_t* msg, uint16_t type, const char* str, uint16_t* nettype, BOOL insert); +SMBEXPORT int SMBCALL smb_hfield_string(smbmsg_t*, uint16_t type, const char*); /* Convenience macro: */ #define smb_hfield_bin(msg, type, data) smb_hfield_add(msg, type, sizeof(data), &(data), /* insert: */FALSE) /* Backward compatibility macros: */ diff --git a/src/smblib/smbtxt.c b/src/smblib/smbtxt.c index 60794b91977ab98dd2dc710f7efc52d48e07b47e..0ffce0d1d5320d54dda792feee3625b5782544da 100644 --- a/src/smblib/smbtxt.c +++ b/src/smblib/smbtxt.c @@ -45,6 +45,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) { char* buf; + char* preamble; char* lzhbuf; char* p; char* str; @@ -107,6 +108,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) buf[l] = 0; } } + preamble = strdup(buf); for(i=0;i<(uint)msg->hdr.total_dfields;i++) { if(msg->dfield[i].length<=sizeof(xlat)) @@ -146,6 +148,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) ,"%s malloc failure of %ld bytes for LZH buffer" , __FUNCTION__, length); free(buf); + free(preamble); return(NULL); } if(smb_fread(smb,lzhbuf,length,smb->sdt_fp) != length) { @@ -154,6 +157,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) , __FUNCTION__, length); free(lzhbuf); free(buf); + free(preamble); return(NULL); } lzhlen=*(int32_t*)lzhbuf; @@ -163,6 +167,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) , __FUNCTION__, l+lzhlen+3L); free(lzhbuf); free(buf); + free(preamble); return(NULL); } buf=p; @@ -176,6 +181,7 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) ,"%s realloc failure of %ld bytes for text buffer" , __FUNCTION__, l+length+3L); free(buf); + free(preamble); return(NULL); } buf=p; @@ -196,9 +202,18 @@ char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode) if(mode&GETMSGTXT_PLAIN) { char* plaintext = smb_getplaintext(msg, buf); - if(plaintext != NULL) - return plaintext; + if(plaintext != NULL) { + buf = malloc(strlen(preamble) + strlen(plaintext) + 1); + if(buf == NULL) + buf = plaintext; + else { + strcpy(buf, preamble); + strcat(buf, plaintext); + free(plaintext); + } + } } + free(preamble); return(buf); } @@ -480,7 +495,6 @@ char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf) const char* txt; enum content_transfer_encoding xfer_encoding = CONTENT_TRANFER_ENCODING_NONE; - FREE_AND_NULL(msg->text_subtype); if(msg->mime_version == NULL || msg->content_type == NULL) /* not MIME */ return NULL; txt = mime_getcontent(buf, msg->content_type, "text/plain", 0, &xfer_encoding, &msg->text_charset @@ -490,9 +504,12 @@ char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf) ,/* attachment: */NULL, /* attachment_len: */0, /* index: */0); if(txt == NULL) return NULL; + free(msg->text_subtype); msg->text_subtype = strdup("html"); - } else + } else { + free(msg->text_subtype); msg->text_subtype = strdup("plain"); + } memmove(buf, txt, strlen(txt)+1); if(*buf == 0) /* No decoding necessary */ diff --git a/src/xpdev/sockwrap.c b/src/xpdev/sockwrap.c index fa22af5086ffbd20642e880f3ed92cc40215fcbf..0cf278787c931c400e58506ca4fac83b8ed68827 100644 --- a/src/xpdev/sockwrap.c +++ b/src/xpdev/sockwrap.c @@ -1,37 +1,21 @@ -/* sockwrap.c */ - /* Berkley/WinSock socket API wrappers */ -/* $Id: sockwrap.c,v 1.74 2020/08/09 02:13:57 rswindell Exp $ */ - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public License * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * - * See the GNU Lesser General Public License for more details: lgpl.txt or * - * http://www.fsf.org/copyleft/lesser.html * - * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * + * See the GNU General Public License for more details: gpl.txt or * + * http://www.fsf.org/copyleft/gpl.html * * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -524,14 +508,22 @@ DLLEXPORT char* socket_strerror(int error_number, char* buf, size_t buflen) #endif } -#if defined(_WIN32) && !defined(_MSC_VER) -DLLEXPORT int inet_pton(int af, const char *src, void *dst) +DLLEXPORT void set_socket_errno(int err) +{ +#if defined(_WINSOCKAPI_) + WSASetLastError(err); +#else + errno = err; +#endif +} + +DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst) { struct addrinfo hints = {0}; struct addrinfo *res, *cur; if (af != AF_INET && af != AF_INET6) { - // TODO: Should set socket_errno to EAFNOSUPPORT + set_socket_errno(EAFNOSUPPORT); return -1; } @@ -558,4 +550,3 @@ DLLEXPORT int inet_pton(int af, const char *src, void *dst) freeaddrinfo(res); return 1; } -#endif diff --git a/src/xpdev/sockwrap.h b/src/xpdev/sockwrap.h index 094701eae2fb77243ad9abe4652b7048cd7b31b3..73e7c90cced306b8d1b67ae9e6b9fc1b99d8bf75 100644 --- a/src/xpdev/sockwrap.h +++ b/src/xpdev/sockwrap.h @@ -1,37 +1,21 @@ -/* sockwrap.h */ - /* Berkley/WinSock socket API wrappers */ -/* $Id: sockwrap.h,v 1.61 2020/08/08 23:26:38 rswindell Exp $ */ - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * * Copyright Rob Swindell - http://www.synchro.net/copyright.html * * * - * This library is free software; you can redistribute it and/or * - * modify it under the terms of the GNU Lesser General Public License * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * * as published by the Free Software Foundation; either version 2 * * of the License, or (at your option) any later version. * - * See the GNU Lesser General Public License for more details: lgpl.txt or * - * http://www.fsf.org/copyleft/lesser.html * - * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * + * See the GNU General Public License for more details: gpl.txt or * + * http://www.fsf.org/copyleft/gpl.html * * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -240,9 +224,10 @@ DLLEXPORT uint16_t inet_addrport(union xp_sockaddr *addr); DLLEXPORT void inet_setaddrport(union xp_sockaddr *addr, uint16_t port); DLLEXPORT BOOL inet_addrmatch(union xp_sockaddr* addr1, union xp_sockaddr* addr2); DLLEXPORT char* socket_strerror(int, char*, size_t); - -#if defined(_WIN32) && !defined(_MSC_VER) -DLLEXPORT int inet_pton(int af, const char *src, void *dst); +DLLEXPORT void set_socket_errno(int); +DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst); +#if defined(_WIN32) // mingw and WinXP's WS2_32.DLL don't have inet_pton(): + #define inet_pton xp_inet_pton #endif #ifdef __cplusplus diff --git a/src/xpdev/xpdev.props b/src/xpdev/xpdev.props index 5966335cc74953e1bb909fbad211dab435e30dda..fc6a51f6e9261a9dc11cb7ef06858172c925cba3 100644 --- a/src/xpdev/xpdev.props +++ b/src/xpdev/xpdev.props @@ -11,7 +11,7 @@ <AdditionalIncludeDirectories>$(MSBuildThisFileDirectory);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> </ClCompile> <Link> - <AdditionalDependencies>netapi32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <AdditionalDependencies>netapi32.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> </Link> </ItemDefinitionGroup> <ItemGroup /> diff --git a/webv4/lib/events/mail.js b/webv4/lib/events/mail.js index 986b81875f53cb41f7035cd4ecaef5a9f0c21c33..4aca79415a478d83f9ad141f1cb6fbc471fcedfb 100644 --- a/webv4/lib/events/mail.js +++ b/webv4/lib/events/mail.js @@ -6,7 +6,7 @@ function cycle() { if (user.number < 1 || user.alias == settings.guest) return; if (time() - last_run <= frequency) return; last_run = time(); - const count = user.stats.mail_waiting; + const count = user.stats.unread_mail_waiting; if (count > 0 || (count == 0 && last_count > 0)) { emit({ event: 'mail', data: JSON.stringify({ count: count })}); } diff --git a/webv4/lib/locale/en_us.ini b/webv4/lib/locale/en_us.ini index c7391e85be75cd9fd9ea9fd21aabd2a8140ff285..655ad46eeb74c0d3a0420ad78ba2c0d42028886d 100644 --- a/webv4/lib/locale/en_us.ini +++ b/webv4/lib/locale/en_us.ini @@ -24,6 +24,7 @@ label_message_date = on label_message_subject = Subject label_tab_inbox = Inbox label_tab_sent = Sent +label_new_message = New [page_register] title = Register diff --git a/webv4/pages/000-mail.xjs b/webv4/pages/000-mail.xjs index a4c71fec2a331b5b66b1ff92a44978278b0ed524..c7e662e58c3e922af0d08ff5b53f5662ec69ff94 100644 --- a/webv4/pages/000-mail.xjs +++ b/webv4/pages/000-mail.xjs @@ -18,6 +18,7 @@ <div class="checkbox"> <label class="checkbox-inline"> <input id="check-<? write(header.number); ?>" type="checkbox" class="mail-select"> + <? write(header.attr&MSG_READ ? '' : '<span class="badge new">' + locale.strings.page_mail.label_new_message + '</span>') ?> </label> </div> </div> diff --git a/webv4/pages/099-xtrnmenu-games.xjs b/webv4/pages/099-xtrnmenu-games.xjs new file mode 100644 index 0000000000000000000000000000000000000000..c7106969f157d3872695bbe5d7e9b140abcde545 --- /dev/null +++ b/webv4/pages/099-xtrnmenu-games.xjs @@ -0,0 +1,149 @@ +<!--HIDDEN:Games--> +<?xjs +/** + * Web Display for Custom External Program Menus + * by Michael Long mlong innerrealmbbs.us + * + * See wiki at http://wiki.synchro.net/module:xtrnmenumod + */ + + load("ftelnethelper.js"); + load('sbbsdefs.js'); + load("xtrnmenulib.js"); + load(settings.web_lib + 'ftelnet.js'); + load(settings.web_lib + 'request.js'); + + var ExternalMenus = new ExternalMenus(); + + if (typeof settings.xtrn_blacklist === 'string') { + settings.xtrn_blacklist = settings.xtrn_blacklist.toLowerCase().split(','); + } else { + settings.xtrn_blacklist = []; + } + + var menuitems = []; +?> + +<style>.fTelnetStatusBar { display : none; }</style> + +<a name="fTelnet"></a> +<div id="fTelnetContainer" class="fTelnetContainer" style="margin-bottom:1em;"></div> + +<div id="xtrn-list" class="list-group"> + +<?xjs + var menuobj; + if ((Request.get_param('type') == 'xtrnmenu') && Request.has_param('target')) { + menuobj = ExternalMenus.getSectionMenu(Request.get_param('target')); + } else { + if (Request.has_param('target')) { + menuobj = ExternalMenus.getMenu(Request.get_param('target')); + } else { + menuobj = ExternalMenus.getMenu('main'); + } + } + + if ((typeof menuobj === "undefined") || !menuobj + || (typeof menuobj.items === "undefined") || (menuobj.items.length < 1)) { + writeln("<h4>" + ExternalMenus.options.no_programs_msg + "</h4>"); + } else if (user.security.restrictions&UFLAG_X) { + writeln("<h4>" + options.restricted_user_msg + "</h4>"); + } else { + // ok to display menu + + writeln("<h4>" + menuobj.title + "</h4>"); + var menuitemsfiltered = ExternalMenus.getSortedItems(menuobj); + menuitemsfiltered.forEach(function (menuitem) { + if (settings.xtrn_blacklist.indexOf(menuitem.target.toLowerCase()) > -1) { + return; + } + menuitems.push({ + 'itemtitle': menuitem.title, + 'itemtype': menuitem.type, + 'itemtarget': menuitem.target + }); + }); + } +?> +</div> + +<script id="fTelnetScript" src="<?xjs write(get_url()); ?>"></script> +<script type="text/javascript"> + var wsp = <?xjs write(settings.wsp || GetWebSocketServicePort()); ?>; + var wssp = <?xjs write(settings.wssp || GetWebSocketServicePort(true)); ?>; + var Options = new fTelnetOptions(); + Options.BareLFtoCRLF = false; + Options.BitsPerSecond = 57600; + Options.ConnectionType = 'rlogin'; + Options.Emulation = 'ansi-bbs'; + Options.Enter = '\r'; + Options.Font = 'CP437'; + Options.ForceWss = false; + Options.Hostname = '<?xjs write(http_request.vhost); ?>'; + Options.LocalEcho = false; + Options.Port = location.protocol == 'https:' ? wssp : wsp; + Options.RLoginClientUsername = '<?xjs write(user.security.password); ?>'; + Options.RLoginServerUsername = '<?xjs write(user.alias); ?>'; + Options.ScreenColumns = 80; + Options.ScreenRows = 25; + Options.SplashScreen = Options.SplashScreen = '<?xjs write(get_splash()); ?>'; + Options.WebSocketUrlPath = '?Port=<?xjs write(GetRLoginPort()); ?>'; + var fTelnet = new fTelnetClient('fTelnetContainer', Options); + fTelnet.OnConnectionClose = function () { + window.location.reload(); + }; + + async function launchXtrn() { + var code = event.srcElement.id; + await v4_get('./api/system.ssjs?call=set-xtrn-intent&code=' + code); + fTelnet._Options.RLoginTerminalType = 'xtrn=' + code; + fTelnet.Connect(); + } + + var menuitems = <?xjs write(JSON.stringify(menuitems)); ?>; + var currentTarget = "<?xjs Request.write_param('target'); ?>"; + var currentType = "<?xjs Request.write_param('type'); ?>"; + var currentTitle = `<?xjs write(menuobj.title) ?>`; + if (currentTitle && !currentTarget) { + // main menu - store title for breadcrumb + sessionStorage.setItem("mainmenu", currentTitle); + } + var div = $('#xtrn-list'); + menuitems.forEach(function (menuitem) { + var a = document.createElement('a'); + $(a).addClass("list-group-item"); + $(a).addClass("striped"); + a.text = menuitem.itemtitle; + + if (menuitem.itemtype == "xtrnprog") { + a.href = "#fTelnet"; + a.id = menuitem.itemtarget; + a.onclick = function () { launchXtrn(); }; + } else { + a.href = "/?page=<?xjs Request.write_param('page') ?>&type=" +menuitem.itemtype + + "&target=" + menuitem.itemtarget; + a.onclick = function () { + sessionStorage.setItem('prev:' + menuitem.itemtarget.toLowerCase(), '<?xjs Request.write_param('target')?>'); + sessionStorage.setItem('prevtype:' + menuitem.itemtarget.toLowerCase(), '<?xjs Request.write_param('type')?>'); + sessionStorage.setItem('prevtitle:' + menuitem.itemtarget.toLowerCase(), currentTitle); + }; + } + $(div).append(a); + }); + + // breadcrumb + var prevTarget = sessionStorage.getItem('prev:' + currentTarget.toLowerCase()); + var prevType = sessionStorage.getItem('prevtype:' + currentTarget.toLowerCase()); + var prevTitle = sessionStorage.getItem('prevtitle:' + currentTarget.toLowerCase()); + if (prevType && prevTarget && prevTitle) { + $('#xtrn-list').prepend('<ol class="breadcrumb"><a href="/?page=<?xjs Request.write_param('page') ?>&type=' + prevType + + '&target=' + prevTarget + '">' + prevTitle + '</a></ol>'); + } else if (currentTarget) { + // level 2, not main menu + var mainmenuTitle = sessionStorage.getItem('mainmenu'); + if (!mainmenuTitle) { + mainmenuTitle = 'Games'; + } + $('#xtrn-list').prepend('<ol class="breadcrumb"><a href="/?page=<?xjs Request.write_param('page') ?>">' + mainmenuTitle + '</a></ol>'); + } +</script> diff --git a/webv4/root/css/style.css b/webv4/root/css/style.css index 7751bb1a8ed1e4a3274afa526dca9a58a355e007..7e49bcf0b1719cc5ecaf0b244f814668234257bf 100644 --- a/webv4/root/css/style.css +++ b/webv4/root/css/style.css @@ -17,6 +17,11 @@ a.unread { background: #FFFFFF; } +span.badge.new { + background: #2E9AFE; + color: #FFFFFF; +} + /* A read mail message in the list view. */ a.read { background: #E6E6E6; @@ -174,4 +179,4 @@ animation: indicator-fade 3s ease 0s 1 alternate !important; .breadcrumb li { display: inline; -} \ No newline at end of file +} diff --git a/xtrn/3rdp-install/bre.ini b/xtrn/3rdp-install/bre.ini new file mode 100644 index 0000000000000000000000000000000000000000..1cdfa1b83ea9cd77ecd4665c8a0e072c2d1637b8 --- /dev/null +++ b/xtrn/3rdp-install/bre.ini @@ -0,0 +1,139 @@ +; Install instructions for Barren Realms Elite +; see http://wiki.synchro.net/howto:door:bre +Name: Barren Realms Elite +Desc: Multi-player strategic war game set in a post-apocalyptic future +By: Mehul Patel,John Dailey +Cats: Games +Subs: Multiplayer, Strategy, War +exe: BRE.EXE + +[md5:127779656ab7564275c61bc3f3fad88b] +ver = 0.988 +url = https://www.johndaileysoftware.com/download/?id=220BRE&fileName=brev988.exe + +[md5:6811a9565d1be70b7a7683cd791b4563] +ver = 0.987 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0987.arj +[md5:eb602a6238b686ed40ec8c6fc71b021a] +ver = 0.986 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0986.arj +[md5:2d8dbb97bc9a42536782127e530848aa] +ver = 0.985 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0985.arj +[md5:05273fda02fc52db2b2cc4cebd92e793] +ver = 0.984 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0984.arj +[md5: 6bdf120f19a41f48f2f0b532eda3ffaa] +ver = 0.983 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0983.arj +[md5:eb4af37fde3c8cf6baceacbb06a7e2fc] +ver = 0.982 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0982.arj +[md5:509bccf44ef158ea0c63b1e26e3ea3a9] +ver = 0.981 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0981.arj +[md5:8fadf439619d0ce52fd44426a1cdc0ef] +ver = 0.980 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0980.arj +[md5:660445e9c47eebcb98f5fdd6422cd3cd] +ver = 0.979 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0979.arj +[md5:4b6c85c8d88eecc4069142477cf5b57a] +ver = 0.978 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0978.arj +[md5:ba565786155d797b58b696f277f163b2] +ver = 0.977 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0977.arj +[md5:45ea1e2b739c216d250bad77b0d01797] +ver = 0.976 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0976.arj +[md5:e107667e1827c092a6bd0468f1134179] +ver = 0.975 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0975.arj +[md5:58167e18933109a2043d309b9e6ed47a] +ver = 0.973 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0973.arj +[md5:fb643b05fb10b955f414b53aff8bc4f6] +ver = 0.972 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0972.arj +[md5:3f4ad076d8fbc7cc7b670b99adbbd636] +ver = 0.971 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0971.arj +[md5:d867abd4f200bec4b8fdf66b3a143b5f] +ver = 0.970 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0970.arj +[md5:d8b4a33af532a17f59b65a826a3e2e72] +ver = 0.964 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0964.arj +[md5:cc3c935134a3bb27da156e96d8d5fb1c] +ver = 0.963 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0963.arj +[md5:a7dd8aef3b7d813fb284ce72aeb96672] +ver = 0.962 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0962.arj +[md5:4ac7ea354e51ff69c29848736e167afe] +ver = 0.961 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0961.arj +[md5:9e1845ec08bb2d0f56cb46ffc8a1bacd] +ver = 0.960 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0960.zip +[md5:4f3669cb3c378f47be3528ce6aed1d2d] +ver = 0.956 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0956.zip +[md5:b8fa5fb3d6fd22fdecb647bf396db6a5] +ver = 0.953 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0953.arj +[md5:f36d54acae29edc86d227e0450b4252f] +ver = 0.910 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0910.arj +[md5:c8c810778f03306d978dbed45684f540] +ver = 0.904 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0904.arj +[md5:7e556348d9ddcefdefaea6e58def25e3] +ver = 0.901 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre0901.zip +[md5:66e2b24623aac433a6a87fe9677e68a5] +ver = 0.875 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre00875.zip +[md5:7249263a11b956e269a3978f82df25e1] +ver = 0.872 +url=http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre00872.arj +[md5:e70c862cbcb8a984e2313c431e6a4028] +ver = 0.850 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre00850.zip +[md5:68fa2736115cca9e35d13306c1229d4f] +ver = 0.601 +url = http://breakintochat.com/files/doors/SRGames/BRE/distributions/bre00601.arj + +[pre-eval:file_exists(startup_dir + 'RESOURCE.DAT')] +prompt = false +required = true +fail = You must create RESOURCE.DAT by running BRE's INSTALL.EXE before you can continue installation. + +[prog:BRE] +; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +name = Barren Realms Elite +; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +cmd = bre +ars = DOS +execution_ars = NOT GUEST +settings = XTRN_ANSI | XTRN_STARTUPDIR | XTRN_SH +type = XTRN_SR +clean_cmd = del inuse.flg + +[prog:BRERESET] +name = Barren Realms Elite Reset +cmd = bre RESET +ars = DOS AND SYSOP +execution_ars = SYSOP +settings = XTRN_ANSI | XTRN_IO_INTS | XTRN_SH +type = XTRN_NONE +clean_cmd = del inuse.flg + +[event:BRESCORE] +prompt = true +cmd = bre score +name = Barren Realms Elite Bulletins +; all days +days = 127 +freq = 4 diff --git a/xtrn/tw2/tw2.js b/xtrn/tw2/tw2.js index bd0aa82409c7231480c43c07496b25e3b5d5b2c4..7f6cde73a24bd4b11cd159362e052fcfade60d05 100644 --- a/xtrn/tw2/tw2.js +++ b/xtrn/tw2/tw2.js @@ -41,7 +41,7 @@ var LOCK_READ=1; Settings=new GameSettings(); if(db==undefined) { - alert("ERROR: Configuation invalid"); + alert("ERROR: Configuration invalid"); exit(1); }