Skip to content
Snippets Groups Projects
Commit 5ab79049 authored by Rob Swindell's avatar Rob Swindell :speech_balloon:
Browse files

Merge branch 'dd_msg_reader_attachents_and_save_refactor' into 'master'

DDMsgReader refactor for sysop save to BBS machine & attachments.

See merge request !187
parents b19466b5 dfd1e302
No related branches found
No related tags found
2 merge requests!463MRC mods by Codefenix (2024-10-20),!187DDMsgReader refactor for sysop save to BBS machine & attachments.
...@@ -1136,3 +1136,14 @@ function syncAttrCodesToANSI(pText) ...@@ -1136,3 +1136,14 @@ function syncAttrCodesToANSI(pText)
else else
return pText; // No Synchronet-style attribute codes found, so just return the text. 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
...@@ -27,8 +27,13 @@ ...@@ -27,8 +27,13 @@
* 2022-03-23 Eric Oulashin Version 1.47a * 2022-03-23 Eric Oulashin Version 1.47a
* Now calls bbs.edit_msg() to edit an existing message (if * Now calls bbs.edit_msg() to edit an existing message (if
* that function exists - It was added in Synchronet 3.18). * 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 * 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 // 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'); ...@@ -132,8 +137,8 @@ var ansiterm = require("ansiterm_lib.js", 'expand_ctrl_a');
   
   
// Reader version information // Reader version information
var READER_VERSION = "1.48"; var READER_VERSION = "1.49";
var READER_DATE = "2022-06-12"; var READER_DATE = "2022-06-13";
   
// Keyboard key codes for displaying on the screen // Keyboard key codes for displaying on the screen
var UP_ARROW = ascii(24); var UP_ARROW = ascii(24);
...@@ -4516,6 +4521,12 @@ function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea) ...@@ -4516,6 +4521,12 @@ function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea)
graphic.width = this.msgAreaWidth; graphic.width = this.msgAreaWidth;
messageText = graphic.MSG; 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); var useScrollingInterface = this.scrollingReaderInterface && console.term_supports(USER_ANSI);
// If we switch to the non-scrolling interface here, then the calling method should // If we switch to the non-scrolling interface here, then the calling method should
// refresh the enhanced reader help line on the screen. // refresh the enhanced reader help line on the screen.
...@@ -4529,9 +4540,7 @@ function DigDistMsgReader_ReadMessageEnhanced(pOffset, pAllowChgArea) ...@@ -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 the message has ANSI codes, remove any ANSI clear screen codes from the message text
if (msgHasANSICodes) if (msgHasANSICodes)
{
messageText = messageText.replace(/\u001b\[[012]J/gi, ""); messageText = messageText.replace(/\u001b\[[012]J/gi, "");
}
retObj = this.ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgArea, messageText, msgHasANSICodes, pOffset); retObj = this.ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgArea, messageText, msgHasANSICodes, pOffset);
} }
else else
...@@ -4580,7 +4589,6 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -4580,7 +4589,6 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
var msgInfo = this.GetMsgInfoForEnhancedReader(msgHeader, true, true, true, messageText, msgHasANSICodes); var msgInfo = this.GetMsgInfoForEnhancedReader(msgHeader, true, true, true, messageText, msgHasANSICodes);
   
var topMsgLineIdxForLastPage = msgInfo.topMsgLineIdxForLastPage; var topMsgLineIdxForLastPage = msgInfo.topMsgLineIdxForLastPage;
var msgFractionShown = msgInfo.msgFractionShown;
var numSolidScrollBlocks = msgInfo.numSolidScrollBlocks; var numSolidScrollBlocks = msgInfo.numSolidScrollBlocks;
var numNonSolidScrollBlocks = msgInfo.numNonSolidScrollBlocks; var numNonSolidScrollBlocks = msgInfo.numNonSolidScrollBlocks;
var solidBlockStartRow = msgInfo.solidBlockStartRow; var solidBlockStartRow = msgInfo.solidBlockStartRow;
...@@ -4614,6 +4622,8 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -4614,6 +4622,8 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
solidBlockLastStartRow = solidBlockStartRow; solidBlockLastStartRow = solidBlockStartRow;
console.gotoxy(1, console.screen_rows); console.gotoxy(1, console.screen_rows);
} }
var msgHasAttachments = msgHdrHasAttachmentFlag(msgHeader);
// User input loop // User input loop
var continueOn = true; var continueOn = true;
while (continueOn) while (continueOn)
...@@ -4664,8 +4674,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -4664,8 +4674,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
// calling method will go to the next message/sub-board. // calling method will go to the next message/sub-board.
// Otherwise (if the message was not deleted), refresh the // Otherwise (if the message was not deleted), refresh the
// last 2 lines of the message on the screen. // last 2 lines of the message on the screen.
var msgWasDeleted = this.PromptAndDeleteMessage(pOffset, promptPos, true, this.msgAreaWidth, var msgWasDeleted = this.PromptAndDeleteMessage(pOffset, promptPos, true, this.msgAreaWidth, true);
true, msgInfo.attachments);
if (msgWasDeleted) if (msgWasDeleted)
{ {
var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset); var msgSearchObj = this.LookForNextOrPriorNonDeletedMsg(pOffset);
...@@ -4788,7 +4797,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -4788,7 +4797,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
writeMessage = false; // Don't write the current message again writeMessage = false; // Don't write the current message again
break; break;
case this.enhReaderKeys.showHelp: // Show the help screen 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 // If the enhanced message header width is less than the console
// width, then clear the screen to remove anything left on the // width, then clear the screen to remove anything left on the
// screen from the help screen. // screen from the help screen.
...@@ -4819,7 +4828,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -4819,7 +4828,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true); var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true);
msgbase.close(); msgbase.close();
// Let the user reply to the message. // Let the user reply to the message.
var replyRetObj = this.ReplyToMsg(extdMsgHdr, msgInfo.msgText, privateReply, pOffset); var replyRetObj = this.ReplyToMsg(extdMsgHdr, messageText, privateReply, pOffset);
retObj.userReplied = replyRetObj.postSucceeded; retObj.userReplied = replyRetObj.postSucceeded;
//retObj.msgNotReadable = replyRetObj.msgWasDeleted; //retObj.msgNotReadable = replyRetObj.msgWasDeleted;
var msgWasDeleted = replyRetObj.msgWasDeleted; var msgWasDeleted = replyRetObj.msgWasDeleted;
...@@ -5224,21 +5233,12 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -5224,21 +5233,12 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
writeMessage = false; // No need to refresh the message writeMessage = false; // No need to refresh the message
break; break;
case this.enhReaderKeys.downloadAttachments: // Download attachments case this.enhReaderKeys.downloadAttachments: // Download attachments
if (msgInfo.hasAttachments) if (msgHasAttachments)
{ {
console.print("\1n"); console.print("\1n");
console.gotoxy(1, console.screen_rows); console.gotoxy(1, console.screen_rows);
console.crlf(); 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); 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);
}
   
// Refresh things on the screen // Refresh things on the screen
console.clear("\1n"); console.clear("\1n");
...@@ -5267,13 +5267,18 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA ...@@ -5267,13 +5267,18 @@ function DigDistMsgReader_ReadMessageEnhanced_Scrollable(msgHeader, allowChgMsgA
console.print("\1n"); console.print("\1n");
if (filename.length > 0) if (filename.length > 0)
{ {
//var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true, msgInfo.messageLines); var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename);
var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true);
console.gotoxy(promptPos); console.gotoxy(promptPos);
console.cleartoeol("\1n"); console.cleartoeol("\1n");
console.gotoxy(promptPos); console.gotoxy(promptPos);
if (saveMsgRetObj.succeeded) 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 else
console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n"); console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n");
mswait(ERROR_PAUSE_WAIT_MS); mswait(ERROR_PAUSE_WAIT_MS);
...@@ -5652,15 +5657,15 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg ...@@ -5652,15 +5657,15 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
refreshEnhancedRdrHelpLine: false refreshEnhancedRdrHelpLine: false
}; };
   
// Separate the message text from any attachments in the message. var msgHasAttachments = msgHdrHasAttachmentFlag(msgHeader);
var msgAndAttachmentInfo = determineMsgAttachments(msgHeader, messageText, true);
// Only interpret @-codes if the user is reading personal email. There // 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 // 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 // script, etc., and I don't want users on message networks to do anything
// malicious to users on other BBSes. // malicious to users on other BBSes.
if (this.readingPersonalEmail) if (this.readingPersonalEmail)
msgAndAttachmentInfo.msgText = replaceAtCodesInStr(msgAndAttachmentInfo.msgText); // Or this.ParseMsgAtCodes(msgAndAttachmentInfo.msgText, msgHeader) to replace only some @ codes messageText = replaceAtCodesInStr(messageText); // Or this.ParseMsgAtCodes(messageText, msgHeader) to replace only some @ codes
var msgTextWrapped = word_wrap(msgAndAttachmentInfo.msgText, console.screen_columns-1); var msgTextWrapped = (msgHasANSICodes ? messageText : word_wrap(messageText, console.screen_columns-1));
   
// Generate the key help text // Generate the key help text
var keyHelpText = "\1n\1c\1h#\1n\1b, \1c\1hLeft\1n\1b, \1c\1hRight\1n\1b, "; 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 ...@@ -5724,8 +5729,6 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
// calling method will go to the next message/sub-board. // calling method will go to the next message/sub-board.
// Otherwise (if the message was not deleted), refresh the // Otherwise (if the message was not deleted), refresh the
// last 2 lines of the message on the screen. // 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); var msgWasDeleted = this.PromptAndDeleteMessage(pOffset);
if (msgWasDeleted) if (msgWasDeleted)
{ {
...@@ -5802,7 +5805,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg ...@@ -5802,7 +5805,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
console.crlf(); console.crlf();
console.crlf(); console.crlf();
} }
this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgAndAttachmentInfo.hasAttachments); this.DisplayEnhancedReaderHelp(allowChgMsgArea, msgHasAttachments);
if (!console.term_supports(USER_ANSI)) if (!console.term_supports(USER_ANSI))
{ {
console.crlf(); console.crlf();
...@@ -5832,7 +5835,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg ...@@ -5832,7 +5835,7 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true); var extdMsgHdr = msgbase.get_msg_header(false, msgHeader.number, true);
msgbase.close(); msgbase.close();
// Let the user reply to the message // 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.userReplied = replyRetObj.postSucceeded;
//retObj.msgNotReadable = replyRetObj.msgWasDeleted; //retObj.msgNotReadable = replyRetObj.msgWasDeleted;
var msgWasDeleted = replyRetObj.msgWasDeleted; var msgWasDeleted = replyRetObj.msgWasDeleted;
...@@ -6171,20 +6174,12 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg ...@@ -6171,20 +6174,12 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
} }
break; break;
case this.enhReaderKeys.downloadAttachments: // Download attachments case this.enhReaderKeys.downloadAttachments: // Download attachments
if (msgAndAttachmentInfo.hasAttachments) if (msgHasAttachments)
{ {
console.print("\1n"); console.print("\1n");
console.crlf(); console.crlf();
console.print("\1c- Download Attached Files -\1n"); 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); allowUserToDownloadMessage_NewInterface(msgHeader, this.subBoardCode);
else
{
// Note: sendAttachedFiles() will output a CRLF at the beginning.
sendAttachedFiles(msgAndAttachmentInfo.attachments);
}
   
// Ensure the message is refreshed on the screen // Ensure the message is refreshed on the screen
writeMessage = true; writeMessage = true;
...@@ -6209,9 +6204,14 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg ...@@ -6209,9 +6204,14 @@ function DigDistMsgReader_ReadMessageEnhanced_Traditional(msgHeader, allowChgMsg
console.crlf(); console.crlf();
if (filename.length > 0) if (filename.length > 0)
{ {
var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename, true); var saveMsgRetObj = this.SaveMsgToFile(msgHeader, filename);
if (saveMsgRetObj.succeeded) if (saveMsgRetObj.succeeded)
{
console.print("\1n\1cThe message has been saved.\1n"); console.print("\1n\1cThe message has been saved.\1n");
if (msgHdrHasAttachmentFlag(msgHeader))
console.print(" Attachments not saved.");
console.print("\1n");
}
else else
console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n"); console.print("\1n\1y\1hFailed: " + saveMsgRetObj.errorMsg + "\1n");
mswait(ERROR_PAUSE_WAIT_MS); mswait(ERROR_PAUSE_WAIT_MS);
...@@ -8212,17 +8212,17 @@ function DigDistMsgReader_DisplaySyncMsgHeader(pMsgHdr) ...@@ -8212,17 +8212,17 @@ function DigDistMsgReader_DisplaySyncMsgHeader(pMsgHdr)
// Generate a string describing the message attributes, then output the default // Generate a string describing the message attributes, then output the default
// header. // header.
var allMsgAttrStr = makeAllMsgAttrStr(pMsgHdr); var allMsgAttrStr = makeAllMsgAttrStr(pMsgHdr);
console.print("\1n\1w۲"); console.print("\1n\1w���������������������������������������������������������������������������۲��");
console.crlf(); 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.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.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.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.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(); console.crlf();
} }
} }
...@@ -10308,12 +10308,10 @@ function DigDistMsgReader_EnhReaderPromptYesNo(pQuestion, pMessageLines, pTopLin ...@@ -10308,12 +10308,10 @@ function DigDistMsgReader_EnhReaderPromptYesNo(pQuestion, pMessageLines, pTopLin
// pPromptRowWidth: Optional - The width of the prompt row (if pProptLoc is valid) // pPromptRowWidth: Optional - The width of the prompt row (if pProptLoc is valid)
// pConfirmDelete: Optional boolean - Whether or not to confirm deleting the // pConfirmDelete: Optional boolean - Whether or not to confirm deleting the
// message. Defaults to true. // 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 // Return value: Boolean - Whether or not the message was deleted
function DigDistMsgReader_PromptAndDeleteMessage(pOffset, pPromptLoc, pClearPromptRowAtFirstUse, function DigDistMsgReader_PromptAndDeleteMessage(pOffset, pPromptLoc, pClearPromptRowAtFirstUse,
pPromptRowWidth, pConfirmDelete, pAttachments) pPromptRowWidth, pConfirmDelete)
{ {
// Sanity checking // Sanity checking
if ((pOffset == null) || (typeof(pOffset) != "number")) if ((pOffset == null) || (typeof(pOffset) != "number"))
...@@ -10396,18 +10394,6 @@ function DigDistMsgReader_PromptAndDeleteMessage(pOffset, pPromptLoc, pClearProm ...@@ -10396,18 +10394,6 @@ function DigDistMsgReader_PromptAndDeleteMessage(pOffset, pPromptLoc, pClearProm
msgWasDeleted = msgbase.remove_msg(false, msgHeader.number); msgWasDeleted = msgbase.remove_msg(false, msgHeader.number);
if (msgWasDeleted) 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 // Delete any vote response messages for this message
var voteDelRetObj = deleteVoteMsgs(msgbase, msgHeader.number, msgHeader.id, (this.subBoardCode == "mail")); var voteDelRetObj = deleteVoteMsgs(msgbase, msgHeader.number, msgHeader.id, (this.subBoardCode == "mail"));
if (!voteDelRetObj.allVoteMsgsDeleted) if (!voteDelRetObj.allVoteMsgsDeleted)
...@@ -10969,7 +10955,7 @@ function DigDistMsgReader_SelectMsgArea_Traditional() ...@@ -10969,7 +10955,7 @@ function DigDistMsgReader_SelectMsgArea_Traditional()
//console.crlf(); //console.crlf();
this.ListMsgGrps(grpSearchText); this.ListMsgGrps(grpSearchText);
console.crlf(); 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"); +(msg_area.sub[this.subBoardCode].grp_index+1) + "\1n\1c]: \1h");
// Accept Q (quit), / or CTRL_F (Search) or a file library number // Accept Q (quit), / or CTRL_F (Search) or a file library number
selectedGrp = console.getkeys("Q/" + CTRL_F, msg_area.grp_list.length); selectedGrp = console.getkeys("Q/" + CTRL_F, msg_area.grp_list.length);
...@@ -11024,7 +11010,7 @@ function DigDistMsgReader_SelectMsgArea_Traditional() ...@@ -11024,7 +11010,7 @@ function DigDistMsgReader_SelectMsgArea_Traditional()
this.DisplayAreaChgHdr(); this.DisplayAreaChgHdr();
this.ListSubBoardsInMsgGroup(selectedGrp-1, defaultSubBoard-1, null, subSearchText); this.ListSubBoardsInMsgGroup(selectedGrp-1, defaultSubBoard-1, null, subSearchText);
console.crlf(); 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"); defaultSubBoard + "\1n\1c]: \1h");
// Accept Q (quit), / or CTRL_F (Search) or a sub-board number // 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); selectedSubBoard = console.getkeys("Q/" + CTRL_F, msg_area.grp_list[selectedGrp - 1].sub_list.length);
...@@ -11524,7 +11510,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen) ...@@ -11524,7 +11510,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen)
console.crlf(); console.crlf();
console.print("\1n\1c\1hMessage area (sub-board) chooser"); console.print("\1n\1c\1hMessage area (sub-board) chooser");
console.crlf(); console.crlf();
console.print("\1k\1n"); console.print("\1k��������������������������������\1n");
console.crlf(); console.crlf();
console.print("\1cFirst, a listing of message groups is displayed. One can be chosen by typing"); console.print("\1cFirst, a listing of message groups is displayed. One can be chosen by typing");
console.crlf(); console.crlf();
...@@ -11536,7 +11522,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen) ...@@ -11536,7 +11522,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen)
console.crlf(); console.crlf();
console.print("Keyboard commands:"); console.print("Keyboard commands:");
console.crlf(); console.crlf();
console.print("\1k\1h\1n"); console.print("\1k\1h�����������������\1n");
console.crlf(); console.crlf();
console.print("\1n\1c\1h/\1n\1c or \1hCTRL-F\1n\1c: Find group/sub-board"); console.print("\1n\1c\1h/\1n\1c or \1hCTRL-F\1n\1c: Find group/sub-board");
console.crlf(); console.crlf();
...@@ -11550,7 +11536,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen) ...@@ -11550,7 +11536,7 @@ function DigDistMsgReader_showChooseMsgAreaHelpScreen(pLightbar, pClearScreen)
console.crlf(); console.crlf();
console.print("\1n\1cThe lightbar interface also allows up & down navigation through the lists:"); console.print("\1n\1cThe lightbar interface also allows up & down navigation through the lists:");
console.crlf(); console.crlf();
console.print("\1k\1h"); console.print("\1k\1h��������������������������������������������������������������������������");
console.crlf(); console.crlf();
console.print("\1n\1c\1hUp\1n\1c/\1hdown arrow\1n\1c: Move the cursor up/down one line"); console.print("\1n\1c\1hUp\1n\1c/\1hdown arrow\1n\1c: Move the cursor up/down one line");
console.crlf(); console.crlf();
...@@ -11998,7 +11984,7 @@ function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly) ...@@ -11998,7 +11984,7 @@ function DigDistMsgReader_GetExtdMsgHdrInfo(pMsgHdr, pKludgeOnly)
// attachments: An array of the attached filenames (as strings) // attachments: An array of the attached filenames (as strings)
// errorMsg: An error message, if something bad happened // errorMsg: An error message, if something bad happened
function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDetermineAttachments, function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDetermineAttachments,
pGetB64Data, pMsgBody, pMsgHasANSICodes) pGetB64Data, pMsgBody)
{ {
var retObj = { var retObj = {
msgText: "", msgText: "",
...@@ -12032,18 +12018,7 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter ...@@ -12032,18 +12018,7 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter
return retObj; 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.msgText = word_wrap(msgBody, console.screen_columns - 1, true);
retObj.attachments = [];
}
   
var msgTextAltered = retObj.msgText; // Will alter the message text, but not yet var msgTextAltered = retObj.msgText; // Will alter the message text, but not yet
// Only interpret @-codes if the user is reading personal email. There // Only interpret @-codes if the user is reading personal email. There
...@@ -12062,7 +12037,6 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter ...@@ -12062,7 +12037,6 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter
// can mess up the display of the message, so remove enter characters // can mess up the display of the message, so remove enter characters
// from the beginning of the message. // from the beginning of the message.
var msgTextWithoutAttrs = strip_ctrl(msgTextAltered); var msgTextWithoutAttrs = strip_ctrl(msgTextAltered);
var first240Chars = msgTextWithoutAttrs.substr(0, 240);
var fromToSearchStr = "By: " + pMsgHdr.from + " to " + pMsgHdr.to; var fromToSearchStr = "By: " + pMsgHdr.from + " to " + pMsgHdr.to;
var toFromSearchStr = "By: " + pMsgHdr.to + " to " + pMsgHdr.from; var toFromSearchStr = "By: " + pMsgHdr.to + " to " + pMsgHdr.from;
var fromToStrIdx = msgTextWithoutAttrs.indexOf(fromToSearchStr); var fromToStrIdx = msgTextWithoutAttrs.indexOf(fromToSearchStr);
...@@ -12124,10 +12098,6 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter ...@@ -12124,10 +12098,6 @@ function DigDistMsgReader_GetMsgInfoForEnhancedReader(pMsgHdr, pWordWrap, pDeter
retObj.numNonSolidScrollBlocks = this.msgAreaHeight - retObj.numSolidScrollBlocks; retObj.numNonSolidScrollBlocks = this.msgAreaHeight - retObj.numSolidScrollBlocks;
retObj.solidBlockStartRow = this.msgAreaTop; 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; return retObj;
} }
   
...@@ -12967,112 +12937,39 @@ function DigDistMsgReader_GetGroupNameAndDesc() ...@@ -12967,112 +12937,39 @@ function DigDistMsgReader_GetGroupNameAndDesc()
// Parameters: // Parameters:
// pMsgHdr: The header object for the message // pMsgHdr: The header object for the message
// pFilename: The name of the file to write the message to // 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: // Return value: An object containing the following properties:
// succeeded: Boolean - Whether or not the file was successfully written // succeeded: Boolean - Whether or not the file was successfully written
// errorMsg: String - On failure, will contain the reason it failed // 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 // Sanity checking
if (typeof(pMsgHdr) !== "object")
return({ succeeded: false, errorMsg: "Header object not given"});
if (typeof(pFilename) != "string") if (typeof(pFilename) != "string")
return({ succeeded: false, errorMsg: "Filename parameter not a string"}); return({ succeeded: false, errorMsg: "Filename parameter not a string"});
if (pFilename.length == 0) if (pFilename.length == 0)
return({ succeeded: false, errorMsg: "Empty filename given"}); 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 = { var retObj = {
succeeded: true, succeeded: true,
errorMsg: "" errorMsg: ""
}; };
   
// If there are message attachments, then treat pFilename as a directory and // Get the message text and save it
// create the directory for saving both the message text & attachments. // Note: GetMsgInfoForEnhancedReader() can expand @-codes in the message,
// Then, save the attachments to that directory. // but for now we're saving the message basically as-is.
var msgTextFilename = pFilename; //var msgInfo = this.GetMsgInfoForEnhancedReader(pMsgHdr, false, false, false);
if ((attachments != null) && (attachments.length > 0)) 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)) msgBody = msgbase.get_msg_body(false, pMsgHdr.number, false, false, true, true);
saveFileError = "Failed to copy " + attachments[attachIdx].filename; msgbase.close();
}
else
saveFileError = "File " + attachments[attachIdx].fullyPathedAttachmentFilename + " doesn't exist";
}
}
if (saveFileError.length > 0)
return({ succeeded: false, errorMsg: saveFileError });
}
   
var messageSaveFile = new File(msgTextFilename); var messageSaveFile = new File(pFilename);
if (messageSaveFile.open("w")) if (messageSaveFile.open("w"))
{ {
// Write some header information to the file // Write some header information to the file
if (typeof(pMsgHdr) == "object")
{
if (pMsgHdr.hasOwnProperty("from")) if (pMsgHdr.hasOwnProperty("from"))
messageSaveFile.writeln("From: " + pMsgHdr.from); messageSaveFile.writeln("From: " + pMsgHdr.from);
if (pMsgHdr.hasOwnProperty("to")) if (pMsgHdr.hasOwnProperty("to"))
...@@ -13094,24 +12991,32 @@ function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename, pStripCtrl, pMsgLine ...@@ -13094,24 +12991,32 @@ function DigDistMsgReader_SaveMsgToFile(pMsgHdr, pFilename, pStripCtrl, pMsgLine
if (pMsgHdr.hasOwnProperty("reply_id")) if (pMsgHdr.hasOwnProperty("reply_id"))
messageSaveFile.writeln("Reply ID: " + pMsgHdr.reply_id); messageSaveFile.writeln("Reply ID: " + pMsgHdr.reply_id);
messageSaveFile.writeln("==============================="); messageSaveFile.writeln("===============================");
// 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 // Write the message body to the file
if (pStripCtrl) messageSaveFile.write(msgBody);
{ messageSaveFile.close();
for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx)
messageSaveFile.writeln(strip_ctrl(msgLines[msgLineIdx]));
} }
else else
{ {
for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx) retObj.succeeded = false;
messageSaveFile.writeln(msgLines[msgLineIdx]); retObj.errorMsg = "Failed to open the file for writing";
} }
messageSaveFile.close();
} }
else else
{ {
retObj.succeeded = false; retObj.succeeded = false;
retObj.errorMsg = "Unable to open the message file for writing"; retObj.errorMsg = "Unable to open the messagebase";
} }
   
return retObj; return retObj;
...@@ -14740,7 +14645,7 @@ function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor) ...@@ -14740,7 +14645,7 @@ function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor)
var solidLine = ""; var solidLine = "";
var textLength = console.strlen(pText); var textLength = console.strlen(pText);
for (var i = 0; i < textLength; ++i) for (var i = 0; i < textLength; ++i)
solidLine += ""; solidLine += HORIZONTAL_SINGLE;
console.center(lineColor + solidLine); console.center(lineColor + solidLine);
} }
else else
...@@ -14750,7 +14655,7 @@ function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor) ...@@ -14750,7 +14655,7 @@ function displayTextWithLineBelow(pText, pCenter, pTextColor, pLineColor)
console.print(lineColor); console.print(lineColor);
var textLength = console.strlen(pText); var textLength = console.strlen(pText);
for (var i = 0; i < textLength; ++i) for (var i = 0; i < textLength; ++i)
console.print(""); console.print(HORIZONTAL_SINGLE);
console.crlf(); console.crlf();
} }
} }
...@@ -16559,423 +16464,6 @@ function getSubBoardCodeFromNum(pSubBoardNum) ...@@ -16559,423 +16464,6 @@ function getSubBoardCodeFromNum(pSubBoardNum)
return subBoardCode; 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 // This function recursively removes a directory and all of its contents. Returns
// whether or not the directory was removed. // whether or not the directory was removed.
// //
...@@ -18602,17 +18090,6 @@ function strWithToUserColor(pStr, pToUserColor) ...@@ -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 // 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 // 0xffffffff, returns the message number of the last readable message in
// the sub-board (this is the message number, not the index). // the sub-board (this is the message number, not the index).
...@@ -18702,7 +18179,6 @@ function allowUserToDownloadMessage_NewInterface(pMsgHdr, pSubCode) ...@@ -18702,7 +18179,6 @@ function allowUserToDownloadMessage_NewInterface(pMsgHdr, pSubCode)
console.creturn(); console.creturn();
bbs.download_msg_attachments(msgHdrForDownloading); bbs.download_msg_attachments(msgHdrForDownloading);
msgBase.close(); msgBase.close();
delete msgBase; // Free some memory?
} }
} }
   
......
Digital Distortion Message Reader Digital Distortion Message Reader
Version 1.48 Version 1.49
Release date: 2022-06-12 Release date: 2022-06-13
by by
......
...@@ -5,6 +5,10 @@ Revision History (change log) ...@@ -5,6 +5,10 @@ Revision History (change log)
============================= =============================
Version Date Description 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.48 2022-06-12 Improved display of ANSI messages
1.47a 2022-03-23 Internal change: Now calls bbs.edit_msg() for editing an 1.47a 2022-03-23 Internal change: Now calls bbs.edit_msg() for editing an
existing message (for Synchronet 3.18 and up). existing message (for Synchronet 3.18 and up).
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment