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
18035
18036
18037
18038
18039
18040
18041
18042
18043
18044
18045
18046
18047
18048
18049
18050
18051
18052
18053
18054
18055
18056
18057
18058
18059
18060
18061
18062
18063
// of the loop.
if (file_isdir(files[i]))
{
retval = deltree(files[i]);
if (!retval)
break;
}
else
{
retval = file_remove(files[i]);
if (!retval)
break;
}
}
// Delete the directory specified by pDir.
if (retval)
retval = rmdir(pDir);
return retval;
*/
}
// Removes a trailing (back)slash from a path.
//
// Parameters:
// pPath: A directory path
//
// Return value: The path without a trailing (back)slash.
function withoutTrailingSlash(pPath)
{
if ((pPath == null) || (pPath == undefined))
return "";
var retval = pPath;
if (retval.length > 0)
{
var lastIndex = retval.length - 1;
var lastChar = retval.charAt(lastIndex);
if ((lastChar == "\\") || (lastChar == "/"))
retval = retval.substr(0, lastIndex);
}
return retval;
}
// Adds double-quotes around a string if the string contains spaces.
//
// Parameters:
// pStr: A string to add double-quotes around if it has spaces
//
// Return value: The string with double-quotes if it contains spaces. If the
// string doesn't contain spaces, then the same string will be
// returned.
function quoteStrWithSpaces(pStr)
{
if (typeof(pStr) != "string")
return "";
var strCopy = pStr;
if (pStr.indexOf(" ") > -1)
strCopy = "\"" + pStr + "\"";
return strCopy;
}
// Given a message header field list type number (i.e., the 'type' property for an
// entry in the field_list array in a message header), this returns a text label
// to be used for outputting the field.
//
// Parameters:
// pFieldListType: A field_list entry type (numeric)
// pIncludeTrailingColon: Optional boolean - Whether or not to include a trailing ":"
// at the end of the returned string. Defaults to true.
//
// Return value: A text label for the field (a string)
function msgHdrFieldListTypeToLabel(pFieldListType, pIncludeTrailingColon)
{
// The page at this URL lists the header field types:
// http://synchro.net/docs/smb.html#Header Field Types:

nightfox
committed
var fieldTypeLabel = "Unknown (" + pFieldListType.toString() + ")";
18080
18081
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
18112
18113
18114
18115
18116
18117
18118
18119
18120
18121
18122
18123
18124
18125
18126
18127
18128
18129
18130
18131
18132
18133
18134
18135
18136
18137
18138
switch (pFieldListType)
{
case 0: // Sender
fieldTypeLabel = "Sender";
break;
case 1: // Sender Agent
fieldTypeLabel = "Sender Agent";
break;
case 2: // Sender net type
fieldTypeLabel = "Sender Net Type";
break;
case 3: // Sender Net Address
fieldTypeLabel = "Sender Net Address";
break;
case 4: // Sender Agent Extension
fieldTypeLabel = "Sender Agent Extension";
break;
case 5: // Sending agent (Sender POS)
fieldTypeLabel = "Sender Agent";
break;
case 6: // Sender organization
fieldTypeLabel = "Sender Organization";
break;
case 16: // Author
fieldTypeLabel = "Author";
break;
case 17: // Author Agent
fieldTypeLabel = "Author Agent";
break;
case 18: // Author Net Type
fieldTypeLabel = "Author Net Type";
break;
case 19: // Author Net Address
fieldTypeLabel = "Author Net Address";
break;
case 20: // Author Extension
fieldTypeLabel = "Author Extension";
break;
case 21: // Author Agent (Author POS)
fieldTypeLabel = "Author Agent";
break;
case 22: // Author Organization
fieldTypeLabel = "Author Organization";
break;
case 32: // Reply To
fieldTypeLabel = "Reply To";
break;
case 33: // Reply To agent
fieldTypeLabel = "Reply To Agent";
break;
case 34: // Reply To net type
fieldTypeLabel = "Reply To net type";
break;
case 35: // Reply To net address
fieldTypeLabel = "Reply To net address";
break;
case 36: // Reply To extension
fieldTypeLabel = "Reply To (extended)";
break;

nightfox
committed
case 37: // Reply To position
18140
18141
18142
18143
18144
18145
18146
18147
18148
18149
18150
18151
18152
18153
18154
18155
18156
fieldTypeLabel = "Reply To position";
break;
case 38: // Reply To organization (0x26 hex)
fieldTypeLabel = "Reply To organization";
break;
case 48: // Recipient (0x30 hex)
fieldTypeLabel = "Recipient";
break;
case 162: // Seen-by
fieldTypeLabel = "Seen-by";
break;
case 163: // Path
fieldTypeLabel = "Path";
break;
case 176: // RFCC822 Header
fieldTypeLabel = "RFCC822 Header";
break;

nightfox
committed
case 177: // RFC822 MSGID
fieldTypeLabel = "RFC822 MSGID";
break;
case 178: // RFC822 REPLYID
fieldTypeLabel = "RFC822 REPLYID";
break;
case 240: // UNKNOWN
fieldTypeLabel = "UNKNOWN";
break;
case 241: // UNKNOWNASCII
fieldTypeLabel = "UNKNOWN (ASCII)";
break;
case 255:
fieldTypeLabel = "UNUSED";
break;

nightfox
committed
fieldTypeLabel = "Unknown (" + pFieldListType.toString() + ")";
break;
}
var includeTrailingColon = (typeof(pIncludeTrailingColon) == "boolean" ? pIncludeTrailingColon : true);
if (includeTrailingColon)
fieldTypeLabel += ":";
return fieldTypeLabel;
}

nightfox
committed
18184
18185
18186
18187
18188
18189
18190
18191
18192
18193
18194
18195
18196
18197
18198
18199
18200
// Capitalizes the first character of a string.
//
// Parameters:
// pStr: The string to capitalize
//
// Return value: A version of the sting with the first character capitalized
function capitalizeFirstChar(pStr)
{
var retStr = "";
if (typeof(pStr) == "string")
{
if (pStr.length > 0)
retStr = pStr.charAt(0).toUpperCase() + pStr.slice(1);
}
return retStr;
}
18201
18202
18203
18204
18205
18206
18207
18208
18209
18210
18211
18212
18213
18214
18215
18216
18217
18218
18219
18220
18221
18222
18223
18224
18225
18226
18227
18228
18229
18230
18231
18232
18233
18234
18235
18236
18237
18238
18239
18240
18241
18242
18243
18244
18245
18246
18247
18248
18249
18250
18251
18252
18253
18254
18255
18256
18257
18258
18259
18260
18261
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
18293
18294
18295
18296
18297
18298
18299
18300
18301
// Parses a list of numbers (separated by commas or spaces), which may contain
// ranges separated by dashes. Returns an array of the individual numbers.
//
// Parameters:
// pList: A comma-separated list of numbers, some which may contain
// 2 numbers separated by a dash denoting a range of numbers.
//
// Return value: An array of the individual numbers from the list
function parseNumberList(pList)
{
if (typeof(pList) != "string")
return [];
var numberList = [];
// Split pList on commas or spaces
var commaOrSpaceSepArray = pList.split(/[\s,]+/);
if (commaOrSpaceSepArray.length > 0)
{
// Go through the comma-separated array - If the element is a
// single number, then append it to the number list to be returned.
// If there is a range (2 numbers separated by a dash), then
// append each number in the range individually to the array to be
// returned.
for (var i = 0; i < commaOrSpaceSepArray.length; ++i)
{
// If it's a single number, append it to numberList.
if (/^[0-9]+$/.test(commaOrSpaceSepArray[i]))
numberList.push(+commaOrSpaceSepArray[i]);
// If there are 2 numbers separated by a dash, then split it on the
// dash and generate the intermediate numbers.
else if (/^[0-9]+-[0-9]+$/.test(commaOrSpaceSepArray[i]))
{
var twoNumbers = commaOrSpaceSepArray[i].split("-");
if (twoNumbers.length == 2)
{
var num1 = +twoNumbers[0];
var num2 = +twoNumbers[1];
// If the 1st number is bigger than the 2nd, then swap them.
if (num1 > num2)
{
var temp = num1;
num1 = num2;
num2 = temp;
}
// Append each individual number in the range to numberList.
for (var number = num1; number <= num2; ++number)
numberList.push(number);
}
}
}
}
return numberList;
}
// Inputs a single keypress from the user from a list of valid keys, allowing
// input modes (see K_* in sbbsdefs.js for mode bits). This is similar to
// console.getkeys(), except that this allows mode bits (such as K_NOCRLF, etc.).
//
// Parameters:
// pAllowedKeys: A list of allowed keys (string)
// pMode: Mode bits (see K_* in sbbsdefs.js)
//
// Return value: The user's inputted keypress
function getAllowedKeyWithMode(pAllowedKeys, pMode)
{
var userInput = "";
var keypress = "";
var i = 0;
var matchedKeypress = false;
while (!matchedKeypress)
{
keypress = console.getkey(K_NOECHO|pMode);
// Check to see if the keypress is one of the allowed keys
for (i = 0; i < pAllowedKeys.length; ++i)
{
if (keypress == pAllowedKeys[i])
userInput = keypress;
else if (keypress.toUpperCase() == pAllowedKeys[i])
userInput = keypress.toUpperCase();
else if (keypress.toLowerCase() == pAllowedKeys[i])
userInput = keypress.toLowerCase();
if (userInput.length > 0)
{
matchedKeypress = true;
// If K_NOECHO is not in pMode, then output the user's keypress
if ((pMode & K_NOECHO) == 0)
console.print(userInput);
// If K_NOCRLF is not in pMode, then output a CRLF
if ((pMode & K_NOCRLF) == 0)
console.crlf();
break;
}
}
}
return userInput;
}
18302
18303
18304
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
// Loads a text file (an .ans or .asc) into an array. This will first look for
// 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;
}
}
18363
18364
18365
18366
18367
18368
18369
18370
18371
18372
18373
18374
18375
18376
18377
18378
18379
18380
18381
18382
18383
/*
// 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;
}
*/
18384
18385
18386
18387
18388
18389
18390
18391
18392
18393
18394
18395
18396
18397
18398
18399
18400
18401
18402
18403
18404
18405
18406
18407
18408
18409
18410
18411
18412
18413
// 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;
}
18450
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
// 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;
}
18482
18483
18484
18485
18486
18487
18488
18489
18490
18491
18492
18493
18494
18495
18496
18497
18498
18499
18500
18501
18502
18503
18504
18505
18506
18507
18508
18509
18510
18511
18512
18513
18514
18515
18516
// 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;
}
18564
18565
18566
18567
18568
18569
18570
18571
18572
18573
18574
18575
18576
18577
18578
18579
18580
18581
18582
18583
18584
18585
18586
18587
18588
18589
18590
18591
18592
18593
18594
}
}
// 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;
}
18607
18608
18609
18610
18611
18612
18613
18614
18615
18616
18617
18618
18619
18620
18621
18622
18623
18624
18625
18626
18627
18628
18629
18630
18631
18632
18633
18634
18635
18636
18637
18638
18639
18640
18641
18642
18643
18644
18645
// 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
18646
18647
18648
18649
18650
18651
18652
18653
18654
18655
18656
18657
18658
18659
18660
18661
18662
18663
18664
18665
18666
18667
18668
18669
18670
18671
18672
18673
18674
18675
18676
18677
18678
18679
18680
18681
18682
18683
18684
18685
18686
18687
18688
18689
18690
18691
18692
18693
18694
18695
18696
18697
18698
18699
// 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;
}
/////////////////////////////////////////////////////////////////////////
18701
18702
18703
18704
18705
18706
18707
18708
18709
18710
18711
18712
18713
18714
18715
18716
18717
18718
18719
18720
18721
18722
18723
18724
18725
// 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();
}
// Closes a poll, using an existing MessageBase object.
//
// Parameters:
// pMsgbase: A MessageBase object representing the current sub-board. It
// must be open.
// pMsgNum: The message number (not the index)
//
// Return value: Boolean - Whether or not closing the poll succeeded
function closePollWithOpenMsgbase(pMsgbase, pMsgNum)
{
var pollClosed = false;
if ((pMsgbase !== null) && pMsgbase.is_open)
{

nightfox
committed
var userNameOrAlias = user.alias;
// See if the poll was posted using the user's real name instead of
// their alias
var msgHdr = pMsgbase.get_msg_header(false, pMsgNum, false);
if ((msgHdr != null) && ((msgHdr.attr & MSG_POLL) == MSG_POLL))
{

nightfox
committed
if (msgHdr.from.toUpperCase() == user.name.toUpperCase())
userNameOrAlias = msgHdr.from;
}

nightfox
committed
// Close the poll (the close_poll() method was added in the Synchronet
// 3.17 build on August 19, 2017)
pollClosed = pMsgbase.close_poll(pMsgNum, userNameOrAlias);
18752
18753
18754
18755
18756
18757
18758
18759
18760
18761
18762
18763
18764
18765
18766
18767
18768
18769
18770
18771
18772
18773
18774
}
return pollClosed;
}
// Closes a poll.
//
// Parameters:
// pSubBoardCode: The internal code of the sub-board
// pMsgNum: The message number (not the index)
//
// Return value: Boolean - Whether or not closing the poll succeeded
function closePoll(pSubBoardCode, pMsgNum)
{
var pollClosed = false;
var msgbase = new MsgBase(pSubBoardCode);
if (msgbase.open())
{
pollClosed = closePollWithOpenMsgbase(msgbase, pMsgNum);
msgbase.close();
}
return pollClosed;
}
18775
18776
18777
18778
18779
18780
18781
18782
18783
18784
18785
18786
18787
18788
18789
18790
18791
18792
18793
18794
18795
18796
18797
18798
18799
18800
18801
18802
18803
18804
18805
18806
18807
18808
18809
18810
// Gets a message header from the messagebase, either by index (offset) or number.
//
// Parameters:
// pMsgbase: Optional messagebase object. If this is provided, then pSubBoardCode is not used.
// pSubBoardCode: The messagebase sub-board code
// pByIdx: Boolean - Whether or not to get the message header by index (if false, then by number)
// pMsgIdxOrNum: The message index or number of the message header to retrieve
// pExpandFields: Boolean - Whether or not to expand fields for the message header
function getHdrFromMsgbase(pMsgbase, pSubBoardCode, pByIdx, pMsgIdxOrNum, pExpandFields)
{
var msgbaseIsOpen = false;
var msgbase = null;
var msgHdr = null;
if (pMsgbase == null)
{
msgbase = new MsgBase(pSubBoardCode);
msgbaseIsOpen = msgbase.open();
}
else
{
msgbase = pMsgbase;
msgbaseIsOpen = pMsgbase.is_open;
}
if (msgbaseIsOpen)
{
var getMsgHdr = true;
if (pByIdx)
getMsgHdr = ((pMsgIdxOrNum >= 0) && (pMsgIdxOrNum < msgbase.total_msgs))
if (getMsgHdr)
msgHdr = msgbase.get_msg_header(pByIdx, pMsgIdxOrNum, pExpandFields);
if (pMsgbase == null)
msgbase.close();
}
return msgHdr;
}
18811
18812
18813
18814
18815
18816
18817
18818
18819
18820
18821
18822
18823
18824
18825
18826
18827
18828
18829
18830
18831
18832
18833
18834
18835
18836
18837
18838
18839
18840
18841
18842
18843
18844
18845
18846
18847
18848
18849
18850
18851
18852
18853
18854
18855
18856
18857
18858
18859
18860
18861
18862
18863
18864
18865
18866
18867
18868
18869
18870
18871
18872
18873
18874
18875
18876
18877
18878
18879
18880
18881
18882
18883
18884
18885
18886
18887
18888
18889
18890
18891
18892
18893
18894
18895
// Inputs a string from the user, restricting their input to certain keys (optionally).
//
// Parameters:
// pKeys: A string containing valid characters for input. Optional
// pMaxNumChars: The maximum number of characters to input. Optional
// pCaseSensitive: Boolean - Whether or not the input should be case-sensitive. Optional.
// Defaults to true. If false, then the user input will be uppercased.
//
// Return value: A string containing the user's input
function consoleGetStrWithValidKeys(pKeys, pMaxNumChars, pCaseSensitive)
{
var maxNumChars = 0;
if ((typeof(pMaxNumChars) == "number") && (pMaxNumChars > 0))
maxNumChars = pMaxNumChars;
var regexPattern = (typeof(pKeys) == "string" ? "[" + pKeys + "]" : ".");
var caseSensitive = (typeof(pCaseSensitive) == "boolean" ? pCaseSensitive : true);
var regex = new RegExp(regexPattern, (caseSensitive ? "" : "i"));
var CTRL_H = "\x08";
var BACKSPACE = CTRL_H;
var CTRL_M = "\x0d";
var KEY_ENTER = CTRL_M;
var modeBits = (caseSensitive ? K_NONE : K_UPPER);
var userInput = "";
var continueOn = true;
while (continueOn)
{
var userChar = console.getkey(K_NOECHO|modeBits);
if (regex.test(userChar) && isPrintableChar(userChar))
{
var appendChar = true;
if ((maxNumChars > 0) && (userInput.length >= maxNumChars))
appendChar = false;
if (appendChar)
{
userInput += userChar;
if ((modeBits & K_NOECHO) == 0)
console.print(userChar);
}
}
else if (userChar == BACKSPACE)
{
if (userInput.length > 0)
{
if ((modeBits & K_NOECHO) == 0)
{
console.print(BACKSPACE);
console.print(" ");
console.print(BACKSPACE);
}
userInput = userInput.substr(0, userInput.length-1);
}
}
else if (userChar == KEY_ENTER)
{
continueOn = false;
if ((modeBits & K_NOCRLF) == 0)
console.crlf();
}
}
return userInput;
}
// Returns whether or not a character is printable.
//
// Parameters:
// pChar: A character to test
//
// Return value: Boolean - Whether or not the character is printable
function isPrintableChar(pChar)
{
// Make sure pChar is valid and is a string.
if (typeof(pChar) != "string")
return false;
if (pChar.length == 0)
return false;
// Make sure the character is a printable ASCII character in the range of 32 to 254,
// except for 127 (delete).
var charCode = pChar.charCodeAt(0);
return ((charCode > 31) && (charCode < 255) && (charCode != 127));
}
18896
18897
18898
18899
18900
18901
18902
18903
18904
18905
18906
18907
18908
18909
18910
18911
18912
18913
18914
18915
18916
18917
18918
18919
18920
18921
18922
18923
18924
18925
18926
18927
18928
18929
18930
18931
// Adds message attributes to a message header and saves it in the messagebase.
// To do that, this function first loads the messag header from the messagebase
// without expanded fields, applies the attributes, and then saves the header
// back to the messagebase.
//
// Parameters:
// pMsgbase: An open MessageBase object
// pMsgNum: The number of the message to update
// pMsgAttrs: The message attributes to apply to the message (numeric bitfield)
//
// Return value: An object containing the following properties:
// saveSucceeded: Boolean - Whether or not the message header was successfully saved
// msgAttrs: A numeric bitfield containing the updated attributes of the message header
function applyAttrsInMsgHdrInMessagbase(pMsgbase, pMsgNum, pMsgAttrs)
{
var retObj = {
saveSucceeded: false,
msgAttrs: 0
};
if ((pMsgbase == null) || !pMsgbase.is_open)
return retObj;
// Get the message header without expanded fields (we can't save it with
// expanded fields), then add the 'read' attribute and save it back to the messagebase.
var msgHdr = pMsgbase.get_msg_header(false, pMsgNum, false);
if (msgHdr != null)
{
msgHdr.attr |= pMsgAttrs;
retObj.saveSucceeded = pMsgbase.put_msg_header(false, pMsgNum, msgHdr);
if (retObj.saveSucceeded)
retObj.msgAttrs = msgHdr.attr;
}
return retObj;
}
18932
18933
18934
18935
18936
18937
18938
18939
18940
18941
18942
18943
18944
18945
18946
18947
18948
18949
18950
18951
18952
18953
18954
18955
18956
18957
// 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);
}
}