Skip to content
Snippets Groups Projects
DDMsgReader.js 727 KiB
Newer Older
//  pMsgHdr: A message header (returned from MsgBase.get_msg_header())
//
// Return value: Boolean - Whether or not the message has one of the attachment flags
function msgHdrHasAttachmentFlag(pMsgHdr)
{
	if (typeof(pMsgHdr) !== "object" || typeof(pMsgHdr.auxattr) === "undefined")
		return false;

	var attachmentFlag = false;
	if (typeof(MSG_FILEATTACH) !== "undefined" && typeof(MSG_MIMEATTACH) !== "undefined")
		attachmentFlag = (pMsgHdr.auxattr & (MSG_FILEATTACH|MSG_MIMEATTACH)) > 0;
	return attachmentFlag;
}

// Allows the user to download a message and its attachments, using the newer
// Synchronet interface (the function bbs.download_msg_attachments() must exist).
//
// Parameters:
//  pMsgHdr: The message header
//  pSubCode: The sub-board code that the message is in
function allowUserToDownloadMessage_NewInterface(pMsgHdr, pSubCode)
{
	if (typeof(bbs.download_msg_attachments) !== "function")
		return;
	if (typeof(pSubCode) !== "string")
		return;
	if (typeof(pMsgHdr) !== "object" || typeof(pMsgHdr.number) == "undefined")
		return;

	var msgBase = new MsgBase(pSubCode);
	if (msgBase.open())
	{
		// bbs.download_msg_attachments() requires a message header returned
		// by MsgBase.get_msg_header()
		var msgHdrForDownloading = msgBase.get_msg_header(false, pMsgHdr.number, false);
		// Allow the user to download the message
		if (!console.noyes("Download message", P_NOCRLF))
		{
			if (!download_msg(msgHdrForDownloading, msgBase, console.yesno("Plain-text only")))
				console.print("\x01n\r\nFailed\r\n");
		}
		// Allow the user to download the attachments
		console.creturn();
		bbs.download_msg_attachments(msgHdrForDownloading);
		msgBase.close();
	}
}

// From msglist.js - Prompts the user if they want to download the message text
function download_msg(msg, msgbase, plain_text)
{
	var fname = system.temp_dir + "msg_" + msg.number + ".txt";
	var f = new File(fname);
	if(!f.open("wb"))
		return false;
	var text = msgbase.get_msg_body(msg
				,/* strip ctrl-a */false
				,/* dot-stuffing */false
				,/* tails */true
				,plain_text);
	f.write(msg.get_rfc822_header(/* force_update: */false, /* unfold: */false
		,/* default_content_type */!plain_text));
	f.writeln(text);
	f.close();
	return bbs.send_file(fname);
}


////////// Message list sort functions

// For sorting message headers by date & time
//
// Parameters:
//  msgHdrA: The first message header
//  msgHdrB: The second message header
//
// Return value: -1, 0, or 1, depending on whether header A comes before,
//               is equal to, or comes after header B
function sortMessageHdrsByDateTime(msgHdrA, msgHdrB)
{
	// Return -1, 0, or 1, depending on whether msgHdrA's date & time comes
	// before, is equal to, or comes after msgHdrB's date & time
	// Convert when_written_time to local time before comparing the times
	var localWrittenTimeA = msgWrittenTimeToLocalBBSTime(msgHdrA);
	var localWrittenTimeB = msgWrittenTimeToLocalBBSTime(msgHdrB);
	var yearA = +strftime("%Y", localWrittenTimeA);
	var monthA = +strftime("%m", localWrittenTimeA);
	var dayA = +strftime("%d", localWrittenTimeA);
	var hourA = +strftime("%H", localWrittenTimeA);
	var minuteA = +strftime("%M", localWrittenTimeA);
	var secondA = +strftime("%S", localWrittenTimeA);
	var yearB = +strftime("%Y", localWrittenTimeB);
	var monthB = +strftime("%m", localWrittenTimeB);
	var dayB = +strftime("%d", localWrittenTimeB);
	var hourB = +strftime("%H", localWrittenTimeB);
	var minuteB = +strftime("%M", localWrittenTimeB);
	var secondB = +strftime("%S", localWrittenTimeB);
	if (yearA < yearB)
		return -1;
	else if (yearA > yearB)
		return 1;
	else
	{
		if (monthA < monthB)
			return -1;
		else if (monthA > monthB)
			return 1;
		else
		{
			if (dayA < dayB)
				return -1;
			else if (dayA > dayB)
				return 1;
			else
			{
				if (hourA < hourB)
					return -1;
				else if (hourA > hourB)
					return 1;
				else
				{
					if (minuteA < minuteB)
						return -1;
					else if (minuteA > minuteB)
						return 1;
					else
					{
						if (secondA < secondB)
							return -1;
						else if (secondA > secondB)
							return 1;
						else
							return 0;
					}
				}
			}
		}
	}
}

// Returns an array of internal sub-board codes to scan for a given scan scope.
//
// Parameters:
//  pScanScopeChar: A string specifying "A" for all sub-boards, "G" for current
//                  message group sub-boards, or "S" for the current sub-board
//
// Return value: An array of internal sub-board codes for sub-boards to scan
function getSubBoardsToScanArray(pScanScopeChar)
{
	var subBoardsToScan = [];
	if (pScanScopeChar == "A") // All sub-board scan
	{
		for (var grpIndex = 0; grpIndex < msg_area.grp_list.length; ++grpIndex)
		{
			for (var subIndex = 0; subIndex < msg_area.grp_list[grpIndex].sub_list.length; ++subIndex)
				subBoardsToScan.push(msg_area.grp_list[grpIndex].sub_list[subIndex].code);
		}
	}
	else if (pScanScopeChar == "G") // Group scan
	{
		for (var subIndex = 0; subIndex < msg_area.grp_list[bbs.curgrp].sub_list.length; ++subIndex)
			subBoardsToScan.push(msg_area.grp_list[bbs.curgrp].sub_list[subIndex].code);
	}
	else if (pScanScopeChar == "S") // Current sub-board scan
		subBoardsToScan.push(bbs.cursub_code);
	return subBoardsToScan;
}

// Returns whether a number is a valid scan mode
//
// Parameters:
//  pNum: A number to test
//
// Return value: Boolean - Whether or not the given number is a valid scan mode
function isValidScanMode(pNum)
{
	if (typeof(pNum) !== "number")
		return false;
	// The scan modes are defined in sbbsdefs.js
	var validScanModes = [SCAN_READ, SCAN_CONST, SCAN_NEW, SCAN_BACK, SCAN_TOYOU,
	                      SCAN_FIND, SCAN_UNREAD, SCAN_MSGSONLY, SCAN_POLLS, SCAN_INDEX];
	var numIsValidScanMode = false;
	for (var i = 0; i < validScanModes.length && !numIsValidScanMode; ++i)
		numIsValidScanMode = (pNum === validScanModes[i]);
	return numIsValidScanMode;
}

// Returns whether a user number is valid (only an actual, active user)
//
// Parameters:
//  pUserNum: A user number
//
// Return value: Boolean - Whether or not the given user number is valid
function isValidUserNum(pUserNum)
{
	if (typeof(pUserNum) !== "number")
		return false;
	if (pUserNum < 1 || pUserNum > system.lastuser)
		return false;

	var userIsValid = false;
	var theUser = new User(pUserNum);
	if (theUser != null && (theUser.settings & USER_DELETED) == 0 && (theUser.settings & USER_INACTIVE) == 0)
		userIsValid = true;
	return userIsValid;
}
// Returns the index of the last ANSI code in a string.
//
// Parameters:
//  pStr: The string to search in
//  pANSIRegexes: An array of regular expressions to use for searching for ANSI codes
//
// Return value: The index of the last ANSI code in the string, or -1 if not found
function idxOfLastANSICode(pStr, pANSIRegexes)
{
	var lastANSIIdx = -1;
	for (var i = 0; i < pANSIRegexes.length; ++i)
	{
		var lastANSIIdxTmp = regexLastIndexOf(pStr, pANSIRegexes[i]);
		if (lastANSIIdxTmp > lastANSIIdx)
			lastANSIIdx = lastANSIIdxTmp;
	}
	return lastANSIIdx;
}

// Returns the index of the first ANSI code in a string.
//
// Parameters:
//  pStr: The string to search in
//  pANSIRegexes: An array of regular expressions to use for searching for ANSI codes
//
// Return value: The index of the first ANSI code in the string, or -1 if not found
function idxOfFirstANSICode(pStr, pANSIRegexes)
{
	var firstANSIIdx = -1;
	for (var i = 0; i < pANSIRegexes.length; ++i)
	{
		var firstANSIIdxTmp = regexFirstIndexOf(pStr, pANSIRegexes[i]);
		if (firstANSIIdxTmp > firstANSIIdx)
			firstANSIIdx = firstANSIIdxTmp;
	}
	return firstANSIIdx;
}

// Returns the number of times an ANSI code is matched in a string.
//
// Parameters:
//  pStr: The string to search in
//  pANSIRegexes: An array of regular expressions to use for searching for ANSI codes
//
// Return value: The number of ANSI code matches in the string
function countANSICodes(pStr, pANSIRegexes)
{
	var ANSICount = 0;
	for (var i = 0; i < pANSIRegexes.length; ++i)
	{
		var matches = pStr.match(pANSIRegexes[i]);
		if (matches != null)
			ANSICount += matches.length;
	}
	return ANSICount;
}

// Removes ANSI codes from a string.
//
// Parameters:
//  pStr: The string to remove ANSI codes from
//  pANSIRegexes: An array of regular expressions to use for searching for ANSI codes
//
// Return value: A version of the string without ANSI codes
function removeANSIFromStr(pStr, pANSIRegexes)
{
	if (typeof(pStr) != "string")
		return "";

	var theStr = pStr;
	for (var i = 0; i < pANSIRegexes.length; ++i)
		theStr = theStr.replace(pANSIRegexes[i], "");
	return theStr;
}

// Returns the last index in a string where a regex is found.
// From this page:
// http://stackoverflow.com/questions/273789/is-there-a-version-of-javascripts-string-indexof-that-allows-for-regular-expr
//
// Parameters:
//  pStr: The string to search
//  pRegex: The regular expression to match in the string
//  pStartPos: Optional - The starting position in the string.  If this is not
//             passed, then the end of the string will be used.
//
// Return value: The last index in the string where the regex is found, or -1 if not found.
function regexLastIndexOf(pStr, pRegex, pStartPos)
{
	pRegex = (pRegex.global) ? pRegex : new RegExp(pRegex.source, "g" + (pRegex.ignoreCase ? "i" : "") + (pRegex.multiLine ? "m" : ""));
	if (typeof(pStartPos) == "undefined")
		pStartPos = pStr.length;
	else if (pStartPos < 0)
		pStartPos = 0;
	var stringToWorkWith = pStr.substring(0, pStartPos + 1);
	var lastIndexOf = -1;
	var nextStop = 0;
	while ((result = pRegex.exec(stringToWorkWith)) != null)
	{
		lastIndexOf = result.index;
		pRegex.lastIndex = ++nextStop;
	}
    return lastIndexOf;
}

// Returns the first index in a string where a regex is found.
//
// Parameters:
//  pStr: The string to search
//  pRegex: The regular expression to match in the string
//
// Return value: The first index in the string where the regex is found, or -1 if not found.
function regexFirstIndexOf(pStr, pRegex)
{
	pRegex = (pRegex.global) ? pRegex : new RegExp(pRegex.source, "g" + (pRegex.ignoreCase ? "i" : "") + (pRegex.multiLine ? "m" : ""));
	var indexOfRegex = -1;
	var nextStop = 0;
	while ((result = pRegex.exec(pStr)) != null)
	{
		indexOfRegex = result.index;
		pRegex.lastIndex = ++nextStop;
	}
    return indexOfRegex;
}

// Returns whether or not a string has any ASCII drawing characters (typically above ASCII value 127).
//
// Parameters:
//  pText: The text to check
//
// Return value: Boolean - Whether or not the text has any ASCII drawing characters
function textHasDrawingChars(pText)
{
	if (typeof(pText) !== "string" || pText.length == 0)
		return false;

	if (typeof(textHasDrawingChars.chars) === "undefined")
	{
		textHasDrawingChars.chars = [ascii(169), ascii(170)];
		for (var asciiVal = 174; asciiVal <= 223; ++asciiVal)
			textHasDrawingChars.chars.push(ascii(asciiVal));
		textHasDrawingChars.chars.push(ascii(254));
	}
	var drawingCharsFound = false;
	for (var i = 0; i < textHasDrawingChars.chars.length && !drawingCharsFound; ++i)
		drawingCharsFound = drawingCharsFound || (pText.indexOf(textHasDrawingChars.chars[i]) > -1);
	return drawingCharsFound;
}

// Returns a string with a character repeated a given number of times
//
// Parameters:
//  pChar: The character to repeat in the string
//  pNumTimes: The number of times to repeat the character
//
// Return value: A string with the given character repeated the given number of times
function charStr(pChar, pNumTimes)
{
	if (typeof(pChar) !== "string" || pChar.length == 0 || typeof(pNumTimes) !== "number" || pNumTimes < 1)
		return "";

	var str = "";
	for (var i = 0; i < pNumTimes; ++i)
		str += pChar;
	return str;
}

// For debugging: 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 = "\x01n";
	if ((pClearLineAttrib != null) && (typeof(pClearLineAttrib) == "string"))
		clearLineAttrib = pClearLineAttrib;
	console.gotoxy(pX, pY);
	console.cleartoeol(clearLineAttrib);
	console.print(pText);
	if (pClearLineAfter)
	{
		console.gotoxy(pX, pY);
		console.cleartoeol(clearLineAttrib);
	}