Newer
Older
18001
18002
18003
18004
18005
18006
18007
18008
18009
18010
18011
18012
18013
18014
18015
18016
18017
18018
18019
18020
18021
18022
18023
18024
18025
18026
18027
18028
18029
18030
18031
18032
18033
18034
// an .ans version, and if exists, convert to Synchronet colors before loading
// it. If an .ans doesn't exist, this will look for an .asc version.
//
// Parameters:
// pFilenameBase: The filename without the extension
// pMaxNumLines: Optional - The maximum number of lines to load from the text file
//
// Return value: An array containing the lines from the text file
function loadTextFileIntoArray(pFilenameBase, pMaxNumLines)
{
if (typeof(pFilenameBase) != "string")
return new Array();
var maxNumLines = (typeof(pMaxNumLines) == "number" ? pMaxNumLines : -1);
var txtFileLines = new Array();
// See if there is a header file that is made for the user's terminal
// width (areaChgHeader-<width>.ans/asc). If not, then just go with
// msgHeader.ans/asc.
var txtFileExists = true;
var txtFilenameFullPath = gStartupPath + pFilenameBase;
var txtFileFilename = "";
if (file_exists(txtFilenameFullPath + "-" + console.screen_columns + ".ans"))
txtFileFilename = txtFilenameFullPath + "-" + console.screen_columns + ".ans";
else if (file_exists(txtFilenameFullPath + "-" + console.screen_columns + ".asc"))
txtFileFilename = txtFilenameFullPath + "-" + console.screen_columns + ".asc";
else if (file_exists(txtFilenameFullPath + ".ans"))
txtFileFilename = txtFilenameFullPath + ".ans";
else if (file_exists(txtFilenameFullPath + ".asc"))
txtFileFilename = txtFilenameFullPath + ".asc";
else
txtFileExists = false;
if (txtFileExists)
{
var syncConvertedHdrFilename = txtFileFilename;
// If the user's console doesn't support ANSI and the header file is ANSI,
// then convert it to Synchronet attribute codes and read that file instead.
if (!console.term_supports(USER_ANSI) && (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS"))
{
syncConvertedHdrFilename = txtFilenameFullPath + "_converted.asc";
if (!file_exists(syncConvertedHdrFilename))
{
if (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS")
{
var dotIdx = txtFileFilename.lastIndexOf(".");
if (dotIdx >= 0)
{
var filenameBase = txtFileFilename.substr(0, dotIdx);
var cmdLine = system.exec_dir + "ans2asc \"" + txtFileFilename + "\" \""
+ syncConvertedHdrFilename + "\"";
// Note: Both system.exec(cmdLine) and
// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
// execute the command, but system.exec() seems noticeably faster.
system.exec(cmdLine);
}
}
else
syncConvertedHdrFilename = txtFileFilename;
}
}
18061
18062
18063
18064
18065
18066
18067
18068
18069
18070
18071
18072
18073
18074
18075
18076
18077
18078
18079
18080
18081
/*
// If the header file is ANSI, then convert it to Synchronet attribute
// codes and read that file instead. This is done so that this script can
// accurately get the file line lengths using console.strlen().
var syncConvertedHdrFilename = txtFilenameFullPath + "_converted.asc";
if (!file_exists(syncConvertedHdrFilename))
{
if (getStrAfterPeriod(txtFileFilename).toUpperCase() == "ANS")
{
var filenameBase = txtFileFilename.substr(0, dotIdx);
var cmdLine = system.exec_dir + "ans2asc \"" + txtFileFilename + "\" \""
+ syncConvertedHdrFilename + "\"";
// Note: Both system.exec(cmdLine) and
// bbs.exec(cmdLine, EX_NATIVE, gStartupPath) could be used to
// execute the command, but system.exec() seems noticeably faster.
system.exec(cmdLine);
}
else
syncConvertedHdrFilename = txtFileFilename;
}
*/
18082
18083
18084
18085
18086
18087
18088
18089
18090
18091
18092
18093
18094
18095
18096
18097
18098
18099
18100
18101
18102
18103
18104
18105
18106
18107
18108
18109
18110
18111
// Read the header file into txtFileLines
var hdrFile = new File(syncConvertedHdrFilename);
if (hdrFile.open("r"))
{
var fileLine = null;
while (!hdrFile.eof)
{
// Read the next line from the header file.
fileLine = hdrFile.readln(2048);
// fileLine should be a string, but I've seen some cases
// where it isn't, so check its type.
if (typeof(fileLine) != "string")
continue;
// Make sure the line isn't longer than the user's terminal
//if (fileLine.length > console.screen_columns)
// fileLine = fileLine.substr(0, console.screen_columns);
txtFileLines.push(fileLine);
// If the header array now has the maximum number of lines, then
// stop reading the header file.
if (txtFileLines.length == maxNumLines)
break;
}
hdrFile.close();
}
}
return txtFileLines;
}
// Returns the portion (if any) of a string after the period.
//
// Parameters:
// pStr: The string to extract from
//
// Return value: The portion of the string after the dot, if there is one. If
// not, then an empty string will be returned.
function getStrAfterPeriod(pStr)
{
var strAfterPeriod = "";
var dotIdx = pStr.lastIndexOf(".");
if (dotIdx > -1)
strAfterPeriod = pStr.substr(dotIdx+1);
return strAfterPeriod;
}
// Adjusts a message's when-written time to the BBS's local time.
//
// Parameters:
// pMsgHdr: A message header object
//
// Return value: The message's when_written_time adjusted to the BBS's local time.
// If the message header doesn't have a when_written_time or
// when_written_zone property, then this function will return -1.
function msgWrittenTimeToLocalBBSTime(pMsgHdr)
{
if (!pMsgHdr.hasOwnProperty("when_written_time") || !pMsgHdr.hasOwnProperty("when_written_zone_offset") || !pMsgHdr.hasOwnProperty("when_imported_zone_offset"))
return -1;
var timeZoneDiffMinutes = pMsgHdr.when_imported_zone_offset - pMsgHdr.when_written_zone_offset;
//var timeZoneDiffMinutes = pMsgHdr.when_written_zone - system.timezone;
var timeZoneDiffSeconds = timeZoneDiffMinutes * 60;
var msgWrittenTimeAdjusted = pMsgHdr.when_written_time + timeZoneDiffSeconds;
return msgWrittenTimeAdjusted;
}
18148
18149
18150
18151
18152
18153
18154
18155
18156
18157
18158
18159
18160
18161
18162
18163
18164
18165
18166
18167
18168
18169
18170
18171
18172
18173
18174
18175
18176
18177
18178
18179
// Returns a string containing the message group & sub-board numbers and
// descriptions.
//
// Parameters:
// pMsgbase: A MsgBase object
//
// Return value: A string containing the message group & sub-board numbers and
// descriptions
function getMsgAreaDescStr(pMsgbase)
{
if (typeof(pMsgbase) != "object")
return "";
if (!pMsgbase.is_open)
return "";
var descStr = "";
if (pMsgbase.cfg != null)
{
descStr = format("Group/sub-board num: %d, %d; %s - %s", pMsgbase.cfg.grp_number,
pMsgbase.subnum, msg_area.grp_list[pMsgbase.cfg.grp_number].description,
pMsgbase.cfg.description);
}
else
{
if ((pMsgbase.subnum == -1) || (pMsgbase.subnum == 65535))
descStr = "Electronic Mail";
else
descStr = "Unspecified";
}
return descStr;
}
18180
18181
18182
18183
18184
18185
18186
18187
18188
18189
18190
18191
18192
18193
18194
18195
18196
18197
18198
18199
18200
18201
18202
18203
18204
18205
18206
18207
18208
18209
18210
18211
18212
18213
18214
// Lets the sysop edit a user.
//
// Parameters:
// pUsername: The name of the user to edit
//
// Return value: A function containing the following properties:
// errorMsg: An error message on failure, or a blank string on success
function editUser(pUsername)
{
var retObj = new Object();
retObj.errorMsg = "";
if (typeof(pUsername) != "string")
{
retObj.errorMsg = "Given username is not a string";
return retObj;
}
// If the logged-in user is not a sysop, then just return.
if (!gIsSysop)
{
retObj.errorMsg = "Only a sysop can edit a user";
return retObj;
}
// If the user exists, then let the sysop edit the user.
var userNum = system.matchuser(pUsername);
if (userNum != 0)
bbs.exec("*str_cmds uedit " + userNum);
else
retObj.errorMsg = "User \"" + pUsername + "\" not found";
return retObj;
}
// Returns an object containing bare minimum properties necessary to
// display an invalid message header. Additionally, an object returned
// by this function will have an extra property, isBogus, that will be
// a boolean set to true.
//
// Parameters:
// pSubject: Optional - A string to use as the subject in the bogus message
// header object
function getBogusMsgHdr(pSubject)
{
var msgHdr = new Object();
msgHdr.subject = (typeof(pSubject) == "string" ? pSubject : "");
msgHdr.when_imported_time = 0;
msgHdr.when_written_time = 0;
msgHdr.when_written_zone = 0;
msgHdr.date = "Fri, 1 Jan 1960 00:00:00 -0000";
msgHdr.attr = 0;
msgHdr.to = "Nobody";
msgHdr.from = "Nobody";
msgHdr.number = 0;
msgHdr.offset = 0;
msgHdr.isBogus = true;
return msgHdr;
}
// Returns whether a message is readable to the user, based on its
// header and the sub-board code.
//
// Parameters:
// pMsgHdr: The header object for the message
// pSubBoardCode: The internal code for the sub-board the message is in
//
// Return value: Boolean - Whether or not the message is readable for the user
function isReadableMsgHdr(pMsgHdr, pSubBoardCode)
{
if (pMsgHdr === null)
return false;
// Let the sysop see unvalidated messages and private messages but not other users.
if (gIsSysop)
{

nightfox
committed
if (pSubBoardCode != "mail")
{

nightfox
committed
if ((msg_area.sub[pSubBoardCode].is_moderated && ((pMsgHdr.attr & MSG_VALIDATED) == 0)) ||
(((pMsgHdr.attr & MSG_PRIVATE) == MSG_PRIVATE) && !userHandleAliasNameMatch(pMsgHdr.to)))
{
return false;
}
18262
18263
18264
18265
18266
18267
18268
18269
18270
18271
18272
18273
18274
18275
18276
18277
18278
18279
18280
18281
18282
18283
18284
18285
18286
18287
18288
18289
18290
18291
18292
}
}
// If the message is deleted, determine whether it should be viewable, based
// on the system settings.
if ((pMsgHdr.attr & MSG_DELETE) == MSG_DELETE)
{
// If the user is a sysop, check whether sysops can view deleted messages.
// Otherwise, check whether users can view deleted messages.
if (gIsSysop)
{
if ((system.settings & SYS_SYSVDELM) == 0)
return false;
}
else
{
if ((system.settings & SYS_USRVDELM) == 0)
return false;
}
}
// The message voting and poll variables were added in sbbsdefs.js for
// Synchronet 3.17. Make sure they're defined before referencing them.
if (typeof(MSG_UPVOTE) != "undefined")
{
if ((pMsgHdr.attr & MSG_UPVOTE) == MSG_UPVOTE)
return false;
}
if (typeof(MSG_DOWNVOTE) != "undefined")
{
if ((pMsgHdr.attr & MSG_DOWNVOTE) == MSG_DOWNVOTE)
return false;
}
// Don't include polls as being unreadable messages - They just need to have
// their answer selections read from the header instead of the message body
/*
if (typeof(MSG_POLL) != "undefined")
{
if ((pMsgHdr.attr & MSG_POLL) == MSG_POLL)
return false;
}
*/
return true;
}
18305
18306
18307
18308
18309
18310
18311
18312
18313
18314
18315
18316
18317
18318
18319
18320
18321
18322
18323
18324
18325
18326
18327
18328
18329
18330
18331
18332
18333
18334
18335
18336
18337
18338
18339
18340
18341
18342
18343
// Returns the number of readable messages in a sub-board.
//
// Parameters:
// pMsgbase: The MsgBase object representing the sub-board
// pSubBoardCode: The internal code of the sub-board
//
// Return value: The number of readable messages in the sub-board
function numReadableMsgs(pMsgbase, pSubBoardCode)
{
if ((pMsgbase === null) || !pMsgbase.is_open)
return 0;
var numMsgs = 0;
if (typeof(pMsgbase.get_all_msg_headers) === "function")
{
var msgHdrs = pMsgbase.get_all_msg_headers(true);
for (var msgHdrsProp in msgHdrs)
{
if (msgHdrs[msgHdrsProp] == null)
continue;
else if (isReadableMsgHdr(msgHdrs[msgHdrsProp], pSubBoardCode))
++numMsgs;
}
}
else
{
var msgHeader;
for (var i = 0; i < pMsgbase.total_msgs; ++i)
{
msgHeader = msgBase.get_msg_header(true, i, false);
if (msgHeader == null)
continue;
else if (isReadableMsgHdr(msgHeader, pSubBoardCode))
++numMsgs;
}
}
return numMsgs;
}

nightfox
committed
18344
18345
18346
18347
18348
18349
18350
18351
18352
18353
18354
18355
18356
18357
18358
18359
18360
18361
18362
18363
18364
18365
18366
18367
18368
18369
18370
18371
18372
18373
18374
18375
18376
18377
18378
18379
18380
18381
18382
18383
18384
18385
18386
18387
18388
18389
18390
18391
18392
18393
18394
18395
18396
18397
// Deletes vote messages (messages that have voting response data for a message with
// a given message number).
//
// Parameters:
// pMsgbase: A MessageBase object containing the messages to be deleted
// pMsgNum: The number of the message for which vote messages should be deleted
// pIsMailSub: Boolean - Whether or not it's the personal email area
//
// Return value: An object containing the following properties:
// numVoteMsgs: The number of vote messages for the given message number
// numVoteMsgsDeleted: The number of vote messages that were deleted
// allVoteMsgsDeleted: Boolean - Whether or not all vote messages were deleted
function deleteVoteMsgs(pMsgbase, pMsgNum, pIsEmailSub)
{
var retObj = {
numVoteMsgs: 0,
numVoteMsgsDeleted: 0,
allVoteMsgsDeleted: true
};
if ((pMsgbase === null) || !pMsgbase.is_open)
return retObj;
if (typeof(pMsgNum) != "number")
return retObj;
if (pIsEmailSub)
return retObj;
// This relies on get_all_msg_headers() returning vote messages. The get_all_msg_headers()
// function was added in Synchronet 3.16, and the 'true' parameter to get vote headers was
// added in Synchronet 3.17.
if (typeof(pMsgbase.get_all_msg_headers) === "function")
{
var msgHdrs = pMsgbase.get_all_msg_headers(true);
for (var msgHdrsProp in msgHdrs)
{
if (msgHdrs[msgHdrsProp] == null)
continue;
// If this header is a vote header and its thread_back or reply_id matches the given message
// number, then we can delete this message.
var isVoteMsg = (((msgHdrs[msgHdrsProp].attr & MSG_VOTE) == MSG_VOTE) || ((msgHdrs[msgHdrsProp].attr & MSG_UPVOTE) == MSG_UPVOTE) || ((msgHdrs[msgHdrsProp].attr & MSG_DOWNVOTE) == MSG_DOWNVOTE));
if (isVoteMsg && (msgHdrs[msgHdrsProp].thread_back == pMsgNum) || (msgHdrs[msgHdrsProp].reply_id == pMsgNum))
{
++retObj.numVoteMsgs;
msgWasDeleted = pMsgbase.remove_msg(false, msgHdrs[msgHdrsProp].number);
retObj.allVoteMsgsDeleted = (retObj.allVoteMsgsDeleted && msgWasDeleted);
if (msgWasDeleted)
++retObj.numVoteMsgsDeleted;
}
}
}
return retObj;
}
/////////////////////////////////////////////////////////////////////////
18399
18400
18401
18402
18403
18404
18405
18406
18407
18408
18409
18410
18411
18412
18413
18414
18415
18416
18417
18418
18419
18420
18421
18422
18423
// Debug helper & error output functions
// Prints information from a message header on the screen, for debugging purpurposes.
//
// Parameters:
// pMsgHdr: A message header object
function printMsgHdr(pMsgHdr)
{
for (var prop in pMsgHdr)
{
if ((prop == "field_list") && (typeof(pMsgHdr[prop]) == "object"))
{
console.print(prop + ":\r\n");
for (var objI = 0; objI < pMsgHdr[prop].length; ++objI)
{
console.print(" " + objI + ":\r\n");
for (var innerProp in pMsgHdr[prop][objI])
console.print(" " + innerProp + ": " + pMsgHdr[prop][objI][innerProp] + "\r\n");
}
}
else
console.print(prop + ": " + pMsgHdr[prop] + "\r\n");
}
console.pause();
}
18424
18425
18426
18427
18428
18429
18430
18431
18432
18433
18434
18435
18436
18437
18438
18439
18440
18441
18442
18443
18444
18445
18446
18447
18448
18449
18450
// Writes some text on the screen at a given location with a given pause.
//
// Parameters:
// pX: The column number on the screen at which to write the message
// pY: The row number on the screen at which to write the message
// pText: The text to write
// pPauseMS: The pause time, in milliseconds
// pClearLineAttrib: Optional - The color/attribute to clear the line with.
// If not specified or null is specified, defaults to normal attribute.
// pClearLineAfter: Whether or not to clear the line again after the message is dispayed and
// the pause occurred. This is optional.
function writeWithPause(pX, pY, pText, pPauseMS, pClearLineAttrib, pClearLineAfter)
{
var clearLineAttrib = "\1n";
if ((pClearLineAttrib != null) && (typeof(pClearLineAttrib) == "string"))
clearLineAttrib = pClearLineAttrib;
console.gotoxy(pX, pY);
console.cleartoeol(clearLineAttrib);
console.print(pText);
if (pPauseMS > 0)
mswait(pPauseMS);
if (pClearLineAfter)
{
console.gotoxy(pX, pY);
console.cleartoeol(clearLineAttrib);
}
18451
18452
18453
18454
18455
18456
18457
18458
18459
18460
18461
18462
18463
18464
18465
18466
18467
18468
18469
18470
18471
18472
18473
18474
18475
18476
18477
18478
18479
18480
18481
18482
18483
18484
18485
18486
18487
18488
18489
18490
18491
18492
18493
18494
18495
18496
18497
18498
18499
18500
}
function logStackTrace(levels) {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox / chrome
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
callstack.push(lines[i]);
}
//Remove call to logStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
else if (window.opera && e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i < len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += " at " + lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to logStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
if (levels) {
console.print(callstack.slice(0, levels).join("\r\n"));
}
else {
console.print(callstack.join("\r\n"));
}