diff --git a/exec/load/attr_conv.js b/exec/load/attr_conv.js index 2c47200e36ab7f7885fdd71cd35af66a782375a4..12f403991974e69b971c72b5413dfa5ae076d9d0 100644 --- a/exec/load/attr_conv.js +++ b/exec/load/attr_conv.js @@ -1135,4 +1135,15 @@ function syncAttrCodesToANSI(pText) } else return pText; // No Synchronet-style attribute codes found, so just return the text. +} + +// Returns whether a string has any Synchronet attribute codes +// +// Parameters: +// pStr: the string to check +// +// Return value: Boolean - Whether or not the string has any Synchronet attribute codes +function hasSyncAttrCodes(pStr) +{ + return (pStr.search(/\1[krgybmcwhifn\-_01234567]/i) > -1); } \ No newline at end of file diff --git a/xtrn/DDMsgReader/DDMsgReader.js b/xtrn/DDMsgReader/DDMsgReader.js index 8ea091d0030d6e75e11da7988f0d355bda63174d..070dfbc74cc28d6a300e0cb8251552cfd2ab9247 100644 --- a/xtrn/DDMsgReader/DDMsgReader.js +++ b/xtrn/DDMsgReader/DDMsgReader.js @@ -27,8 +27,13 @@ * 2022-03-23 Eric Oulashin Version 1.47a * Now calls bbs.edit_msg() to edit an existing message (if * that function exists - It was added in Synchronet 3.18). - * 2022-03-23 Eric Oulashin Version 1.48 + * 2022-06-12 Eric Oulashin Version 1.48 * Improved display of ANSI messages via the use of the Graphic object + * 2022-06-13 Eric Oulashin Version 1.49 + * Refactor: Simplified saving a message to BBS machine for sysop + * (as-is, less processing); removed attachment stuff for pre-Synchronet + * 3.17; moved hasSyncAttrCodes() to attr_conv.js because that's where it + * needs to be. */ // TODO: In the message list, add the ability to search with / similar to my area chooser @@ -132,8 +137,8 @@ var ansiterm = require("ansiterm_lib.js", 'expand_ctrl_a'); // Reader version information -var READER_VERSION = "1.48"; -var READER_DATE = "2022-06-12"; +var READER_VERSION = "1.49"; +var READER_DATE = "2022-06-13"; // Keyboard key codes for displaying on the screen var UP_ARROW = ascii(24); @@ -4516,6 +4521,12 @@ function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea) graphic.width = this.msgAreaWidth; messageText = graphic.MSG; } + if (msgHdrHasAttachmentFlag(msgHeader)) + { + messageText = "\1n\1g\1h- This message contains one or more attachments. Press CTRL-A to download.\1n\r\n" + + "\1n\1g\1h--------------------------------------------------------------------------\1n\r\n" + + messageText; + } var useScrollingInterface = this.scrollingReaderInterface && console.term_supports(USER_ANSI); // If we switch to the non-scrolling interface here, then the calling method should // refresh the enhanced reader help line on the screen. @@ -4529,9 +4540,7 @@ function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea) { // If the message has ANSI codes, remove any ANSI clear screen codes from the message text if (msgHasANSICodes) - { messageText = messageText.replace(/\u001b\[[012]J/gi, ""); - } retObj = this.ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgArea, messageText, msgHasANSICodes, pOffset); } else @@ -4580,7 +4589,6 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA var msgInfo = this.GetMsgInfoForEnhancedReader(msgHeader, true, true, true, messageText, msgHasANSICodes); var topMsgLineIdxForLastPage = msgInfo.topMsgLineIdxForLastPage; - var msgFractionShown = msgInfo.msgFractionShown; var numSolidScrollBlocks = msgInfo.numSolidScrollBlocks; var numNonSolidScrollBlocks = msgInfo.numNonSolidScrollBlocks; var solidBlockStartRow = msgInfo.solidBlockStartRow; @@ -4614,6 +4622,8 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA solidBlockLastStartRow = solidBlockStartRow; console.gotoxy(1, console.screen_rows); } + + var msgHasAttachments = msgHdrHasAttachmentFlag(msgHeader); // User input loop var continueOn = true; while (continueOn) @@ -4664,8 +4674,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA // calling method will go to the next message/sub-board. // Otherwise (if the message was not deleted), refresh the // last 2 lines of the message on the screen. - var msgWasDeleted = this.PromptAndDeleteMessage(pOffset, promptPos, true, this.msgAreaWidth, - true, msgInfo.attachments); + var msgWasDeleted = this.PromptAndDeleteMessage(pOffset, promptPos, true, this.msgAreaWidth, true); if (msgWasDeleted) { var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset); @@ -4788,7 +4797,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA writeMessage = false; // Don't write the current message again break; case this.enhReaderKeys.showHelp: // Show the help screen - this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgInfo.hasAttachments); + this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgHasAttachments); // If the enhanced message header width is less than the console // width, then clear the screen to remove anything left on the // screen from the help screen. @@ -4819,8 +4828,8 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true); msgbase.close(); // Let the user reply to the message. - var replyRetObj = this.ReplyToMsg(extdMsgHdr, msgInfo.msgText, privateReply, pOffset); - retObj.userReplied = replyRetObj.postSucceeded; + var replyRetObj = this.ReplyToMsg(extdMsgHdr, messageText, privateReply, pOffset); + retObj.userReplied = replyRetObj.postSucceeded; //retObj.msgNotReadable = replyRetObj.msgWasDeleted; var msgWasDeleted = replyRetObj.msgWasDeleted; //if (retObj.msgNotReadable) @@ -5224,21 +5233,12 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA writeMessage = false; // No need to refresh the message break; case this.enhReaderKeys.downloadAttachments: // Download attachments - if (msgInfo.hasAttachments) + if (msgHasAttachments) { console.print("\1n"); console.gotoxy(1, console.screen_rows); console.crlf(); - // If bbs.download_msg_attachments() exists (Synchronet 3.17+), use - // the new method. Otherwise, use the older method. - if (typeof(bbs.download_msg_attachments) === "function") - allowUserToDownloadMessage_NewInterface(msgHeader, this.subBoardCode); - else - { - console.print("\1c- Download Attached Files -\1n"); - // Note: sendAttachedFiles() will output a CRLF at the beginning. - sendAttachedFiles(msgInfo.attachments); - } + allowUserToDownloadMessage_NewInterface(msgHeader, this.subBoardCode); // Refresh things on the screen console.clear("\1n"); @@ -5267,13 +5267,18 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA console.print("\1n"); if (filename.length > 0) { - //var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true, msgInfo.messageLines); - var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true); + var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename); console.gotoxy(promptPos); console.cleartoeol("\1n"); console.gotoxy(promptPos); if (saveMsgRetObj.succeeded) - console.print("\1n\1cThe message has been saved.\1n"); + { + var statusMsg = "\1n\1cThe message has been saved."; + if (msgHdrHasAttachmentFlag(msgHeader)) + statusMsg += " Attachments not saved."; + statusMsg += "\1n"; + console.print(statusMsg); + } else console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n"); mswait(ERROR_PAUSE_WAIT_MS); @@ -5651,16 +5656,16 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg nextAction: ACTION_NONE, refreshEnhancedRdrHelpLine: false }; - - // Separate the message text from any attachments in the message. - var msgAndAttachmentInfo = determineMsgAttachments(msgHeader, messageText, true); + + var msgHasAttachments = msgHdrHasAttachmentFlag(msgHeader); + // Only interpret @-codes if the user is reading personal email. There // are many @-codes that do some action such as move the cursor, execute a // script, etc., and I don't want users on message networks to do anything // malicious to users on other BBSes. if (this.readingPersonalEmail) - msgAndAttachmentInfo.msgText = replaceAtCodesInStr(msgAndAttachmentInfo.msgText); // Or this.ParseMsgAtCodes(msgAndAttachmentInfo.msgText, msgHeader) to replace only some @ codes - var msgTextWrapped = word_wrap(msgAndAttachmentInfo.msgText, console.screen_columns-1); + messageText = replaceAtCodesInStr(messageText); // Or this.ParseMsgAtCodes(messageText, msgHeader) to replace only some @ codes + var msgTextWrapped = (msgHasANSICodes ? messageText : word_wrap(messageText, console.screen_columns-1)); // Generate the key help text var keyHelpText = "\1n\1c\1h#\1n\1b, \1c\1hLeft\1n\1b, \1c\1hRight\1n\1b, "; @@ -5724,8 +5729,6 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg // calling method will go to the next message/sub-board. // Otherwise (if the message was not deleted), refresh the // last 2 lines of the message on the screen. - // TODO: For the DeleteMessage() call, pass the array of file - // attachments for it to delete (i.e., msgInfo.attachments) var msgWasDeleted = this.PromptAndDeleteMessage(pOffset); if (msgWasDeleted) { @@ -5802,7 +5805,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg console.crlf(); console.crlf(); } - this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgAndAttachmentInfo.hasAttachments); + this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgHasAttachments); if (!console.term_supports(USER_ANSI)) { console.crlf(); @@ -5832,7 +5835,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true); msgbase.close(); // Let the user reply to the message - var replyRetObj = this.ReplyToMsg(extdMsgHdr, msgAndAttachmentInfo.msgText, privateReply, pOffset); + var replyRetObj = this.ReplyToMsg(extdMsgHdr, messageText, privateReply, pOffset); retObj.userReplied = replyRetObj.postSucceeded; //retObj.msgNotReadable = replyRetObj.msgWasDeleted; var msgWasDeleted = replyRetObj.msgWasDeleted; @@ -6171,20 +6174,12 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg } break; case this.enhReaderKeys.downloadAttachments: // Download attachments - if (msgAndAttachmentInfo.hasAttachments) + if (msgHasAttachments) { console.print("\1n"); console.crlf(); console.print("\1c- Download Attached Files -\1n"); - // If bbs.download_msg_attachments() exists (Synchronet 3.17+), use - // the new method. Otherwise, use the older method. - if (typeof(bbs.download_msg_attachments) === "function") - allowUserToDownloadMessage_NewInterface(msgHeader, this.subBoardCode); - else - { - // Note: sendAttachedFiles() will output a CRLF at the beginning. - sendAttachedFiles(msgAndAttachmentInfo.attachments); - } + allowUserToDownloadMessage_NewInterface(msgHeader, this.subBoardCode); // Ensure the message is refreshed on the screen writeMessage = true; @@ -6209,9 +6204,14 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg console.crlf(); if (filename.length > 0) { - var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true); + var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename); if (saveMsgRetObj.succeeded) + { console.print("\1n\1cThe message has been saved.\1n"); + if (msgHdrHasAttachmentFlag(msgHeader)) + console.print(" Attachments not saved."); + console.print("\1n"); + } else console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n"); mswait(ERROR_PAUSE_WAIT_MS); @@ -8212,17 +8212,17 @@ function DigDistMsgReader_DisplaySyncMsgHeader(pMsgHdr) // Generate a string describing the message attributes, then output the default // header. var allMsgAttrStr = makeAllMsgAttrStr(pMsgHdr); - console.print("\1n\1w���������������������������������������������������������������������������۲��"); + console.print("\1n\1w���������������������������������������������������������������������������۲��"); console.crlf(); - console.print("\1n\1w�����\1cFrom\1w\1h: \1b" + pMsgHdr["from"].substr(0, console.screen_columns-12)); + console.print("\1n\1w�����\1cFrom\1w\1h: \1b" + pMsgHdr["from"].substr(0, console.screen_columns-12)); console.crlf(); - console.print("\1n\1w�����\1cTo \1w\1h: \1b" + pMsgHdr["to"].substr(0, console.screen_columns-12)); + console.print("\1n\1w�����\1cTo \1w\1h: \1b" + pMsgHdr["to"].substr(0, console.screen_columns-12)); console.crlf(); - console.print("\1n\1w�����\1cSubj\1w\1h: \1b" + pMsgHdr["subject"].substr(0, console.screen_columns-12)); + console.print("\1n\1w�����\1cSubj\1w\1h: \1b" + pMsgHdr["subject"].substr(0, console.screen_columns-12)); console.crlf(); - console.print("\1n\1w�����\1cDate\1w\1h: \1b" + dateTimeStr.substr(0, console.screen_columns-12)); + console.print("\1n\1w�����\1cDate\1w\1h: \1b" + dateTimeStr.substr(0, console.screen_columns-12)); console.crlf(); - console.print("\1n\1w�����\1cAttr\1w\1h: \1b" + allMsgAttrStr.substr(0, console.screen_columns-12)); + console.print("\1n\1w�����\1cAttr\1w\1h: \1b" + allMsgAttrStr.substr(0, console.screen_columns-12)); console.crlf(); } } @@ -10308,12 +10308,10 @@ function DigDistMsgReader_EnhReaderPromptYesNo(pQuestion, pMessageLines, pTopLin // pPromptRowWidth: Optional - The width of the prompt row (if pProptLoc is valid) // pConfirmDelete: Optional boolean - Whether or not to confirm deleting the // message. Defaults to true. -// pAttachments: Optional - An array of file attachment information returned by -// determineMsgAttachments() // // Return value: Boolean - Whether or not the message was deleted function DigDistMsgReader_PromptAndDeleteMessage(pOffset, pPromptLoc, pClearPromptRowAtFirstUse, - pPromptRowWidth, pConfirmDelete, pAttachments) + pPromptRowWidth, pConfirmDelete) { // Sanity checking if ((pOffset == null) || (typeof(pOffset) != "number")) @@ -10396,18 +10394,6 @@ function DigDistMsgReader_PromptAndDeleteMessage(pOffset, pPromptLoc, pClearProm msgWasDeleted = msgbase.remove_msg(false, msgHeader.number); if (msgWasDeleted) { - // If there are attachments, then delete them. - if (Object.prototype.toString.call(pAttachments) === "[object Array]") - { - if (pAttachments.length > 0) - { - for (var attachIdx = 0; attachIdx < pAttachments.length; ++attachIdx) - { - if (file_exists(pAttachments[attachIdx].fullyPathedFilename)) - file_remove(pAttachments[attachIdx].fullyPathedFilename); - } - } - } // Delete any vote response messages for this message var voteDelRetObj = deleteVoteMsgs(msgbase, msgHeader.number, msgHeader.id, (this.subBoardCode == "mail")); if (!voteDelRetObj.allVoteMsgsDeleted) @@ -10969,7 +10955,7 @@ function DigDistMsgReader_SelectMsgArea_Traditional() //console.crlf(); this.ListMsgGrps(grpSearchText); console.crlf(); - console.print("\1n\1b\1h� \1n\1cWhich, \1h/\1n\1c or \1hCTRL-F\1n\1c, \1hQ\1n\1cuit, or [\1h" + + console.print("\1n\1b\1h� \1n\1cWhich, \1h/\1n\1c or \1hCTRL-F\1n\1c, \1hQ\1n\1cuit, or [\1h" + +(msg_area.sub[this.subBoardCode].grp_index+1) + "\1n\1c]: \1h"); // Accept Q (quit), / or CTRL_F (Search) or a file library number selectedGrp = console.getkeys("Q/" + CTRL_F, msg_area.grp_list.length); @@ -11024,7 +11010,7 @@ function DigDistMsgReader_SelectMsgArea_Traditional() this.DisplayAreaChgHdr(); this.ListSubBoardsInMsgGroup(selectedGrp-1, defaultSubBoard-1, null, subSearchText); console.crlf(); - console.print("\1n\1b\1h� \1n\1cWhich, \1h/\1n\1c or \1hCTRL-F\1n\1c, \1hQ\1n\1cuit, or [\1h" + + console.print("\1n\1b\1h� \1n\1cWhich, \1h/\1n\1c or \1hCTRL-F\1n\1c, \1hQ\1n\1cuit, or [\1h" + defaultSubBoard + "\1n\1c]: \1h"); // Accept Q (quit), / or CTRL_F (Search) or a sub-board number selectedSubBoard = console.getkeys("Q/" + CTRL_F, msg_area.grp_list[selectedGrp - 1].sub_list.length); @@ -11524,7 +11510,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen) console.crlf(); console.print("\1n\1c\1hMessage area (sub-board) chooser"); console.crlf(); - console.print("\1k��������������������������������\1n"); + console.print("\1k��������������������������������\1n"); console.crlf(); console.print("\1cFirst, a listing of message groups is displayed. One can be chosen by typing"); console.crlf(); @@ -11536,7 +11522,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen) console.crlf(); console.print("Keyboard commands:"); console.crlf(); - console.print("\1k\1h�����������������\1n"); + console.print("\1k\1h�����������������\1n"); console.crlf(); console.print("\1n\1c\1h/\1n\1c or \1hCTRL-F\1n\1c: Find group/sub-board"); console.crlf(); @@ -11550,7 +11536,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen) console.crlf(); console.print("\1n\1cThe lightbar interface also allows up & down navigation through the lists:"); console.crlf(); - console.print("\1k\1h��������������������������������������������������������������������������"); + console.print("\1k\1h��������������������������������������������������������������������������"); console.crlf(); console.print("\1n\1c\1hUp\1n\1c/\1hdown arrow\1n\1c: Move the cursor up/down one line"); console.crlf(); @@ -11998,7 +11984,7 @@ function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly) // attachments: An array of the attached filenames (as strings) // errorMsg: An error message, if something bad happened function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDetermineAttachments, - pGetB64Data, pMsgBody, pMsgHasANSICodes) + pGetB64Data, pMsgBody) { var retObj = { msgText: "", @@ -12032,18 +12018,7 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter return retObj; } } - if (determineAttachments) - { - var msgInfo = determineMsgAttachments(pMsgHdr, msgBody, getB64Data); - retObj.msgText = msgInfo.msgText; - retObj.attachments = msgInfo.attachments; - } - else - { - retObj.msgText = msgBody; - retObj.msgText = word_wrap(msgBody, console.screen_columns - 1, true); - retObj.attachments = []; - } + retObj.msgText = word_wrap(msgBody, console.screen_columns - 1, true); var msgTextAltered = retObj.msgText; // Will alter the message text, but not yet // Only interpret @-codes if the user is reading personal email. There @@ -12062,7 +12037,6 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter // can mess up the display of the message, so remove enter characters // from the beginning of the message. var msgTextWithoutAttrs = strip_ctrl(msgTextAltered); - var first240Chars = msgTextWithoutAttrs.substr(0, 240); var fromToSearchStr = "By: " + pMsgHdr.from + " to " + pMsgHdr.to; var toFromSearchStr = "By: " + pMsgHdr.to + " to " + pMsgHdr.from; var fromToStrIdx = msgTextWithoutAttrs.indexOf(fromToSearchStr); @@ -12124,10 +12098,6 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter retObj.numNonSolidScrollBlocks = this.msgAreaHeight - retObj.numSolidScrollBlocks; retObj.solidBlockStartRow = this.msgAreaTop; - // Set the hasAttachments attribute of retObj. For Synchronet 3.17 and newer, - // the header might have an attachment attribute set, so we can use that. - retObj.hasAttachments = (msgHdrHasAttachmentFlag(pMsgHdr) || retObj.attachments.length > 0); - return retObj; } @@ -12967,112 +12937,39 @@ function DigDistMsgReader_GetGroupNameAndDesc() // Parameters: // pMsgHdr: The header object for the message // pFilename: The name of the file to write the message to -// pStripCtrl: Boolean - Whether or not to remove Synchronet control -// codes from the message lines -// pMsgLines: An array containing the message lines -// pAttachments: An array containing attachment information (as returned by determineMsgAttachments()) // // Return value: An object containing the following properties: // succeeded: Boolean - Whether or not the file was successfully written // errorMsg: String - On failure, will contain the reason it failed -function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename, pStripCtrl, pMsgLines, pAttachments) +function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename) { // Sanity checking + if (typeof(pMsgHdr) !== "object") + return({ succeeded: false, errorMsg: "Header object not given"}); if (typeof(pFilename) != "string") return({ succeeded: false, errorMsg: "Filename parameter not a string"}); if (pFilename.length == 0) return({ succeeded: false, errorMsg: "Empty filename given"}); - // If no message lines are passed in, then get the message lines now. - var msgLines = pMsgLines; - var attachments = pAttachments; - if ((pMsgLines == null) || (typeof(pMsgLines) != "object")) - { - if (typeof(pMsgHdr) == "object") - { - // Get the message text, interpret any @-codes in it, replace tabs with spaces - // to prevent weirdness when displaying the message lines, and word-wrap the - // text so that it looks good on the screen, - //GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDetermineAttachments, pGetB64Data) - //var msgInfo = this.GetMsgInfoForEnhancedReader(pMsgHdr, false, false, false); - var msgInfo = this.GetMsgInfoForEnhancedReader(pMsgHdr, true, true, true); - msgLines = msgInfo.messageLines; - if (msgInfo.hasOwnProperty("attachments")) - attachments = msgInfo.attachments; - } - else - return({ succeeded: false, errorMsg: "No message lines and null header object"}); - } - var retObj = { succeeded: true, errorMsg: "" }; - // If there are message attachments, then treat pFilename as a directory and - // create the directory for saving both the message text & attachments. - // Then, save the attachments to that directory. - var msgTextFilename = pFilename; - if ((attachments != null) && (attachments.length > 0)) + // Get the message text and save it + // Note: GetMsgInfoForEnhancedReader() can expand @-codes in the message, + // but for now we're saving the message basically as-is. + //var msgInfo = this.GetMsgInfoForEnhancedReader(pMsgHdr, false, false, false); + var msgbase = new MsgBase(this.subBoardCode); + if (msgbase.open()) { - if (file_isdir(pFilename)) - { - if (file_exists(pFilename)) - return({ succeeded: false, errorMsg: "Can't make directory: File with that name exists"}); - } - else - { - if (!mkdir(pFilename)) - return({ succeeded: false, errorMsg: "Failed to create directory"}); - } - - // The name of the file to save the message text will be called "messageText.txt" - // in the save directory. - var savePathWithTrailingSlash = backslash(pFilename); - msgTextFilename = savePathWithTrailingSlash + "messageText.txt"; - - // Save the attachments to the directory - var saveFileError = ""; - for (var attachIdx = 0; (attachIdx < attachments.length) && (saveFileError.length == 0); ++attachIdx) - { - var destFilename = savePathWithTrailingSlash + attachments[attachIdx].filename; - // If the file info has base64 data, then decode & save it to the directory. - // Otherwise, the file was probably uploaded to the user's mailbox in Synchronet, - // so copy the file to the save directory. - if (attachments[attachIdx].hasOwnProperty("B64Data")) - { - var attachedFile = new File(destFilename); - if (attachedFile.open("wb")) - { - attachedFile.base64 = true; - if (!attachedFile.write(attachments[attachIdx].B64Data)) - saveFileError = "\1n\1cFailed to save " + attachments[attachIdx].filename; - attachedFile.close(); - } - } - else - { - // There is no base64 data for the file, so it's probably in the - // user's mailbox in Synchronet, so copy it to the save directory. - if (file_exists(attachments[attachIdx].fullyPathedFilename)) - { - if (!file_copy(attachments[attachIdx].fullyPathedFilename, destFilename)) - saveFileError = "Failed to copy " + attachments[attachIdx].filename; - } - else - saveFileError = "File " + attachments[attachIdx].fullyPathedAttachmentFilename + " doesn't exist"; - } - } - if (saveFileError.length > 0) - return({ succeeded: false, errorMsg: saveFileError }); - } + msgBody = msgbase.get_msg_body(false, pMsgHdr.number, false, false, true, true); + msgbase.close(); - var messageSaveFile = new File(msgTextFilename); - if (messageSaveFile.open("w")) - { - // Write some header information to the file - if (typeof(pMsgHdr) == "object") + var messageSaveFile = new File(pFilename); + if (messageSaveFile.open("w")) { + // Write some header information to the file if (pMsgHdr.hasOwnProperty("from")) messageSaveFile.writeln("From: " + pMsgHdr.from); if (pMsgHdr.hasOwnProperty("to")) @@ -13094,24 +12991,32 @@ function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename, pStripCtrl, pMsgLine if (pMsgHdr.hasOwnProperty("reply_id")) messageSaveFile.writeln("Reply ID: " + pMsgHdr.reply_id); messageSaveFile.writeln("==============================="); - } - // Write the message body to the file - if (pStripCtrl) - { - for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx) - messageSaveFile.writeln(strip_ctrl(msgLines[msgLineIdx])); + + // If the message body has ANSI, then use the Graphic object to strip it + // of any cursor movement codes + var msgHasANSICodes = msgBody.indexOf("\x1b[") >= 0; + if (msgHasANSICodes) + { + var graphic = new Graphic(this.msgAreaWidth, this.msgAreaHeight); + graphic.auto_extend = true; + graphic.ANSI = ansiterm.expand_ctrl_a(msgBody); + msgBody = syncAttrCodesToANSI(graphic.MSG); + } + + // Write the message body to the file + messageSaveFile.write(msgBody); + messageSaveFile.close(); } else { - for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx) - messageSaveFile.writeln(msgLines[msgLineIdx]); + retObj.succeeded = false; + retObj.errorMsg = "Failed to open the file for writing"; } - messageSaveFile.close(); } else { retObj.succeeded = false; - retObj.errorMsg = "Unable to open the message file for writing"; + retObj.errorMsg = "Unable to open the messagebase"; } return retObj; @@ -14740,7 +14645,7 @@ function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor) var solidLine = ""; var textLength = console.strlen(pText); for (var i = 0; i < textLength; ++i) - solidLine += "�"; + solidLine += HORIZONTAL_SINGLE; console.center(lineColor + solidLine); } else @@ -14750,7 +14655,7 @@ function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor) console.print(lineColor); var textLength = console.strlen(pText); for (var i = 0; i < textLength; ++i) - console.print("�"); + console.print(HORIZONTAL_SINGLE); console.crlf(); } } @@ -16559,423 +16464,6 @@ function getSubBoardCodeFromNum(pSubBoardNum) 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. -// -// 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 - // inbox directory, then add its filename to the list of attached - // filenames that will be returned - var fullyPathedAttachmentFilename = userInboxDir + pMsgHdr.subject; - if (file_exists(fullyPathedAttachmentFilename)) - { - retObj.attachments.push({ filename: pMsgHdr.subject, - fullyPathedFilename: fullyPathedAttachmentFilename }); - } - - // The message to prepend onto the message text if the message has attachments - var msgHasAttachmentsTxt = "\1n\1g\1h- This message contains one or more attachments. Press CTRL-A to download.\1n\r\n" - + "\1n\1g\1h--------------------------------------------------------------------------\1n\r\n"; - - // Sanity checking - if (typeof(pMsgText) != "string") - { - // If there are any attachments, prepend the message text with a message - // saying that the message contains attachments. - if (msgHdrHasAttachmentFlag(pMsgHdr) || retObj.attachments.length > 0) - retObj.msgText = msgHasAttachmentsTxt + retObj.msgText; - return retObj; - } - - // If the message text doesn't include a line starting with -- and a - // line starting with "Content-type:", then then just return the - // the same text in retObj. - //var hasMultiParts = /--\S+\s*Content-Type:/.test(pMsgText); - //var hasMultiParts = ((dashDashIdx > -1) && (/Content-Type/.test(pMsgText))); - var dashDashIdx = pMsgText.indexOf("--"); - var hasMultiParts = ((dashDashIdx > -1) && (pMsgText.indexOf("Content-Type", dashDashIdx+1) > dashDashIdx)); - if (!hasMultiParts) - { - //retObj.msgText = pMsgText; - // If there are any attachments, prepend the message text with a message - // saying that the message contains attachments. - if (msgHdrHasAttachmentFlag(pMsgHdr) || retObj.attachments.length > 0) - retObj.msgText = msgHasAttachmentsTxt + pMsgText; - else - retObj.msgText = pMsgText; - return retObj; - } - - var getB64Data = true; - if (typeof(pGetB64Data) == "boolean") - getB64Data = pGetB64Data; - - // Look in the message text for a line starting with -- followed by some characters, - // then whitespace - var sepMatches = /--\S+\s/.exec(pMsgText); - var msgSeparator = sepMatches[0]; - // If the last character in msgSeparator is a whitepsace character, then - // remove it. - if (/\s/.test(msgSeparator.substr(msgSeparator.length-1, 1))) - msgSeparator = msgSeparator.substr(0, msgSeparator.length-1); - var contentType = ""; // The content type of the current section - var lastContentType = ""; // The content type of the last section - var contentEncodingType = ""; - var sepIdx = 0; - var lastSepIdx = -1; - var lastContentTypeIdx = -1; - var lastContentEncodingTypeIdx = -1; - var startIdx = 0; - var gotMessageText = false; // In case the message has both text/plain & text/html - while ((sepIdx = pMsgText.indexOf(msgSeparator, startIdx)) >= 0) - { - var contentEncodingTypeIdx = -1; - // Look for a "Content-Type:" from the starting index - var contentTypeIdx = pMsgText.indexOf("Content-Type: ", startIdx+msgSeparator.length); - if (contentTypeIdx > -1) - { - // Extract the content-type string up to a newline or 15 characters - // if there's no newline - var newlineIdx = pMsgText.indexOf("\n", contentTypeIdx+14); - contentType = pMsgText.substring(contentTypeIdx+14, newlineIdx > -1 ? newlineIdx : contentTypeIdx+29); - // If the last character is whitespace (i.e., a newline), then remove it. - if (/\s/.test(contentType.substr(contentType.length-1, 1))) - contentType = contentType.substr(0, contentType.length-1); - - // Update the start index for looking for the next message separator string - // - This should be after the "Content-type:" value. - startIdx = contentTypeIdx + contentType.length; - } - else - { - // No "Content-Type:" string was found - // Update the start index for looking for the next message separator string - startIdx = sepIdx + msgSeparator.length; - } - - if ((lastSepIdx > -1) && (lastContentTypeIdx > -1)) - { - // msgTextSearchStartIdx stores the index of where to start looking - // for the message text. It could be lastContentTypeIdx, or it could - // be the content encoding type index if the "Content encoding type" - // text is found for the current message part. - var msgTextSearchStartIdx = lastContentTypeIdx; - - // Look for "Content-Transfer-Encoding:" right after the content type - // and extract the content encoding type string - contentEncodingTypeIdx = pMsgText.indexOf("Content-Transfer-Encoding:", lastContentTypeIdx); - // If "Content-Transfer-Encoding:" wasn't found after the content type, - // then look just before the content type, but after the last separator - // string. - if (contentEncodingTypeIdx == -1) - contentEncodingTypeIdx = pMsgText.indexOf("Content-Transfer-Encoding:", lastSepIdx); - // If the next "Content-Encoding-Type" is after the current section, - // then this section doesn't have a content type, so blank it out. - if (contentEncodingTypeIdx > sepIdx) - { - contentEncodingTypeIdx = -1; - contentEncodingType = ""; - } - else - { - msgTextSearchStartIdx = contentEncodingTypeIdx; - // Extract the content encoding type - var newlineIdx = pMsgText.indexOf("\n", contentEncodingTypeIdx+26); - contentEncodingType = pMsgText.substring(contentEncodingTypeIdx, newlineIdx); - // If the last character is whitespace (i.e., a newline), then remove it. - if (/\s/.test(contentEncodingType.substr(contentEncodingType.length-1, 1))) - contentEncodingType = contentEncodingType.substr(0, contentEncodingType.length-1); - // Update startIdx based on the length of the "content encoding type" string - startIdx += contentEncodingType.length; - // Now, store just the content type in contentEncodingType (i.e., "base64"). - contentEncodingType = contentEncodingType.substr(27).toLowerCase(); - } - - // Look for the message text - var contentTypeSearchIdx = -1; - //if ((contentTypeSearchIdx = lastContentType.indexOf("text/plain")) > -1) - if ((contentTypeSearchIdx = lastContentType.indexOf("text/")) > -1) - { - if (!gotMessageText) - { - var newlineIdx = pMsgText.indexOf("\n", msgTextSearchStartIdx); // Used to be lastContentTypeIdx - if (newlineIdx > -1) - retObj.msgText = pMsgText.substring(newlineIdx+1, sepIdx); - else - retObj.msgText = pMsgText.substring(lastSepIdx, sepIdx); - gotMessageText = true; - } - } - else - { - // Look for a filename in the content-type specification - // If it doesn't contain the filename, then we'll have to look on the - // next line for the filename. - var attachmentFilename = ""; - var matches = /name="(.*)"/.exec(lastContentType); - if (matches != null) - { - if (matches.length >= 2) - attachmentFilename = matches[1]; - } - if (attachmentFilename.length == 0) - { - // Look for the filename on the next line - var newlineIdx = pMsgText.indexOf("\n", lastContentTypeIdx); - if (newlineIdx > -1) - { - // 1000 chars should be enough - var nextLine = pMsgText.substr(newlineIdx+1, 1000); - var matches = /name="(.*)"/.exec(nextLine); - if (matches != null) - { - if (matches.length >= 2) - attachmentFilename = matches[1]; - } - } - } - // If we got a filename, then extract the base64-encoded file data. - if (attachmentFilename.length > 0) - { - var fileInfo = { filename: attachmentFilename, - fullyPathedFilename: gFileAttachDir + attachmentFilename }; - // Only extract the base64-encoded data if getB64Data is true - // and the current section's encoding type was actually specified - // as base64. - if (getB64Data && (contentEncodingType == "base64")) - { - // There should be 2 newlines before the base64 data - // TODO: There's a bug here where sometimes it isn't getting - // the correct section for base64 data. The code later that - // looks for an existing filename in the attachments is sort - // of a way around that though. - var lineSeparator = ascii(13) + ascii(10); - var twoNLIdx = pMsgText.indexOf(lineSeparator + lineSeparator, lastContentTypeIdx); - if (twoNLIdx > -1) - { - // Get the base64-encoded data for the current file from the message, - // and remove the newline & carriage return characters and whitespace - // from it. - fileInfo.B64Data = pMsgText.substring(twoNLIdx+2, sepIdx); - fileInfo.B64Data = fileInfo.B64Data.replace(new RegExp(ascii(13) + "|" + ascii(10), "g"), "").trim(); - - // Update the start index for looking for the next message separator - // string - startIdx = twoNLIdx; - } - } - // Add the file attachment information to the return object. - // If there is already an entry with the filename, then replace - // that one; otherwise, append it. - var fileExists = false; - for (var fileIdx = 0; (fileIdx < retObj.attachments.length) && !fileExists; ++fileIdx) - { - if (retObj.attachments[fileIdx].filename == fileInfo.filename) - { - fileExists = true; - if (getB64Data && fileInfo.hasOwnProperty("B64Data")) - retObj.attachments[fileIdx].B64Data = fileInfo.B64Data; - } - } - if (!fileExists) - retObj.attachments.push(fileInfo); - } - } - } - - lastContentType = contentType; - lastSepIdx = sepIdx; - lastContentTypeIdx = contentTypeIdx; - lastContentEncodingTypeIdx = contentEncodingTypeIdx; - - // The end of the message will have the message separator string with - // "--" appended to it. If we've reached that point, then we know we - // can stop. - if (pMsgText.substr(sepIdx, msgSeparator.length+2) == msgSeparator + "--") - break; - } - - // If there are any attachments, prepend the message text with a message - // saying that the message contains attachments. - if (msgHdrHasAttachmentFlag(pMsgHdr) || retObj.attachments.length > 0) - retObj.msgText = msgHasAttachmentsTxt + retObj.msgText; - - // If there are attachments and the message text is more than will fit on the - // screen (75% of the console height to account for the ), then append text at - // the end to say there are attachments. - var maxNumCharsOnScreen = 79 * Math.floor(console.screen_rows * 0.75); - if ((msgHdrHasAttachmentFlag(pMsgHdr) || (retObj.attachments.length > 0)) && (retObj.msgText.length > maxNumCharsOnScreen)) - { - retObj.msgText += "\1n\r\n\1g\1h--------------------------------------------------------------------------\1n\r\n"; - retObj.msgText += "\1g\1h- This message contains one or more attachments. Press CTRL-A to download.\1n"; - } - - return retObj; -} - -// Allows the user to download files that were attached to a message. Takes an -// array of file information given by determineMsgAttachments(). -// -// Parameters: -// pAttachments: An array of file attachment information returned by -// determineMsgAttachments() -// 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 -function sendAttachedFiles(pAttachments) -{ - if (Object.prototype.toString.call(pAttachments) !== "[object Array]") - return; - - // Synchronet doesn't allow batch downloading of files that aren't in the - // file database, so we have to send each one at a time. :( - - // Get the file download confirmation text from text.dat - // 662: "\r\nDownload attached file: \1w%s\1b (%s bytes)" - var DLPromptTextOrig = bbs.text(DownloadAttachedFileQ); - - var anyErrors = false; - // For each item in the array, allow the user to download the attachment. - var fileNum = 1; - pAttachments.forEach(function(fileInfo) { - console.print("\1n"); - console.crlf(); - - // If the file doesn't exist and base64 data is available for the file, - // then save it to the temporary attachments directory. - // Note that we need to save the file first in order to get the file's size - // to display in the confirmation prompt to download the file. - // errorMsg will contain an error if something went wrong creating the - // temporary attachments directory, etc. - var errorMsg = ""; - var savedFileToBBS = false; // If we base64-decoded the file, we'll want to delete it after it's sent. - if (!file_exists(fileInfo.fullyPathedFilename)) - { - if (fileInfo.hasOwnProperty("B64Data")) - { - // If the temporary attachments directory doesn't exist, - // then create it. - var attachmentDirExists = true; // Will be false if it couldn't be created - if (!file_isdir(gFileAttachDir)) - { - // If it's a file rather than a directory, then remove it - // before creating it as a directory. - if (file_exists(gFileAttachDir)) - file_remove(gFileAttachDir); - attachmentDirExists = mkdir(gFileAttachDir); - } - - // Write the file to the BBS machine - if (attachmentDirExists) - { - var attachedFile = new File(fileInfo.fullyPathedFilename); - if (attachedFile.open("wb")) - { - attachedFile.base64 = true; - if (!attachedFile.write(fileInfo.B64Data)) - errorMsg = "\1h\1g* \1n\1cCan't send " + quoteStrWithSpaces(fileInfo.filename) + " - Failed to save it to the BBS!"; - attachedFile.close(); - // Saved the file to the temporary attachments directory (even if it failed - // to write, there's probably still an empty file there). - savedFileToBBS = true; - } - else - errorMsg = "\1h\1g* \1n\1cFailed to save " + quoteStrWithSpaces(fileInfo.filename) + "!"; - } - else - errorMsg = "\1h\1g* \1n\1cFailed to create temporary directory on the BBS!"; - } - else - errorMsg = "\1h\1g* \1n\1cCan't send " + quoteStrWithSpaces(fileInfo.filename) + " because it doesn't exist or wasn't encoded in a known format"; - } - // If we can send the file, then prompt the user for confirmation, and if they - // answer yes, then send it. - // Note that we needed to save the file first in order to get the file's size - // to display in the confirmation prompt. - if (errorMsg.length == 0) - { - // Print the file number - console.print("\1n\1cFile \1g" + fileNum + "\1c of \1g" + pAttachments.length + "\1n"); - console.crlf(); - // Prompt the user to confirm whether they want to download the - // file. If the user chooses yes, then send it. - var fileSize = Math.round(file_size(fileInfo.fullyPathedFilename)); - var DLPromptText = format(DLPromptTextOrig, fileInfo.filename, fileSize); - if (console.yesno(DLPromptText)) - bbs.send_file(fileInfo.fullyPathedFilename); - - // If the file was base64-decoded and saved to the BBS machine (as opposed to - // being in the user's mailbox), then delete the file. - if (savedFileToBBS) - file_remove(fileInfo.fullyPathedFilename); - } - else - { - // There was an error creating the temporary attachment directory, etc., so - // display the error and pause to let the user read it. - //console.print(errorMsg); - //console.putmsg(word_wrap(errorMsg, console.screen_columns-1, errorMsg.length, false)); - //console.crlf(); - var errMsgLines = lfexpand(word_wrap(errorMsg, console.screen_columns-1, errorMsg.length, false)).split("\r\n"); - console.print("\1n"); - for (var errorIdx = 0; errorIdx < errMsgLines.length; ++errorIdx) - { - console.print(errMsgLines[errorIdx]); - console.crlf(); - } - console.pause(); - } - - ++fileNum; - }); - - // If the temporary attachments directory exists, then delete it. - if (file_exists(gFileAttachDir)) - deltree(gFileAttachDir); -} - // This function recursively removes a directory and all of its contents. Returns // whether or not the directory was removed. // @@ -18602,17 +18090,6 @@ function strWithToUserColor(pStr, pToUserColor) */ } -// Returns whether a string has any Synchronet attribute codes -// -// Parameters: -// pStr: the string to check -// -// Return value: Boolean - Whether or not the string has any Synchronet attribute codes -function hasSyncAttrCodes(pStr) -{ - return (pStr.search(/\1[krgybmcwhifn\-_01234567]/i) > -1); -} - // Gets the value of the user's current scan_ptr in a sub-board, or if it's // 0xffffffff, returns the message number of the last readable message in // the sub-board (this is the message number, not the index). @@ -18702,7 +18179,6 @@ function allowUserToDownloadMessage_NewInterface(pMsgHdr, pSubCode) console.creturn(); bbs.download_msg_attachments(msgHdrForDownloading); msgBase.close(); - delete msgBase; // Free some memory? } } diff --git a/xtrn/DDMsgReader/readme.txt b/xtrn/DDMsgReader/readme.txt index 77df3221afb006b21406391bc61abfaa876a0aaf..930137772bceb4ef911bf62bb9877ed95c4309e0 100644 --- a/xtrn/DDMsgReader/readme.txt +++ b/xtrn/DDMsgReader/readme.txt @@ -1,6 +1,6 @@ Digital Distortion Message Reader - Version 1.48 - Release date: 2022-06-12 + Version 1.49 + Release date: 2022-06-13 by diff --git a/xtrn/DDMsgReader/revision_history.txt b/xtrn/DDMsgReader/revision_history.txt index fe3a5377f9b25c0ba18a5ffb72ed933a17b929dd..98bc97b544a0fe033168c1504ced81548c980746 100644 --- a/xtrn/DDMsgReader/revision_history.txt +++ b/xtrn/DDMsgReader/revision_history.txt @@ -5,6 +5,10 @@ Revision History (change log) ============================= Version Date Description ------- ---- ----------- +1.49 2022-06-13 Refactor: Simplified saving a message to BBS machine for + sysop (as-is, less processing); removed attachment stuff + for pre-Synchronet 3.17; moved hasSyncAttrCodes() to + attr_conv.js because that's where it needs to be. 1.48 2022-06-12 Improved display of ANSI messages 1.47a 2022-03-23 Internal change: Now calls bbs.edit_msg() for editing an existing message (for Synchronet 3.18 and up).