Newer
Older
16001
16002
16003
16004
16005
16006
16007
16008
16009
16010
16011
16012
16013
16014
16015
16016
16017
16018
16019
16020
16021
16022
16023
16024
16025
16026
16027
16028
16029
16030
16031
16032
16033
16034
16035
16036
16037
16038
16039
16040
16041
16042
16043
16044
16045
16046
16047
16048
16049
16050
16051
16052
16053
16054
16055
16056
16057
16058
16059
16060
16061
16062
16063
16064
16065
16066
16067
16068
16069
16070
16071
16072
16073
16074
16075
16076
16077
16078
16079
16080
16081
16082
16083
16084
16085
16086
16087
16088
16089
16090
16091
16092
16093
16094
16095
16096
16097
16098
16099
16100
16101
16102
16103
16104
16105
16106
// Return value: A string describing the search type value
function searchTypeValToStr(pSearchType)
{
if (typeof(pSearchType) != "number")
return "Unknown (not a number)";
var searchTypeStr = "";
switch (pSearchType)
{
case SEARCH_NONE:
searchTypeStr = "None (SEARCH_NONE)";
break;
case SEARCH_KEYWORD:
searchTypeStr = "Keyword (SEARCH_KEYWORD)";
break;
case SEARCH_FROM_NAME:
searchTypeStr = "'From' name (SEARCH_FROM_NAME)";
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
searchTypeStr = "'To' name (SEARCH_TO_NAME_CUR_MSG_AREA)";
break;
case SEARCH_TO_USER_CUR_MSG_AREA:
searchTypeStr = "To you (SEARCH_TO_USER_CUR_MSG_AREA)";
break;
case SEARCH_MSG_NEWSCAN:
searchTypeStr = "New message scan (SEARCH_MSG_NEWSCAN)";
break;
case SEARCH_MSG_NEWSCAN_CUR_SUB:
searchTypeStr = "New in current message area (SEARCH_MSG_NEWSCAN_CUR_SUB)";
break;
case SEARCH_MSG_NEWSCAN_CUR_GRP:
searchTypeStr = "New in current message group (SEARCH_MSG_NEWSCAN_CUR_GRP)";
break;
case SEARCH_MSG_NEWSCAN_ALL:
searchTypeStr = "Newscan - All (SEARCH_MSG_NEWSCAN_ALL)";
break;
case SEARCH_TO_USER_NEW_SCAN:
searchTypeStr = "To You new scan (SEARCH_TO_USER_NEW_SCAN)";
break;
case SEARCH_TO_USER_NEW_SCAN_CUR_SUB:
searchTypeStr = "To You new scan, current sub-board (SEARCH_TO_USER_NEW_SCAN_CUR_SUB)";
break;
case SEARCH_TO_USER_NEW_SCAN_CUR_GRP:
searchTypeStr = "To You new scan, current group (SEARCH_TO_USER_NEW_SCAN_CUR_GRP)";
break;
case SEARCH_TO_USER_NEW_SCAN_ALL:
searchTypeStr = "To You new scan, all sub-boards (SEARCH_TO_USER_NEW_SCAN_ALL)";
break;
case SEARCH_ALL_TO_USER_SCAN:
searchTypeStr = "All To You scan (SEARCH_ALL_TO_USER_SCAN)";
break;
default:
searchTypeStr = "Unknown (" + pSearchType + ")";
break;
}
return searchTypeStr;
}
// This function converts a reader mode string to one of the defined reader mode
// value constants. If the passed-in mode string is unknown, then the return value
// will be -1.
//
// Parameters:
// pModeStr: A string describing a reader mode ("read", "reader", "list", "lister")
//
// Return value: An integer representing the reader mode value (READER_MODE_READ,
// READER_MODE_LIST), or -1 if the passed-in mode string is unknown.
function readerModeStrToVal(pModeStr)
{
if (typeof(pModeStr) != "string")
return -1;
var readerModeInt = -1;
var modeStr = pModeStr.toLowerCase();
if ((modeStr == "read") || (modeStr == "reader"))
readerModeInt = READER_MODE_READ;
else if ((modeStr == "list") || (modeStr == "lister"))
readerModeInt = READER_MODE_LIST;
return readerModeInt;
}
// This function returns a boolean to signify whether or not the user's
// terminal supports both high-ASCII characters and ANSI codes.
function canDoHighASCIIAndANSI()
{
//return (console.term_supports(USER_ANSI) && (user.settings & USER_NO_EXASCII == 0));
return (console.term_supports(USER_ANSI));
}
// Searches a given range in an open message base and returns an object with arrays
// containing the message headers (0-based indexed and indexed by message number)
// with the message headers of any found messages.
//
// Parameters:
// pSubCode: The internal code of the message sub-board
// pSearchType: The type of search to do (one of the SEARCH_ values)
// pSearchString: The string to search for.
// pListingPersonalEmailFromUser: Optional boolean - Whether or not we're listing
// personal email sent by the user. This defaults
// to false.
// pStartIndex: The starting message index (0-based). Optional; defaults to 0.
// pEndIndex: One past the last message index. Optional; defaults to the total number
// of messages.
//
// Return value: An object with the following arrays:
// indexed: A 0-based indexed array of message headers
function searchMsgbase(pSubCode, pSearchType, pSearchString, pListingPersonalEmailFromUser, pStartIndex, pEndIndex)
{
var msgHeaders = {
indexed: []
};
if ((pSubCode != "mail") && ((typeof(pSearchString) != "string") || !searchTypePopulatesSearchResults(pSearchType)))
return msgHeaders;
var msgbase = new MsgBase(pSubCode);
if (!msgbase.open())
return msgHeaders;
var startMsgIndex = 0;
var endMsgIndex = msgbase.total_msgs;
if (typeof(pStartIndex) == "number")
{
if ((pStartIndex >= 0) && (pStartIndex < msgbase.total_msgs))
startMsgIndex = pStartIndex;
}
if (typeof(pEndIndex) == "number")
{
if ((pEndIndex >= 0) && (pEndIndex > startMsgIndex) && (pEndIndex <= msgbase.total_msgs))
endMsgIndex = pEndIndex;
}
// Define a search function for the message field we're going to search
var readingPersonalEmailFromUser = (typeof(pListingPersonalEmailFromUser) == "boolean" ? pListingPersonalEmailFromUser : false);
var matchFn = null;
switch (pSearchType)
{
// It might seem odd to have SEARCH_NONE in here, but it's here because
// when reading personal email, we need to search for messages only to
// the current user.
case SEARCH_NONE:
if (pSubCode == "mail")
{
// Set up the match function slightly differently depending on whether
// we're looking for mail from the current user or to the current user.
if (readingPersonalEmailFromUser)
{
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var msgText = strip_ctrl(pMsgBase.get_msg_body(false, pMsgHdr.number, false, false, true, true));
return gAllPersonalEmailOptSpecified || msgIsFromUser(pMsgHdr);
}
}
else
{
// We're reading mail to the user
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var msgText = strip_ctrl(pMsgBase.get_msg_body(false, pMsgHdr.number, false, false, true, true));
var msgMatchesCriteria = (gAllPersonalEmailOptSpecified || msgIsToUserByNum(pMsgHdr));
// If only new/unread personal email is to be displayed, then check
// that the message has not been read.
if (gCmdLineArgVals.onlynewpersonalemail)
msgMatchesCriteria = (msgMatchesCriteria && ((pMsgHdr.attr & MSG_READ) == 0));
return msgMatchesCriteria;
}
}
}
break;
case SEARCH_KEYWORD:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var msgText = strip_ctrl(pMsgBase.get_msg_body(false, pMsgHdr.number, false, false, true, true));
var keywordFound = ((pMsgHdr.subject.toUpperCase().indexOf(pSearchStr) > -1) || (msgText.toUpperCase().indexOf(pSearchStr) > -1));
if (pSubBoardCode == "mail")
return keywordFound && msgIsToUserByNum(pMsgHdr);
else
return keywordFound;
}
break;
case SEARCH_FROM_NAME:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
var fromNameFound = (pMsgHdr.from.toUpperCase() == pSearchStr.toUpperCase());
if (pSubBoardCode == "mail")
return fromNameFound && (gAllPersonalEmailOptSpecified || msgIsToUserByNum(pMsgHdr));
else
return fromNameFound;
}
break;
case SEARCH_TO_NAME_CUR_MSG_AREA:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
return (pMsgHdr.to.toUpperCase() == pSearchStr);
}
break;
case SEARCH_TO_USER_CUR_MSG_AREA:
16198
16199
16200
16201
16202
16203
16204
16205
16206
16207
16208
16209
16210
16211
16212
16213
16214
16215
16216
16217
16218
16219
case SEARCH_ALL_TO_USER_SCAN:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
// See if the message is not marked as deleted and the 'To' name
// matches the user's handle, alias, and/or username.
return (((pMsgHdr.attr & MSG_DELETE) == 0) && userNameHandleAliasMatch(pMsgHdr.to));
}
break;
case SEARCH_TO_USER_NEW_SCAN:
case SEARCH_TO_USER_NEW_SCAN_CUR_SUB:
case SEARCH_TO_USER_NEW_SCAN_CUR_GRP:
case SEARCH_TO_USER_NEW_SCAN_ALL:
if (pSubCode != "mail")
{
// If pStartIndex or pEndIndex aren't specified, then set
// startMsgIndex to the scan pointer and endMsgIndex to one
// past the index of the last message in the sub-board
if (typeof(pStartIndex) != "number")
{
// First, write some messages to the log if verbose logging is enabled
if (gCmdLineArgVals.verboselogging)
{
writeToSysAndNodeLog("New-to-user scan for " +
subBoardGrpAndName(pSubCode) + " -- Scan pointer: " +
msg_area.sub[pSubCode].scan_ptr);
}
//startMsgIndex = absMsgNumToIdx(msgbase, msg_area.sub[pSubCode].last_read);
startMsgIndex = absMsgNumToIdx(msgbase, GetScanPtrOrLastMsgNum(pSubCode));
if (startMsgIndex == -1)
{
msg_area.sub[pSubCode].scan_ptr = 0;
startMsgIndex = 0;
}
else
{
// If this message has been read, then start at the next message.
var startMsgHeader = msgbase.get_msg_header(true, startMsgIndex, false);
if (startMsgHeader == null)
++startMsgIndex;
else
{
if ((startMsgHeader.attr & MSG_READ) == MSG_READ)
++startMsgIndex;
}
}
}
if (typeof(pEndIndex) != "number")
endMsgIndex = (msgbase.total_msgs > 0 ? msgbase.total_msgs : 0);
}
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
// Note: This assumes pSubBoardCode is not "mail" (personal mail).
// See if the message 'To' name matches the user's handle, alias,
// and/or username and is not marked as deleted and is unread.
return (((pMsgHdr.attr & MSG_DELETE) == 0) && ((pMsgHdr.attr & MSG_READ) == 0) && userNameHandleAliasMatch(pMsgHdr.to));
}
break;
case SEARCH_MSG_NEWSCAN:
case SEARCH_MSG_NEWSCAN_CUR_SUB:
case SEARCH_MSG_NEWSCAN_CUR_GRP:
case SEARCH_MSG_NEWSCAN_ALL:
matchFn = function(pSearchStr, pMsgHdr, pMsgBase, pSubBoardCode) {
// Note: This assumes pSubBoardCode is not "mail" (personal mail).
// Get the offset of the last read message and compare it with the
// offset of the given message header
var lastReadMsgHdr = pMsgBase.get_msg_header(false, msg_area.sub[pSubBoardCode].last_read, false);
var lastReadMsgOffset = 0;
if (lastReadMsgHdr != null)
lastReadMsgOffset = msgNumToIdxFromMsgbase(pSubBoardCode, lastReadMsgHdr.number);

nightfox
committed
if (lastReadMsgOffset < 0)
lastReadMsgOffset = 0;
return (msgNumToIdxFromMsgbase(pSubBoardCode, pMsgHdr.number) > lastReadMsgOffset);
}
break;
}
// Search the messages
if (matchFn != null)
{
// If get_all_msg_headers exists as a function, then use it. Otherwise,
// iterate through all message offsets and get the headers. We want to
// use get_all_msg_hdrs() if possible because that will include information
// about how many votes each message got (up/downvotes for regular
// messages or who voted for which options for poll messages).
if (useGetAllMsgHdrs && (typeof(msgbase.get_all_msg_headers) === "function"))
{
// Pass false to get_all_msg_headers() to tell it not to include vote messages
// (the parameter was introduced in Synchronet 3.17+)
var tmpHdrs = msgbase.get_all_msg_headers(false);
// Re-do startMsgIndex and endMsgIndex based on the message headers we got
startMsgIndex = 0;
endMsgIndex = msgbase.total_msgs;
if (typeof(pStartIndex) == "number")
{
if ((pStartIndex >= 0) && (pStartIndex < tmpHdrs.length))
startMsgIndex = pStartIndex;
}
if (typeof(pEndIndex) == "number")
{
if ((pEndIndex >= 0) && (pEndIndex > startMsgIndex) && (pEndIndex <= tmpHdrs.length))
endMsgIndex = pEndIndex;
}
// Search the message headers
var msgIdx = 0;
for (var prop in tmpHdrs)
{
// Only add the message header if the message is readable to the user
// and msgIdx is within bounds
if ((msgIdx >= startMsgIndex) && (msgIdx < endMsgIndex) && isReadableMsgHdr(tmpHdrs[prop], pSubCode))
{
if (tmpHdrs[prop] != null)
{
if (matchFn(pSearchString, tmpHdrs[prop], msgbase, pSubCode))
msgHeaders.indexed.push(tmpHdrs[prop]);
}
}
++msgIdx;
}
}
else
{
for (var msgIdx = startMsgIndex; msgIdx < endMsgIndex; ++msgIdx)
{
var msgHeader = msgbase.get_msg_header(true, msgIdx, false);
// I've seen situations where the message header object is null for
// some reason, so check that before running the search function.
if (msgHeader != null)
{
if (matchFn(pSearchString, msgHeader, msgbase, pSubCode))
msgHeaders.indexed.push(msgHeader);
}
}
}
}
msgbase.close();
return msgHeaders;
}
// Returns whether or not a message is to the current user (either the current
// logged-in user or the user specified by the userNum command-line argument)
// and is not deleted.
//
// Parameters:
// pMsgHdr: A message header object
//
// Return value: Boolean - Whether or not the message is to the user and is not
// deleted.
function msgIsToUserByNum(pMsgHdr)
{
if (typeof(pMsgHdr) != "object")
return false;
// Return false if the message is marked as deleted
if ((pMsgHdr.attr & MSG_DELETE) == MSG_DELETE)
return false;
var msgIsToUser = false;
// If an alternate user number was specified on the command line, then use that
// user information. Otherwise, use the current logged-in user.
if (gCmdLineArgVals.hasOwnProperty("altUserNum"))
msgIsToUser = (pMsgHdr.to_ext == gCmdLineArgVals.altUserNum);
else
msgIsToUser = (pMsgHdr.to_ext == user.number);
return msgIsToUser;
}
// Returns whether or not a message is from the current user (either the current
// logged-in user or the user specified by the userNum command-line argument)
// and is not deleted.
//
// Parameters:
// pMsgHdr: A message header object
//
// Return value: Boolean - Whether or not the message is from the logged-in user
// and is not deleted.
function msgIsFromUser(pMsgHdr)
{
if (typeof(pMsgHdr) != "object")
return false;
16373
16374
16375
16376
16377
16378
16379
16380
16381
16382
16383
16384
16385
16386
16387
16388
16389
16390
16391
16392
16393
16394
16395
16396
16397
16398
// Return false if the message is marked as deleted
if ((pMsgHdr.attr & MSG_DELETE) == MSG_DELETE)
return false;
var isFromUser = false;
// If an alternate user number was specified on the command line, then use that
// user information. Otherwise, use the current logged-in user.
if (pMsgHdr.hasOwnProperty("from_ext"))
{
if (gCmdLineArgVals.hasOwnProperty("altUserNum"))
isFromUser = (pMsgHdr.from_ext == gCmdLineArgVals.altUserNum);
else
isFromUser = (pMsgHdr.from_ext == user.number);
}
else
{
var hdrFromUpper = pMsgHdr.from.toUpperCase();
if (gCmdLineArgVals.hasOwnProperty("altUserName") && gCmdLineArgVals.hasOwnProperty("altUserAlias"))
isFromUser = ((hdrFromUpper == gCmdLineArgVals.altUserAlias.toUpperCase()) || (hdrFromUpper == gCmdLineArgVals.altUserName.toUpperCase()));
else
isFromUser = ((hdrFromUpper == user.alias.toUpperCase()) || (hdrFromUpper == user.name.toUpperCase()));
}
return isFromUser;
}
16401
16402
16403
16404
16405
16406
16407
16408
16409
16410
16411
16412
16413
16414
16415
16416
16417
16418
16419
16420
16421
16422
16423
16424
16425
16426
16427
16428
16429
16430
16431
16432
16433
16434
16435
16436
16437
16438
16439
16440
16441
16442
16443
16444
16445
16446
16447
16448
16449
// Given some text, this converts ANSI color codes to Synchronet codes and
// removes unwanted ANSI codes (such as cursor movement codes, etc.).
//
// Parameters:
// pText: A string to process
//
// Return value: A version of the string with Synchronet color codes converted to
// Synchronet attribute codes and unwanted ANSI codes removed
function cvtANSIToSyncAndRemoveUnwantedANSI(pText)
{
// Attributes
var txt = pText.replace(/\[0[mM]/g, "\1n"); // All attributes off
txt = txt.replace(/\[1[mM]/g, "\1h"); // Bold on (use high intensity)
txt = txt.replace(/\[5[mM]/g, "\1i"); // Blink on
// Foreground colors
txt = txt.replace(/\[30[mM]/g, "\1k"); // Black foreground
txt = txt.replace(/\[31[mM]/g, "\1r"); // Red foreground
txt = txt.replace(/\[32[mM]/g, "\1g"); // Green foreground
txt = txt.replace(/\[33[mM]/g, "\1y"); // Yellow foreground
txt = txt.replace(/\[34[mM]/g, "\1b"); // Blue foreground
txt = txt.replace(/\[35[mM]/g, "\1m"); // Magenta foreground
txt = txt.replace(/\[36[mM]/g, "\1c"); // Cyan foreground
txt = txt.replace(/\[37[mM]/g, "\1w"); // White foreground
// Background colors
txt = txt.replace(/\[40[mM]/g, "\1" + "0"); // Black background
txt = txt.replace(/\[41[mM]/g, "\1" + "1"); // Red background
txt = txt.replace(/\[42[mM]/g, "\1" + "2"); // Green background
txt = txt.replace(/\[43[mM]/g, "\1" + "3"); // Yellow background
txt = txt.replace(/\[44[mM]/g, "\1" + "4"); // Blue background
txt = txt.replace(/\[45[mM]/g, "\1" + "5"); // Magenta background
txt = txt.replace(/\[46[mM]/g, "\1" + "6"); // Cyan background
txt = txt.replace(/\[47[mM]/g, "\1" + "7"); // White background
// Convert ;-delimited modes (such as [Value;...;Valuem)
txt = ANSIMultiConvertToSyncCodes(txt);
// Remove ANSI codes that are not wanted (such as moving the cursor, etc.)
txt = txt.replace(/\[[0-9]+[aA]/g, ""); // Cursor up
txt = txt.replace(/\[[0-9]+[bB]/g, ""); // Cursor down
txt = txt.replace(/\[[0-9]+[cC]/g, ""); // Cursor forward
txt = txt.replace(/\[[0-9]+[dD]/g, ""); // Cursor backward
txt = txt.replace(/\[[0-9]+;[0-9]+[hH]/g, ""); // Cursor position
txt = txt.replace(/\[[0-9]+;[0-9]+[fF]/g, ""); // Cursor position
txt = txt.replace(/\[[sS]/g, ""); // Restore cursor position
txt = txt.replace(/\[2[jJ]/g, ""); // Erase display
txt = txt.replace(/\[[kK]/g, ""); // Erase line
txt = txt.replace(/\[=[0-9]+[hH]/g, ""); // Set various screen modes
txt = txt.replace(/\[=[0-9]+[lL]/g, ""); // Reset various screen modes
return txt;
}
16450
16451
16452
16453
16454
16455
16456
16457
16458
16459
16460
16461
16462
16463
16464
16465
16466
16467
16468
16469
16470
16471
16472
16473
16474
16475
16476
16477
16478
16479
16480
16481
16482
16483
16484
16485
16486
16487
16488
16489
// Returns whether a given message group index & sub-board index (or the current ones,
// based on bbs.curgrp and bbs.cursub) are for the last message sub-board on the system.
//
// Parameters:
// pGrpIdx: Optional - The index of the message group. If not specified, this will
// default to bbs.curgrp. If bbs.curgrp is not defined in that case,
// then this method will return false.
// pSubIdx: Optional - The index of the message sub-board. If not specified, this will
// default to bbs.cursub. If bbs.cursub is not defined in that case,
// then this method will return false.
//
// Return value: Boolean - Whether or not the current/given message group index & sub-board
// index are for the last message sub-board on the system. If there
// are any issues with any of the values (including bbs.curgrp or
// bbs.cursub), this method will return false.
function curMsgSubBoardIsLast(pGrpIdx, pSubIdx)
{
var curGrp = 0;
if (typeof(pGrpIdx) == "number")
curGrp = pGrpIdx;
else if (typeof(bbs.curgrp) == "number")
curGrp = bbs.curgrp;
else
return false;
var curSub = 0;
if (typeof(pSubIdx) == "number")
curSub = pSubIdx;
else if (typeof(bbs.cursub) == "number")
curSub = bbs.cursub;
else
return false;
return (curGrp == msg_area.grp_list.length-1) && (curSub == msg_area.grp_list[msg_area.grp_list.length-1].sub_list.length-1);
}
// Parses arguments, where each argument in the given array is in the format
// -arg=val. If the value is the string "true" or "false", then the value will
// be a boolean. Otherwise, the value will be a string.
//
// Parameters:
// argv: An array of strings containing values in the format -arg=val
//
// Return value: An object containing the argument values. The index will be
// the argument names, converted to lowercase. The values will
// be either the string argument values or boolean values, depending
// on the formats of the arguments passed in.
function parseArgs(argv)
{
var argVals = getDefaultArgParseObj();
// Sanity checking for argv - Make sure it's an array
if ((typeof(argv) != "object") || (typeof(argv.length) != "number"))
return argVals;
// First, test the arguments to see if they're in a format as called by
// Synchronet for loadable modules
argVals = parseLoadableModuleArgs(argv);
if (argVals.loadableModule)
return argVals;
// Go through argv looking for strings in the format -arg=val and parse them
// into objects in the argVals array.
var equalsIdx = 0;
var argName = "";
var argVal = "";
var argValLower = ""; // For case-insensitive "true"/"false" matching
var argValIsTrue = false;
for (var i = 0; i < argv.length; ++i)
{
// We're looking for strings that start with "-", except strings that are
// only "-".
if ((typeof(argv[i]) != "string") || (argv[i].length == 0) ||
(argv[i].charAt(0) != "-") || (argv[i] == "-"))
{
continue;
}
// Look for an = and if found, split the string on the =
equalsIdx = argv[i].indexOf("=");
// If a = is found, then split on it and add the argument name & value
// to the array. Otherwise (if the = is not found), then treat the
// argument as a boolean and set it to true (to enable an option).
if (equalsIdx > -1)
{
argName = argv[i].substring(1, equalsIdx).toLowerCase();
argVal = argv[i].substr(equalsIdx+1);
argValLower = argVal.toLowerCase();
// If the argument value is the word "true" or "false", then add it as a
// boolean. Otherwise, add it as a string.
argValIsTrue = (argValLower == "true");
if (argValIsTrue || (argValLower == "false"))
argVals[argName] = argValIsTrue;
else
argVals[argName] = argVal;
}
else // An equals sign (=) was not found. Add as a boolean set to true to enable the option.
{
argName = argv[i].substr(1).toLowerCase();
if ((argName == "chooseareafirst") || (argName == "personalemail") ||
(argName == "personalemailsent") || (argName == "allpersonalemail") ||
(argName == "verboselogging") || (argName == "suppresssearchtypetext") ||
(argName == "onlynewpersonalemail"))
{
argVals[argName] = true;
}
}
}
// Sanity checking
// If the arguments include personalEmail and personalEmail is enabled,
// then check to see if a search type was specified - If so, only allow
// keyword search and from name search.
if (argVals.hasOwnProperty("personalemail") && argVals.personalemail)
{
// If a search type is specified, only allow keyword search & from name
// search
if (argVals.hasOwnProperty("search"))
{
var searchValLower = argVals.search.toLowerCase();
if ((searchValLower != "keyword_search") && (searchValLower != "from_name_search"))
delete argVals.search;
}
}
16573
16574
16575
16576
16577
16578
16579
16580
16581
16582
16583
16584
16585
16586
16587
16588
16589
16590
16591
16592
16593
16594
16595
16596
16597
// If the arguments include userNum, make sure the value is all digits. If so,
// add altUserNum to the arguments as a number type for user matching when looking
// for personal email to the user.
if (argVals.hasOwnProperty("usernum"))
{
if (/^[0-9]+$/.test(argVals.usernum))
{
var specifiedUserNum = Number(argVals.usernum);
// If the specified number is different than the current logged-in
// user, then load the other user account and read their name and
// alias and also store their user number in the arg vals as a
// number.
if (specifiedUserNum != user.number)
{
var theUser = new User(specifiedUserNum);
argVals.altUserNum = theUser.number;
argVals.altUserName = theUser.name;
argVals.altUserAlias = theUser.alias;
}
else
delete argVals.usernum;
}
else
delete argVals.usernum;
}
return argVals;
}
16601
16602
16603
16604
16605
16606
16607
16608
16609
16610
16611
16612
16613
16614
16615
16616
16617
16618
16619
16620
16621
16622
16623
16624
16625
16626
16627
16628
16629
16630
16631
16632
16633
16634
16635
16636
16637
16638
16639
16640
16641
16642
16643
16644
16645
16646
16647
16648
16649
16650
16651
16652
16653
16654
16655
16656
16657
16658
16659
16660
16661
16662
16663
16664
16665
16666
16667
16668
16669
16670
16671
16672
16673
16674
16675
16676
16677
16678
16679
16680
16681
16682
16683
16684
16685
16686
16687
16688
16689
16690
16691
16692
16693
16694
16695
16696
16697
16698
16699
16700
16701
16702
16703
16704
16705
16706
16707
16708
16709
16710
16711
16712
16713
16714
16715
16716
16717
16718
16719
16720
16721
16722
16723
16724
16725
16726
16727
16728
16729
16730
16731
16732
16733
16734
16735
16736
16737
16738
16739
16740
16741
16742
16743
16744
16745
16746
16747
16748
16749
16750
16751
16752
16753
16754
// Helper for parseArgs() - If we get loadable module arguments from Synchronet, this parses them.
//
// Parameters:
// argv: An array of strings containing values in the format -arg=val
//
// Return value: An object containing the argument values. The property "loadableModule"
// in this object will be a boolean that specifies whether or not loadable
// module arguments were specified.
function parseLoadableModuleArgs(argv)
{
var argVals = getDefaultArgParseObj();
var allDigitsRegex = /^[0-9]+$/; // To check if a string consists only of digits
var arg1Lower = argv[0].toLowerCase();
// 2 args, and the 1st arg is a sub-board code & the 2nd arg is numeric & is
// the value of SCAN_INDEX: List messages in the specified sub-board (List Msgs module)
if (argv.length == 2 && subBoardCodeIsValid(arg1Lower) && allDigitsRegex.test(argv[1]) && +(argv[1]) === SCAN_INDEX)
{
argVals.loadableModule = true;
argVals.subboard = arg1Lower;
argVals.startmode = "list";
}
// 2 parameters: Whether or not all subs are being scanned (0 or 1), and the scan mode (numeric)
// (Scan Subs module)
else if (argv.length == 2 && /^[0-1]$/.test(argv[0]) && allDigitsRegex.test(argv[1]) && isValidScanMode(+(argv[1])))
{
argVals.loadableModule = true;
var scanAllSubs = (argv[0] == "1");
var scanMode = +(argv[1]);
if ((scanMode & SCAN_NEW) == SCAN_NEW)
{
// Newscan
// TODO: SCAN_CONST and SCAN_BACK could be used along with SCAN_NEW
// SCAN_CONST: Continuous message scanning
// SCAN_BACK: Display most recent message if none new
argVals.search = "new_msg_scan";
argVals.suppresssearchtypetext = true;
if (scanAllSubs)
argVals.search = "new_msg_scan_all";
}
else if (((scanMode & SCAN_TOYOU) == SCAN_TOYOU) || ((scanMode & SCAN_UNREAD) == SCAN_UNREAD))
{
// Scan for messages posted to you/new messages posted to you
argVals.startmode = "read";
argVals.search = "to_user_new_scan";
argVals.suppresssearchtypetext = true;
if (scanAllSubs)
argVals.search = "to_user_new_scan_all";
}
else if ((scanMode & SCAN_FIND) == SCAN_FIND)
{
argVals.search = "keyword_search";
argVals.startmode = "list";
}
else
{
// Stock Synchronet functionality. Includes SCAN_CONST and SCAN_BACK.
bbs.scan_subs(scanMode, scanAllSubs);
argVals.exitNow = true;
}
}
// Scan Msgs loadable module support:
// 1. The sub-board internal code
// 2. The scan mode (numeric)
// 3. Optional: Search text (if any)
else if ((argv.length == 2 || argv.length == 3) && subBoardCodeIsValid(arg1Lower) && allDigitsRegex.test(argv[1]) && isValidScanMode(+(argv[1])))
{
argVals.loadableModule = true;
var scanMode = +(argv[1]);
if (scanMode == SCAN_READ)
{
argVals.subboard = arg1Lower;
argVals.startmode = "read";
// If a search string is specified (as the 3rd command-line argument),
// then use it for a search scan.
if (argv.length == 3 && argv[2] != "")
{
argVals.search = "keyword_search";
argVals.searchtext = argv[2];
}
}
else if (scanMode == SCAN_FIND)
{
argVals.subboard = arg1Lower;
argVals.search = "keyword_search";
argVals.startmode = "list";
if (argv.length == 3 && argv[2] != "")
argVals.searchtext = argv[2];
}
// Some modes that the Digital Distortion Message Reader doesn't handle yet: Use
// Synchronet's stock behavior.
else
{
if (argv.length == 3)
bbs.scan_msgs(arg1Lower, scanMode, argv[2]);
else
bbs.scan_msgs(arg1Lower, scanMode);
argVals.exitNow = true;
}
}
// Reading personal email: 'Which' mailbox & user number (both numeric) (Read Mail module)
else if ((argv.length == 2 || argv.length == 3) && allDigitsRegex.test(argv[0]) && allDigitsRegex.test(argv[1]) && isValidUserNum(+(argv[1])))
{
argVals.loadableModule = true;
var whichMailbox = +(argv[0]);
var userNum = +(argv[1]);
// The optional 3rd argument in this case is mode bits. See if we should only display
// new (unread) personal email.
var newMailOnly = false;
if (argv.length >= 3)
{
var modeVal = +(argv[2]);
newMailOnly = (((modeVal & SCAN_FIND) == SCAN_FIND) && ((modeVal & LM_UNREAD) == LM_UNREAD));
}
// Start in list mode
argVals.startmode = "list"; // "read"
// Note: MAIL_ANY won't be passed to this script.
switch (whichMailbox)
{
case MAIL_YOUR: // Mail sent to you
argVals.personalemail = true;
argVals.usernum = argv[1];
if (newMailOnly)
argVals.onlynewpersonalemail = true;
break;
case MAIL_SENT: // Mail you have sent
argVals.personalemailsent = true;
argVals.usernum = argv[1];
break;
case MAIL_ALL:
argVals.allpersonalemail = true;
break;
default:
bbs.read_mail(whichMailbox);
argVals.exitNow = true;
break;
}
}
return argVals;
}
// Returns an object with default settings for argument parsing
function getDefaultArgParseObj()
{
return {
chooseareafirst: false,
personalemail: false,
onlynewpersonalemail: false,
personalemailsent: false,
verboselogging: false,
suppresssearchtypetext: false,
loadableModule: false,
exitNow: false
};
}
// Returns a string describing all message attributes (main, auxiliary, and net).
//
// Parameters:
// pMsgHdr: A message header object.
//
// Return value: A string describing all of the message attributes
function makeAllMsgAttrStr(pMsgHdr)
{
if ((pMsgHdr == null) || (typeof(pMsgHdr) != "object"))
return "";
var msgAttrStr = makeMainMsgAttrStr(pMsgHdr.attr);
var auxAttrStr = makeAuxMsgAttrStr(pMsgHdr.auxattr);
if (auxAttrStr.length > 0)
{
if (msgAttrStr.length > 0)
msgAttrStr += ", ";
msgAttrStr += auxAttrStr;
}
var netAttrStr = makeNetMsgAttrStr(pMsgHdr.netattr);
if (netAttrStr.length > 0)
{
if (msgAttrStr.length > 0)
msgAttrStr += ", ";
msgAttrStr += netAttrStr;
}
return msgAttrStr;
}
// Returns a string describing the main message attributes. Makes use of the
// gMainMsgAttrStrs object for the main message attributes and description
// strings.
//
// Parameters:
// pMainMsgAttrs: The bit field for the main message attributes
// (normally, the 'attr' property of a header object)
// pIfEmptyString: Optional - A string to use if there are no attributes set
//
// Return value: A string describing the main message attributes
function makeMainMsgAttrStr(pMainMsgAttrs, pIfEmptyString)
{
var msgAttrStr = "";
if (typeof(pMainMsgAttrs) == "number")
{
for (var prop in gMainMsgAttrStrs)
{
if ((pMainMsgAttrs & prop) == prop)
{
if (msgAttrStr.length > 0)
msgAttrStr += ", ";
msgAttrStr += gMainMsgAttrStrs[prop];
}
}
}
if ((msgAttrStr.length == 0) && (typeof(pIfEmptyString) == "string"))
msgAttrStr = pIfEmptyString;
return msgAttrStr;
}
// Returns a string describing auxiliary message attributes. Makes use of the
// gAuxMsgAttrStrs object for the auxiliary message attributes and description
// strings.
//
// Parameters:
// pAuxMsgAttrs: The bit field for the auxiliary message attributes
// (normally, the 'auxattr' property of a header object)
// pIfEmptyString: Optional - A string to use if there are no attributes set
//
// Return value: A string describing the auxiliary message attributes
function makeAuxMsgAttrStr(pAuxMsgAttrs, pIfEmptyString)
{
var msgAttrStr = "";
if (typeof(pAuxMsgAttrs) == "number")
{
for (var prop in gAuxMsgAttrStrs)
{
if ((pAuxMsgAttrs & prop) == prop)
{
if (msgAttrStr.length > 0)
msgAttrStr += ", ";
msgAttrStr += gAuxMsgAttrStrs[prop];
}
}
}
if ((msgAttrStr.length == 0) && (typeof(pIfEmptyString) == "string"))
msgAttrStr = pIfEmptyString;
return msgAttrStr;
}
// Returns a string describing network message attributes. Makes use of the
// gNetMsgAttrStrs object for the network message attributes and description
// strings.
//
// Parameters:
// pNetMsgAttrs: The bit field for the network message attributes
// (normally, the 'netattr' property of a header object)
// pIfEmptyString: Optional - A string to use if there are no attributes set
//
// Return value: A string describing the network message attributes
function makeNetMsgAttrStr(pNetMsgAttrs, pIfEmptyString)
{
var msgAttrStr = "";
if (typeof(pNetMsgAttrs) == "number")
{
for (var prop in gNetMsgAttrStrs)
{
if ((pNetMsgAttrs & prop) == prop)
{
if (msgAttrStr.length > 0)
msgAttrStr += ", ";
msgAttrStr += gNetMsgAttrStrs[prop];
}
}
}
if ((msgAttrStr.length == 0) && (typeof(pIfEmptyString) == "string"))
msgAttrStr = pIfEmptyString;
16872
16873
16874
16875
16876
16877
16878
16879
16880
16881
16882
16883
16884
16885
16886
16887
16888
16889
16890
16891
16892
16893
16894
16895
16896
16897
16898
16899
16900
16901
16902
16903
16904
16905
16906
16907
16908
16909
16910
16911
16912
16913
16914
16915
16916
16917
16918
16919
16920
16921
return msgAttrStr;
}
// Given a sub-board code, this function returns a sub-board's group and name.
// If the given sub-board code is "mail", then this will return "Personal mail".
//
// Parameters:
// pSubBoardCode: An internal sub-board code
//
// Return value: A string containing the sub-board code group & name, or
// "Personal email" if it's the personal email sub-board
function subBoardGrpAndName(pSubBoardCode)
{
if (typeof(pSubBoardCode) != "string")
return "";
var subBoardGrpAndName = "";
if (pSubBoardCode == "mail")
subBoardGrpAndName = "Personal mail";
else
{
subBoardGrpAndName = msg_area.sub[pSubBoardCode].grp_name + " - "
+ msg_area.sub[pSubBoardCode].name;
}
return subBoardGrpAndName;
}
// Returns whether a given string matches the current user's name, handle, or alias.
// Does a case-insensitive match.
//
// Parameters:
// pStr: The string to match against the user's name/handle/alias
//
// Return value: Boolean - Whether or not the string matches the current user's name,
// handle, or alias
function userNameHandleAliasMatch(pStr)
{
if (typeof(pStr) != "string")
return false;
var strUpper = pStr.toUpperCase();
return ((strUpper == user.name.toUpperCase()) || (strUpper == user.handle.toUpperCase()) || (strUpper == user.alias.toUpperCase()));
}
// Writes a log message to the system log (using LOG_INFO log level) and to the
// node log. This will prepend the text "Digital Distortion Message Reader ("
// + user.alias + "): " to the log message.
//
// Parameters:
// pMessage: The message to log
// pLogLevel: The log level. Optional - Defaults to LOG_INFO.
function writeToSysAndNodeLog(pMessage, pLogLevel)
{
if (typeof(pMessage) != "string")
return;
var logMessage = "Digital Distortion Message Reader (" + user.alias + "): " + pMessage;
var logLevel = (typeof(pLogLevel) == "number" ? pLogLevel : LOG_INFO);
log(logLevel, logMessage);
bbs.log_str(logMessage);
}
16934
16935
16936
16937
16938
16939
16940
16941
16942
16943
16944
16945
16946
16947
16948
16949
16950
16951
16952
16953
16954
16955
16956
16957
16958
16959
// This function looks up and returns a sub-board code from the sub-board number.
// If no matching sub-board is found, this will return an empty string.
//
// Parameters:
// pSubBoardNum: A sub-board number
//
// Return value: The sub-board code. If no matching sub-board is found, an empty
// string will be returned.
function getSubBoardCodeFromNum(pSubBoardNum)
{
// Ensure we're using a numeric type for the sub-board number
// (in case pSubBoardNum is a string rather than a number)
var subNum = Number(pSubBoardNum);
var subBoardCode = "";
for (var subCode in msg_area.sub)
{
if (msg_area.sub[subCode].number == subNum)
{
subBoardCode = subCode;
break;
}
}
return subBoardCode;
}
// Separates message text and any attachment data.
// This is for message headers generated in version 3.16 and earlier of Synchronet.
// In version 3.17 and later, Synchronet added auxiliary attributes (auxattr)
// MSG_FILEATTACH and MSG_MIMEATTACH as well as the function bbs.download_msg_attachments(msgHdr)
// which will allow a user to download attachments in a message.
16965
16966
16967
16968
16969
16970
16971
16972
16973
16974
16975
16976
16977
16978
16979
16980
16981
16982
16983
16984
16985
16986
16987
16988
16989
16990
16991
//
// Parameters:
// pMsgHdr: The message header object
// pMsgText: The text of a message
// pGetB64Data: Optional boolean - Whether or not to get the Base64-encoded
// data for base64-encoded attachments (i.e., in multi-part MIME
// emails). Defaults to true.
//
// Return value: An object containing the following properties:
// msgText: The text of the message, without any of the
// attachment base64-encoded data, etc. If
// the message doesn't have any attachments, then
// this will likely be the same as pMsgText.
// attachments: An array of objects containing the following properties
// for each attachment:
// B64Data: Base64-encoded file data - Only for attachments
// that were attached as base64 in the message (i.e.,
// in a multi-part MIME message). If the attachment
// was uploaded to the user's Synchronet mailbox,
// then the object won't have the B64Data property.
// filename: The name of the attached file
// fullyPathedFilename: The full path & filename of the
// attached file saved on the BBS machine
// errorMsg: An error message if anything went wrong. If
// nothing went wrong, this will be an empty string.
function determineMsgAttachments(pMsgHdr, pMsgText, pGetB64Data)
{
var retObj = {
msgText: "",
attachments: [],
errorMsg: ""
};
// Keep track of the user's inbox directory: sbbs/data/file/<userNum>.in
var userInboxDir = backslash(backslash(system.data_dir + "file") + format("%04d.in", user.number));
// If the message subject is a filename that exists in the user's