diff --git a/load/defs.js b/load/defs.js index e48ce6b..fef34fb 100644 --- a/load/defs.js +++ b/load/defs.js @@ -1,3 +1,8 @@ +require('smbdefs.js','MSG_DELETE'); +require('sbbsdefs.js','SUB_FIDO'); +load('string.js'); +load('lz-string.js'); + /** * ANSItex definitions */ @@ -11,6 +16,9 @@ const FRAMES_MSG_BASE = 'vtx_data'; /* Load frames from files */ const FRAMES_MSG_FILES = true; +const PAGE_LENGTH = 4; // The size of our page tag as stored in the msgbase for echomail/netmail +const NUMERIC_REGEX = /^[0-9]*$/; + /* Unit of cost */ const FRAME_COSTUNIT = 'c'; @@ -51,6 +59,7 @@ const MODE_RFSENT = 5; const MODE_RFERROR = 6; /** FRAME TYPES **/ +// @todo Change these to bits /* Information Frame, requires no response after viewed */ const FRAME_TYPE_INFO = 'i'; @@ -120,14 +129,16 @@ const FRAME_SYSTEM_ERROR = {frame: '998',index: 'a'}; const FRAME_SAVE_ATTRS = [ 'content', // raw source content of a frame (as stored in vtx/tex files) 'cost', // integer, frame cost - 'dynamic_fields', // array of fields + 'date', // date, date frame created + 'dynamic_fields', // array of fields // @todo deprecate and work these out when loading and parsing a frame 'frame', // Page ID, 'index', // Page index, - 'input_fields', // array of fields - 'isAccessible', // boolean - 'isPublic', // boolean - 'key', // array, representing our key actions - 'type', // frame type + 'input_fields', // array of fields // @todo deprecate and work these out when loading and parsing a frame + 'isAccessible', // boolean // @todo deprecate and merge into 'state' + 'isPublic', // boolean // @todo deprecate and merge into 'state' + 'key', // array, representing our key actions (0-9) + 'state', // integer, representing type, accessible and public status + 'type', // frame type // @todo deprecate and merge into 'state' 'version', // frame version (1) 'window' // processed frame data ]; @@ -138,7 +149,7 @@ const MAIL_TEMPLATE_FRAME = {frame: '199',index: 'a'}; /* The page that has our echomail area summary template */ const MAIL_TEMPLATE_AREA_SUMMARY = {frame: '198',index: 'a'}; -// The maximum size of embedded dynamic fields in frames +// The maximum size (length) of embedded dynamic fields in frames const DYNAMIC_FIELD_SIZE_MAX = 50; /** ESCAPE CODES **/ diff --git a/load/msgbases.js b/load/msgbases.js index 969568b..f8fa905 100644 --- a/load/msgbases.js +++ b/load/msgbases.js @@ -1,98 +1,181 @@ -const PAGE_LENGTH = 4; // The size of our page tag as stored in the msgbase for echomail/netmail -const PAGE_LAST_KEY = 'last_page'; // Last page which has the latest message -const MAX_PAGE_NUM = 9999; // Maximum page number. @todo Can this be changed to '9'.repeat(PAGE_LENGTH)? +'use strict'; +require('ansitex/load/defs.js','ANSITEX_HOME'); -// Our message bases +// Our message bases that are enabled to render Videotex Messages +/* + * If a message area doesnt have: + * + a zone_id, then the group isnt Videotex enabled + * + a area_id, then the area isnt Videotex enabled + */ function MsgAreas() { - 'use strict'; - - this.areas = []; - this.areas_excluded = []; + this.areas = []; // Message areas var zone_id; var zone_name; var ma; - for(var g in msg_area.grp_list) { - if (msg_area.grp_list[g].name.indexOf(':') !== -1) { - zone_id = msg_area.grp_list[g].name.split(':')[0]; - zone_name = msg_area.grp_list[g].name.split(':')[1]; + // Load the message areas + for (var g in msg_area.grp_list) { + for (var a in msg_area.grp_list[g].sub_list) { + //writeln('group:'+g+' name:'+msg_area.grp_list[g].name+' FIDO:'+JSON.stringify(msg_area.grp_list[g].sub_list[a].fidonet_addr)+' INDEX:'+JSON.stringify(msg_area.grp_list[g].sub_list[a].ptridx)); + + ma = new MsgArea(); + ma.group_id = g; + ma.sub_id = a; + + if (msg_area.grp_list[g].name.indexOf(':') === -1) { + // If the sub is enabled for FTN, and the zone < 9999, then we'll us that if zone_id is undefined + if ((msg_area.grp_list[g].sub_list[a].settings & SUB_FIDO) && msg_area.grp_list[g].sub_list[a].fidonet_addr) { + var x = msg_area.grp_list[g].sub_list[a].fidonet_addr.split(/([0-9]+):/)[1]; + if ((x > 0) && (x < 9999)) { + zone_id = x.padStart(4,'0'); + zone_name = msg_area.grp_list[g].name; + } + } + + } else { + zone_id = msg_area.grp_list[g].name.split(':')[0]; + zone_name = msg_area.grp_list[g].name.split(':')[1]; + } + + if (zone_id) { + ma.zone_id = zone_id; + ma.zone_name = zone_name; - for (var a in msg_area.grp_list[g].sub_list) { if (msg_area.grp_list[g].sub_list[a].name.indexOf(':') !== -1) { - ma = new MsgArea(); - ma.zone_id = zone_id; - ma.zone_name = zone_name; - ma.area_id = msg_area.grp_list[g].sub_list[a].name.split(':')[0]; - ma.area_name = msg_area.grp_list[g].sub_list[a].name.split(':')[1]; - ma.code = msg_area.grp_list[g].sub_list[a].code; + var sublist = msg_area.grp_list[g].sub_list[a].name.split(':'); + ma.area_id = sublist[0]; + ma.area_name = sublist[1]; - this.areas.push(ma); - } else { - this.areas_excluded.push(zone_name+':'+msg_area.grp_list[g].sub_list[a].name); + /* + writeln(' code:'+ma.code); + writeln(' fullname:'+ma.full_name); + writeln(' pageprefix:'+ma.page_prefix) + writeln(); + */ } } - } else { - zone_name = msg_area.grp_list[g].name; - - for (var a in msg_area.grp_list[g].sub_list) { - this.areas_excluded.push(zone_name+':'+msg_area.grp_list[g].sub_list[a].name); - } + this.areas.push(ma); } } + /* List areas that we do and dont manage */ Object.defineProperty(this,'list',{ get: function() { - writeln('Areas that we are NOT managing mail:'+this.areas_excluded.length); - writeln('Areas that we ARE managing mail:'+this.areas.length); - - for(var x in this.areas_excluded) { - writeln(x+':'+((this.areas_excluded[x].area_name === undefined) - ? this.areas_excluded[x] - : JSON.stringify(this.areas_excluded[x]))); + var areas = this.managed; + writeln('Areas that we ARE managing mail:'+areas.length); + for (var x in areas.sort(function(a,b) { return a.page_prefix > b.page_prefix; })) { + writeln(x+':'+areas[x].code+' prefix:'+areas[x].page_prefix); } + + areas = this.unmanaged; + writeln('Areas that we are NOT managing mail:'+areas.length); + for (var x in areas) + writeln(x+':'+areas[x].code); } }); + + /* List of message areas managed */ + Object.defineProperty(this,'managed',{ + get: function() { + return this.areas.filter(function(item) { + return item.managed; + }); + } + }); + + /* List of message areas unmanaged */ + Object.defineProperty(this,'unmanaged',{ + get: function() { + return this.areas.filter(function(item) { + return ! item.managed; + }) + } + }); + + /* Fetch a specific message area */ + MsgAreas.prototype.getArea = function(area) { + if (area === undefined) + return undefined; + + return this.areas.filter(function(x) { + // If the area is a 6 digits, then its a page prefix, otherwise its an area code name + if ((area.length === 6) && (NUMERIC_REGEX.test(area))) + return x.page_prefix === area; + + else + return x.code === area; + }).pop(); + } + + // @todo review + MsgAreas.prototype.getMessage = function(page) { + var area = this.getArea(page); + log(LOG_DEBUG,' - msg:'+JSON.stringify(page.substr(7,4))); + + return area ? area.getMessage(page.substr(7,4)) : undefined; + } + + // @todo review + MsgAreas.prototype.getUserStats = function(page) { + var area = this.getArea(page); + + return area ? msg_area.grp_list[area.msgbase.cfg.grp_number].sub_list[msg_area.sub[area.msgbase.cfg.code].index] : undefined; + } } function MsgArea() { - this.zone_id = undefined; - this.zone_name = undefined; - this.area_id = undefined; - this.area_name = undefined; - this.msgbase = undefined; - this.headers = undefined; - this.tagged_list = undefined; - this.untagged_list = undefined; - this.grp_number = undefined; - this.subnum = undefined; + this.zone_id = undefined; // Zone this message area belongs to, eg: 0516 + this.zone_name = undefined; // Zone Name, eg: VIDEOTEX + this.area_id = undefined; // Sub Area ID for this message area, eg: 01 + this.area_name = undefined; // Sub Area Name for this message area, eg: CHAT + this.group_id = undefined; // SBBS Message Group ID + this.sub_id = undefined; // SBBS Message Sub ID - /** - * Build a MsgArea once we are given the code - */ + // MSG Base Code Object.defineProperty(this,'code',{ - set: function(code) { - this.msgbase = new MsgBase(code); + get: function() { + //writeln('group_id:'+this.group_id+' sub_id:'+this.sub_id); + return msg_area.grp_list[this.group_id].sub_list[this.sub_id].code; + } + }); + + // Get all frames that are tagged + Object.defineProperty(this,'frames',{ + get: function() { + if (! this.managed) + return undefined; + + var msgbase = new MsgAreas().getArea(FRAMES_MSG_BASE).msgbase; + var frames = []; + var regex = this.page_prefix_regex; try { - if (this.msgbase.open()) { - headers = this.msgbase.get_all_msg_headers(false,false) || []; + if (msgbase.open()) { + var headers = msgbase.get_all_msg_headers(false,false) || []; - // Just take the last MAX_MESSAGES - this.headers = Object.keys(headers).slice(-(MAX_PAGE_NUM+1)).map(function(key) { return headers[key]; }); - headers = undefined; + for (var i in headers) { + if ((! (headers[i].attr&MSG_DELETE)) && headers[i].tags && regex.test(headers[i].tags)) + frames.push(headers[i]); + } - this.msgbase.close(); + msgbase.close(); } else { - log(LOG_ERROR,code+' cannot be opened:'+this.msgbase.error); - this.headers = []; + writeln('*** NOPE WONT OPEN msgbase:'+JSON.stringify(msgbase)); } } catch (e) { - log(LOG_ERROR,code+' cannot be opened:'+e.message); - this.headers = []; + log(LOG_ERROR,this.code+' cannot be opened (frames):'+e.message); + + return undefined; } + + return frames.sort(function(a,b) { + return (a.when_imported_time !== b.when_imported_time) + ? a.when_imported_time > b.when_imported_time + : a.number > b.number; + }); } }); @@ -103,6 +186,7 @@ function MsgArea() { } }); + // @deprecated use frames instead // Total tagged messages Object.defineProperty(this,'list_tagged',{ get: function() { @@ -124,42 +208,101 @@ function MsgArea() { } }); - // List untagged messages - Object.defineProperty(this,'list_untagged',{ + Object.defineProperty(this,'last_tagged',{ get: function() { - if (this.untagged_list === undefined) { - this.untagged_list = []; + var last_tag = this.frames.sort(function(a,b) { + if (a.when_imported_time === b.when_imported_time) + return a.number > b.number + else + return (a.when_imported_time > b.when_imported_time); + }).pop(); - if (! this.headers) - return this.untagged_list; + var msgbase = new MsgAreas().getArea(FRAMES_MSG_BASE).msgbase; - for(var x in this.headers) { - if ((! this.headers[x].tags) || (this.headers[x].tags.length !== PAGE_LENGTH)) { - this.untagged_list.push(this.headers[x]); - write(); // @todo This is needed for this to work? - } + try { + if (msgbase.open()) { + var body = JSON.parse(LZString.decompressFromBase64(msgbase.get_msg_body(last_tag.number))); + + msgbase.close(); + + } else { + writeln('*** NOPE WONT OPEN msgbase:'+JSON.stringify(msgbase)); } + + } catch (e) { + log(LOG_ERROR,this.code+' cannot be opened (last_tagged):'+e.message); + + return undefined; } - return this.untagged_list; + msgbase = this.msgbase; + + try { + if (msgbase.open()) { + var index = msgbase.get_msg_header(body.id); + + msgbase.close(); + + } else { + writeln('*** NOPE WONT OPEN msgbase:'+JSON.stringify(msgbase)); + } + + } catch (e) { + log(LOG_ERROR,this.code+' cannot be opened (last_tagged):'+e.message); + + return undefined; + } + + return index; + } + }) + + // Is this area defined for videotex messages + Object.defineProperty(this,'managed',{ + get: function() { + return (this.zone_id !== undefined) && (this.area_id !== undefined); + } + }); + + Object.defineProperty(this,'max_page',{ + get: function() { + return parseInt('9'.repeat(PAGE_LENGTH),10); + } + }); + + Object.defineProperty(this,'msgbase',{ + get: function() { + return new MsgBase(this.code); } }); // Get Next page number Object.defineProperty(this,'page_next',{ get: function() { - var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini')); - if (! f.open('r')) { - writeln('Unable to open ini file'); - exit(2); + if (! this.managed) + return undefined; + + var next_tag = this.frames.pop(); + + if (next_tag !== undefined) { + next_tag = next_tag.tags; + + if (next_tag.indexOf('1'+this.page_prefix) === 0) + next_tag = next_tag.slice(this.page_prefix.length+1); + + next_tag = parseInt(next_tag,10)+1; + + if (next_tag > this.max_page) + next_tag = 0; + + } else { + next_tag = 0; } - var page = f.iniGetValue('prefix:'+this.page_prefix,PAGE_LAST_KEY) - f.close(); - - return page ? page : '0'.repeat(PAGE_LENGTH); + return (''+next_tag).padStart(4,'0'); }, + /* set: function(page) { var f = new File(file_cfgname(system.mods_dir,'ansitex/ctrl/videotex.ini')); if (! f.open('r+')) { @@ -170,6 +313,7 @@ function MsgArea() { f.iniSetValue('prefix:'+this.page_prefix,PAGE_LAST_KEY,(''+page).padStart(4,'0')); f.close(); } + */ }); // Our page prefix for this msg area @@ -178,251 +322,335 @@ function MsgArea() { return ''+this.zone_id+this.area_id; }, }); -} -/** - * Unread messages [1..] - * Array key 0 returns the last read message - * - * @returns {*[]} - */ -MsgArea.prototype.newMsgs = function() { - var msgs = []; - var stats = this.getUserStats(); - //log(LOG_DEBUG,'Users last_read pointer: '+JSON.stringify(stats.last_read)); + // Return a REG to test if this frame is part of this msgbase + Object.defineProperty(this,'page_prefix_regex',{ + get: function() { + return new RegExp( '^1'+this.page_prefix); + }, + }); - for(var x in this.list_tagged) { - // Advance past our last scan_ptr - if (this.list_tagged[x].number <= stats.last_read) - continue; + // Total Messages in a msgbase + Object.defineProperty(this,'total_msgs',{ + get: function() { + if (! this.managed) + return undefined; - msgs.push(this.list_tagged[x]); + var msgbase = this.msgbase; - write(); // @todo This is needed for this to work? - } + try { + if (msgbase.open()) { + var index = msgbase.get_index() || []; - return msgs; -} + msgbase.close(); -/** - * New Messages for the logged in user - */ -MsgArea.prototype.newMsgsToMe = function() { - var msgs = []; - var stats = this.getUserStats(); - var last = null; - //log(LOG_DEBUG,'Users scan_ptr pointer: '+JSON.stringify(stats.scan_ptr)); + } else { + writeln('*** NOPE WONT OPEN msgbase:'+JSON.stringify(msgbase)); + } - for(var x in this.list_tagged) { - // Advance past our last scan_ptr - if (this.list_tagged[x].number <= stats.scan_ptr) { - if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias)) - last = x; + } catch (e) { + log(LOG_ERROR,this.code+' cannot be opened (total_msgs):'+e.message); - continue; + return undefined; + } + + return index.length; } + }); - // Add our previous to me message - if (msgs.length === 0) - msgs.push(last !== null ? this.list_tagged[last] : []); + // List untagged messages + Object.defineProperty(this,'untagged', { + get: function() { + if (! this.managed) + return undefined; + + var msgbase = this.msgbase; + var last_tag = this.last_tagged; + var msgs = []; + + try { + if (msgbase.open()) { + var headers = msgbase.get_all_msg_headers(false,false); + + for (var x in headers) { + if (last_tag + && ((headers[x].when_imported_time < last_tag.when_imported_time) + || ((headers[x].when_imported_time === last_tag.when_imported_time) && (headers[x].number <= last_tag.number)))) + continue; + + msgs.push(headers[x]); + } + + msgbase.close(); + + } else { + writeln('*** NOPE WONT OPEN msgbase:'+JSON.stringify(msgbase)); + } + + } catch (e) { + log(LOG_ERROR,this.code+' cannot be opened (untagged):'+e.message); + + return undefined; + } + + return msgs.sort(function(a,b) { + return (a.when_imported_time !== b.when_imported_time) + ? a.when_imported_time > b.when_imported_time + : a.number > b.number; + }); + } + }); + + // @todo review + /** + * Unread messages [1..] + * Array key 0 returns the last read message + * + * @returns {*[]} + */ + MsgArea.prototype.newMsgs = function() { + var msgs = []; + var stats = this.getUserStats(); + //log(LOG_DEBUG,'Users last_read pointer: '+JSON.stringify(stats.last_read)); + + for(var x in this.list_tagged) { + // Advance past our last scan_ptr + if (this.list_tagged[x].number <= stats.last_read) + continue; - if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias)) msgs.push(this.list_tagged[x]); - write(); // @todo This is needed for this to work? - } - - return msgs; -} - -/** - * Get a specific message with a tag - */ -MsgArea.prototype.getMessage = function(page) { - var msg = undefined; - - for(var x in this.list_tagged) { - if (this.list_tagged[x].tags === page) { - msg = this.list_tagged[x]; - break; - } - write(); // @todo This is needed for this to work? - } - - if (! msg) - return undefined; - - if (! this.msgbase.open()) { - writeln(code+' cannot be opened:'+this.msgbase.error); - return undefined; - } - - msg.grp_number = this.msgbase.cfg.grp_number; - var cfg = this.msgbase.cfg; - msg.subnum = msg_area.grp_list[cfg.grp_number].sub_list.filter(function(x) { return x.number === cfg.number; }).pop().index; - msg.content = this.msgbase.get_msg_body(false,msg.number,false,false,true,true); - this.msgbase.close(); - - return msg; -} - -/** - * Get a message page by pointer - * - * @param number - * @returns {string} - */ -MsgArea.prototype.getMessagePage = function(number) { - log(LOG_DEBUG,'Get Message Page with number ['+number+']'); - - var r; - - for (var x in this.list_tagged) { - if (this.list_tagged[x].number === number) { - r = this.list_tagged[x]; - - break; - } - } - - if (! r || r.tags === undefined) - return null; - - return '1'+this.zone_id+this.area_id+r.tags; -} - -MsgArea.prototype.getUserStats = function() { - return this.msgbase.cfg ? msg_area.grp_list[this.msgbase.cfg.grp_number].sub_list[msg_area.sub[this.msgbase.cfg.code].index] : []; -} - -MsgArea.prototype.MessageNext = function(page) { - var x = null; - - if (! page) - return undefined; - - var msgid = page.substr(7,4); - - for(x in this.list_tagged) { - if (this.list_tagged[x].tags === msgid) { - break; + write(); // @todo This is needed for this to work? } - write(); // @todo This is needed for this to work? + return msgs; } - //log(LOG_DEBUG,'- Next Message is:'+JSON.stringify(this.list_tagged[(parseInt(x)+1)])+', msgid:'+msgid+', page:'+page+', x:'+x); - - /* - = Our next message is either - + x+1 if x < this.list_tagged.length - + x=0 if x == this.list_tagged.length (-1) - + null if this.list_tagged.length == null; (thus no messages) + // @todo review + /** + * New Messages for the logged in user */ + MsgArea.prototype.newMsgsToMe = function() { + var msgs = []; + var stats = this.getUserStats(); + var last = null; + //log(LOG_DEBUG,'Users scan_ptr pointer: '+JSON.stringify(stats.scan_ptr)); - return x === null ? null : this.list_tagged[(parseInt(x) === this.list_tagged.length-1) ? 0 : (parseInt(x)+1)]; -} + for(var x in this.list_tagged) { + // Advance past our last scan_ptr + if (this.list_tagged[x].number <= stats.scan_ptr) { + if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias)) + last = x; -MsgArea.prototype.MessagePrev = function(page) { - var prev = null; - var x = null; + continue; + } - if (! page) - return undefined; + // Add our previous to me message + if (msgs.length === 0) + msgs.push(last !== null ? this.list_tagged[last] : []); - var msgid = page.substr(7,4); + if ((this.list_tagged[x].to === user.name) || (this.list_tagged[x].to === user.alias)) + msgs.push(this.list_tagged[x]); - for(x in this.list_tagged) { - if (this.list_tagged[x].tags === msgid) { - break; - - } else { - prev = x; + write(); // @todo This is needed for this to work? } - write(); // @todo This is needed for this to work? + return msgs; } - /* - = Our previous message is either - + prev if a tag was found, unless - + prev is null, in which case it is this.list_tagged.length -1 - + null if x is still null (thus no messages) + // @todo review + /** + * Get a specific message with a tag */ + MsgArea.prototype.getMessage = function(page) { + var msg = undefined; - // If prev is still null, then our last message must be the last one, unless x is null then there are no messages - return x === null ? null : this.list_tagged[(prev === null) ? this.list_tagged.length-1 : parseInt(prev)]; -} + for(var x in this.list_tagged) { + if (this.list_tagged[x].tags === page) { + msg = this.list_tagged[x]; + break; + } + write(); // @todo This is needed for this to work? + } -/** - * Tag messages with a frame number - * @note: May need to run jsexec with -m 32MB to overcome memory issues - * - * @returns {boolean} - */ -MsgArea.prototype.tag_msgs = function() { - var msgs = this.list_untagged; + if (! msg) + return undefined; - writeln("We have "+msgs.length+" messages to tag."); + if (! this.msgbase.open()) { + writeln(code+' cannot be opened (getMessage):'+this.msgbase.error); + return undefined; + } - // See if we need to tag something - if (! msgs.length) - return; + msg.grp_number = this.msgbase.cfg.grp_number; + var cfg = this.msgbase.cfg; + msg.subnum = msg_area.grp_list[cfg.grp_number].sub_list.filter(function(x) { return x.number === cfg.number; }).pop().index; + msg.content = this.msgbase.get_msg_body(false,msg.number,false,false,true,true); + this.msgbase.close(); - if (! this.msgbase.open()) { - writeln(code+' cannot be opened:'+this.msgbase.error); - return false; + return msg; } - var page_next = this.page_next; + // @todo review + /** + * Get a message page by pointer + * + * @param number + * @returns {string} + */ + MsgArea.prototype.getMessagePage = function(number) { + log(LOG_DEBUG,'Get Message Page with number ['+number+']'); - for(var x in msgs) { - msgs[x].tags = (''+(page_next)).padStart(4,'0'); + var r; - if(! this.msgbase.put_msg_header(msgs[x].number,msgs[x])) { - writeln('ERROR:'+this.msgbase.error); + for (var x in this.list_tagged) { + if (this.list_tagged[x].number === number) { + r = this.list_tagged[x]; + + break; + } + } + + if (! r || r.tags === undefined) + return null; + + return '1'+this.zone_id+this.area_id+r.tags; + } + + // @todo review + MsgArea.prototype.getUserStats = function() { + return this.msgbase.cfg ? msg_area.grp_list[this.msgbase.cfg.grp_number].sub_list[msg_area.sub[this.msgbase.cfg.code].index] : []; + } + + // @todo review + MsgArea.prototype.MessageNext = function(page) { + var x = null; + + if (! page) + return undefined; + + var msgid = page.substr(7,4); + + for(var x in this.list_tagged) { + if (this.list_tagged[x].tags === msgid) { + break; + } + + write(); // @todo This is needed for this to work? + } + + //log(LOG_DEBUG,'- Next Message is:'+JSON.stringify(this.list_tagged[(parseInt(x)+1)])+', msgid:'+msgid+', page:'+page+', x:'+x); + + /* + = Our next message is either + + x+1 if x < this.list_tagged.length + + x=0 if x == this.list_tagged.length (-1) + + null if this.list_tagged.length == null; (thus no messages) + */ + + return x === null ? null : this.list_tagged[(parseInt(x) === this.list_tagged.length-1) ? 0 : (parseInt(x)+1)]; + } + + // @todo review + MsgArea.prototype.MessagePrev = function(page) { + var prev = null; + var x = null; + + if (! page) + return undefined; + + var msgid = page.substr(7,4); + + for(var x in this.list_tagged) { + if (this.list_tagged[x].tags === msgid) { + break; + + } else { + prev = x; + } + + write(); // @todo This is needed for this to work? + } + + /* + = Our previous message is either + + prev if a tag was found, unless + + prev is null, in which case it is this.list_tagged.length -1 + + null if x is still null (thus no messages) + */ + + // If prev is still null, then our last message must be the last one, unless x is null then there are no messages + return x === null ? null : this.list_tagged[(prev === null) ? this.list_tagged.length-1 : parseInt(prev)]; + } + + /** + * Tag messages with a frame number + * @note: May need to run jsexec with -m 32MB to overcome memory issues + * + * @returns {boolean} + */ + MsgArea.prototype.tag_msgs = function() { + var msgs = this.untagged; + var msgbase = new MsgAreas().getArea(FRAMES_MSG_BASE).msgbase; + var page_next = this.page_next; + + writeln('* We have '+msgs.length+' messages to tag, starting at '+page_next); + + // See if we need to tag something + if (! msgs.length) + return; + + if (! msgbase.open()) { + writeln(code+' cannot be opened (tag_msgs):'+msgbase.error); + return false; + } + + writeln('Starting at:'+page_next+' (max:'+this.max_page+')'); + + for (var x in msgs) { + //writeln('- '+msgs[x].when_imported_time+', #:'+msgs[x].number); + + var frame = '1'+this.page_prefix+page_next; + + var hdr = { + to: frame+'a', + from: 'SYSTEM', + tags: frame, + date: msgs[x].date, + subject: this.code+':'+msgs[x].id, + }; + + var page = { + id: msgs[x].number, + area_id: this.area_id, + group_id: this.group_id, + date: msgs[x].date, + msgid: msgs[x].id, + imported: msgs[x].when_imported_time, + }; + + var body = LZString.compressToBase64(JSON.stringify(page)); + + writeln('Tagging:'+page.id+' Tag:'+page_next+' MSGID:'+page.msgid); + + if (! msgbase.save_msg(hdr,body)) { + writeln('! ERROR: Failed to tag '+msgs[x].number); + exit(1); + } - } else { page_next++; - if (page_next > MAX_PAGE_NUM) + if (page_next > this.max_page) page_next = 0; + + page_next = (''+page_next).padStart(4,'0'); } + + msgbase.close(); + + return true; } - this.msgbase.close(); - this.page_next = page_next; - - return true; -} - -MsgArea.prototype.page = function(msgid) { - return '1'+this.page_prefix+msgid; -} - -MsgAreas.prototype.getArea = function(area) { - log(LOG_DEBUG,'- AREA:'+JSON.stringify(area)); - if (area === undefined) - return undefined; - - var zone = (''+area).substr(1,4); - var echo = (''+area).substr(5,2); - log(LOG_DEBUG,' - zone:'+zone); - log(LOG_DEBUG,' - echo:'+echo); - - return this.areas.filter(function(x) { - return x.zone_id === zone && x.area_id === echo; - })[0] -} - -MsgAreas.prototype.getMessage = function(page) { - var area = this.getArea(page); - log(LOG_DEBUG,' - msg:'+JSON.stringify(page.substr(7,4))); - - return area ? area.getMessage(page.substr(7,4)) : undefined; -} - -MsgAreas.prototype.getUserStats = function(page) { - var area = this.getArea(page); - - return area ? msg_area.grp_list[area.msgbase.cfg.grp_number].sub_list[msg_area.sub[area.msgbase.cfg.code].index] : undefined; + // @todo review + MsgArea.prototype.page = function(msgid) { + return '1'+this.page_prefix+msgid; + } } diff --git a/load/page.js b/load/page.js index fe16cb2..78627b3 100644 --- a/load/page.js +++ b/load/page.js @@ -1,3 +1,4 @@ +'use strict'; /** PAGE.js handles ANSItex and ViewData page frames @@ -76,7 +77,7 @@ */ load('ansitex/load/windows.js'); // Our supporting window class -require('ansitex/load/msgbases.js','MAX_PAGE_NUM'); // To read/write to message bases +require('ansitex/load/msgbases.js','PAGE_LENGTH'); // To read/write to message bases require('sbbsdefs.js','SS_USERON'); // Need for our ANSI colors eg: BG_* /** @@ -196,7 +197,7 @@ function Page(debug) { } } - // @todo change this to Object.defineProperty() - see session.js + // @todo change this to Object.defineProperty() - see page.js /** * Determine if this frame is accessible to the current user */ @@ -294,6 +295,7 @@ function Page(debug) { this.__properties__.isPublic = bool; }); + // Key Array Page.prototype.__defineGetter__('key',function() { return this.__properties__.key; }); @@ -301,6 +303,9 @@ function Page(debug) { if (typeof array !== 'object') throw new Error('key must be an array :'+typeof array); + if (array.length !== 10) + throw new Error('key must contain 10 items :'+array); + return this.__properties__.key = array; }); @@ -430,7 +435,7 @@ function Page(debug) { var content = fields[i].value.split(''); - for (x=fields[i].x;x 0) throw new Error('Dynamic fields ['+df.length+'] without values:'+(df.map(function(item) { return item.name; }).join('|'))); // Render the display - for (y=1;y<=this.height;y++) { + for (var y=1;y<=this.height;y++) { var line = ''; if (new_line) @@ -529,7 +534,7 @@ function Page(debug) { if (debug) writeln('============== ['+y+'] ==============='); - for (x=1;x<=this.width;x++) { + for (var x=1;x<=this.width;x++) { if (debug) log(LOG_DEBUG,'* CELL : y:'+y+', x:'+x); // The current char value @@ -729,7 +734,7 @@ function Page(debug) { // Dump Header write('--:'); - for (x=0;x