Newer
Older
nextMsgOffset = prevMsgHdr.offset;
}
}
*/
if (this.GetMsgIdx(pMsgHdr.number) > 0)
{
var prevMsgHdr;
for (var messageIdx = this.GetMsgIdx(pMsgHdr.number)-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
prevMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (((prevMsgHdr.attr & MSG_DELETE) == 0) && (typeof(prevMsgHdr.thread_id) == "number") && (prevMsgHdr.thread_id == pMsgHdr.thread_id))
{
//nextMsgOffset = prevMsgHdr.offset;
nextMsgOffset = this.GetMsgIdx(prevMsgHdr.number);

nightfox
committed
nextMsgOffset = 0;
}
}
}
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}
// Fall back to thread_next if the Synchronet version is below 3.16 or there is
// no thread_id field in the header
else if ((typeof(pMsgHdr.thread_back) == "number") && (pMsgHdr.thread_back > 0))

nightfox
committed
{
//newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_back);
newMsgOffset = this.GetMsgIdx(pMsgHdr.thread_back);
if (newMsgOffset < 0)
newMsgOffset = 0;
}
/*

nightfox
committed
// If thread_back is valid for the message header, then use that.
if ((typeof(pMsgHdr.thread_back) == "number") && (pMsgHdr.thread_back > 0))

nightfox
committed
{
//newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_back);
newMsgOffset = this.GetMsgIdx(pMsgHdr.thread_back);
}

nightfox
committed
else
{

nightfox
committed
// If thread_id is defined and the index of the first message
// in the thread is before the current message, then search
// backwards for messages with a matching thread_id.

nightfox
committed
//var firstThreadMsgIdx = this.AbsMsgNumToIdx(pMsgHdr.thread_first);
var firstThreadMsgIdx = this.GetMsgIdx(pMsgHdr.thread_first);

nightfox
committed
13046
13047
13048
13049
13050
13051
13052
13053
13054
13055
13056
13057
13058
13059
13060
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
if ((typeof(pMsgHdr.thread_id) == "number") && (firstThreadMsgIdx < pMsgHdr.offset))
{
// Note (2014-10-11): Digital Man said thread_id was
// introduced in Synchronet version 3.16 and was still
// a work in progress and isn't 100% accurate for
// networked sub-boards.
// Look for the previous message with the same thread ID.
// Note: I'm not sure when thread_id was introduced in
// Synchronet, so I'm not sure of the minimum version where
// this will work.
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the previous message in the thread
var nextMsgOffset = -1;
if (pMsgHdr.offset > 0)
{
for (var messageIdx = pMsgHdr.offset-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
var prevMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (((prevMsgHdr.attr & MSG_DELETE) == 0) && (typeof(prevMsgHdr.thread_id) == "number") && (prevMsgHdr.thread_id == pMsgHdr.thread_id))
nextMsgOffset = prevMsgHdr.offset;
}
}
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}
}
*/

nightfox
committed
break;
case THREAD_BY_TITLE:
case THREAD_BY_AUTHOR:
case THREAD_BY_TO_USER:
// Title (subject) searching will look for the subject anywhere in the
// other messages' subjects (not a fully exact subject match), so if
// the message subject is blank, we won't want to do the search.
var doSearch = true;
if ((pThreadType == THREAD_BY_TITLE) && (pMsgHdr.subject.length == 0))
doSearch = false;
if (doSearch)
{

nightfox
committed
13094
13095
13096
13097
13098
13099
13100
13101
13102
13103
13104
13105
13106
13107
13108
13109
13110
13111
13112
13113
13114
13115
13116
13117
13118
13119
13120
13121
13122
13123
13124
13125
13126
13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137
13138
13139
13140
13141
var subjUppercase = "";
var fromNameUppercase = "";
var toNameUppercase = "";
// Set up a message header matching function, depending on
// which field of the header we want to match
var msgHdrMatch;
if (pThreadType == THREAD_BY_TITLE)
{
subjUppercase = pMsgHdr.subject.toUpperCase();
// Remove any leading instances of "RE:" from the subject
while (/^RE:/.test(subjUppercase))
subjUppercase = subjUppercase.substr(3);
while (/^RE: /.test(subjUppercase))
subjUppercase = subjUppercase.substr(4);
// Remove any leading & trailing whitespace from the subject
subjUppercase = trimSpaces(subjUppercase, true, true, true);
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.subject.toUpperCase().indexOf(subjUppercase, 0) > -1));
};
}
else if (pThreadType == THREAD_BY_AUTHOR)
{
fromNameUppercase = pMsgHdr.from.toUpperCase();
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.from.toUpperCase() == fromNameUppercase));
};
}
else if (pThreadType == THREAD_BY_TO_USER)
{
toNameUppercase = pMsgHdr.to.toUpperCase();
msgHdrMatch = function(pMsgHdr) {
return (((pMsgHdr.attr & MSG_DELETE) == 0) && (pMsgHdr.to.toUpperCase() == toNameUppercase));
};
}
// Perform the search
// Write "Searching.." in case searching takes a while.
console.print("\1n");
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
console.print("\1h\1ySearching\1i...\1n");
// Look for the next message that contains the given message's subject
var nextMsgOffset = -1;
/*

nightfox
committed
if (pMsgHdr.offset > 0)
{

nightfox
committed
var nextMsgHdr;
for (var messageIdx = pMsgHdr.offset-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (msgHdrMatch(nextMsgHdr))
nextMsgOffset = nextMsgHdr.offset;
}
}
*/
if (this.GetMsgIdx(pMsgHdr.number) > 0)
{
var nextMsgHdr;
for (var messageIdx = this.GetMsgIdx(pMsgHdr.number)-1; (messageIdx >= 0) && (nextMsgOffset == -1); --messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (msgHdrMatch(nextMsgHdr))
{
//nextMsgOffset = nextMsgHdr.offset;
nextMsgOffset = this.GetMsgIdx(nextMsgHdr.number);
}
}
}

nightfox
committed
if (nextMsgOffset > -1)
newMsgOffset = nextMsgOffset;
}

nightfox
committed
break;
}
// If no messages were found, then output a message to say so with a momentary pause.
if (newMsgOffset == -1)
{
if (pPositionCursorForStatus)
{
console.gotoxy(1, console.screen_rows);
console.cleartoeol();
console.gotoxy(this.msgAreaLeft, console.screen_rows);
}
else
console.crlf();
console.print("\1n\1h\1yNo messages found.\1n");
mswait(ERROR_PAUSE_WAIT_MS);
}
return newMsgOffset;
}
// For the DigDistMsgReader class: Calculates the top message index for a page,
// for the traditional-style message list.
//
// Parameters:
// pPageNum: A page number (1-based)
function DigDistMsgReader_CalcTraditionalMsgListTopIdx(pPageNum)
{
if (this.reverseListOrder)
this.tradListTopMsgIdx = this.NumMessages() - (this.tradMsgListNumLines * (pPageNum-1)) - 1;
else
this.tradListTopMsgIdx = (this.tradMsgListNumLines * (pPageNum-1));
}
// For the DigDistMsgReader class: Calculates the top message index for a page,
// for the lightbar message list.
//
// Parameters:
// pPageNum: A page number (1-based)
function DigDistMsgReader_CalcLightbarMsgListTopIdx(pPageNum)
{
if (this.reverseListOrder)
this.lightbarListTopMsgIdx = this.NumMessages() - (this.lightbarMsgListNumLines * (pPageNum-1)) - 1;
else
{
//this.lightbarListTopMsgIdx = (this.lightbarMsgListNumLines * (pPageNum-1));
var pageIdx = pPageNum - 1;
if (pageIdx < 0)
pageIdx = 0;
this.lightbarListTopMsgIdx = this.lightbarMsgListNumLines * pageIdx;
}
}
// For the DigDistMsgReader class: Given a message number (1-based), this calculates
// the screen index veriables (stored in the object) for the message list. This is
// used for the enhanced reader mode when we want the message list to be in the
// correct place for the message being read.
//
// Parameters:
// pMsgNum: The message number (1-based)
function DigDistMsgReader_CalcMsgListScreenIdxVarsFromMsgNum(pMsgNum)
{
// Calculate the message list variables
var numItemsPerPage = this.tradMsgListNumLines;
if (this.msgListUseLightbarListInterface && canDoHighASCIIAndANSI())
numItemsPerPage = this.lightbarMsgListNumLines;
var newPageNum = findPageNumOfItemNum(pMsgNum, numItemsPerPage, this.NumMessages(), this.reverseListOrder);
13237
13238
13239
13240
13241
13242
13243
13244
13245
13246
13247
13248
13249
13250
13251
13252
13253
13254
13255
13256
13257
13258
13259
13260
13261
13262
13263
13264
13265
13266
13267
13268
13269
13270
13271
13272
13273
13274
13275
13276
13277
13278
13279
13280
13281
13282
13283
13284
13285
13286
13287
13288
13289
13290
13291
13292
13293
13294
13295
13296
13297
13298
13299
13300
13301
13302
13303
13304
13305
13306
13307
13308
13309
13310
13311
13312
13313
13314
13315
13316
13317
13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328
13329
13330
13331
13332
13333
13334
13335
13336
13337
13338
13339
13340
13341
this.CalcTraditionalMsgListTopIdx(newPageNum);
this.CalcLightbarMsgListTopIdx(newPageNum);
this.lightbarListSelectedMsgIdx = pMsgNum - 1;
if (this.lightbarListCurPos == null)
this.lightbarListCurPos = new Object();
this.lightbarListCurPos.x = 1;
this.lightbarListCurPos.y = this.lightbarMsgListStartScreenRow + ((pMsgNum-1) - this.lightbarListTopMsgIdx);
}
// For the DigDistMsgReader class: Validates a user's choice in message area.
// Returns a status/error message for the caller to display if there's an
// error. This function outputs intermediate status messages (i.e.,
// "Searching..").
//
// Parameters:
// pGrpIdx: The message group index (i.e., bbs.curgrp)
// pSubIdx: The message sub-board index (i.e., bbs.cursub)
// pCurPos: Optional - An object containing x and y properties representing
// the cursor position. Used for outputting intermediate status
// messages, but not for outputting the error message.
//
// Return value: An object containing the following properties:
// msgAreaGood: A boolean to indicate whether the message area
// can be selected
// errorMsg: If the message area can't be selected, this string
// will contain an eror message. Otherwise, this will
// be an empty string.
function DigDistMsgReader_ValidateMsgAreaChoice(pGrpIdx, pSubIdx, pCurPos)
{
var retObj = new Object();
retObj.msgAreaGood = true;
retObj.errorMsg = "";
// Get the internal code of the sub-board from the given group & sub-board
// indexes
var subCode = msg_area.grp_list[pGrpIdx].sub_list[pSubIdx].code;
// If a search is specified that would populate the search results, then do
// a search in the given sub-board.
if (this.SearchTypePopulatesSearchResults())
{
// See if we can use pCurPos to move the cursor before displaying messages
var useCurPos = (console.term_supports(USER_ANSI) && (typeof(pCurPos) == "object") &&
(typeof(pCurPos.x) == "number") && (typeof(pCurPos.y) == "number"));
// TODO: In case new messages were posted in this sub-board, it might help
// to check the current number of messages vs. the previous number of messages
// and search the new messages if there are more.
// Determine whether or not to search - If there are no search results for
// the given sub-board already, then do a search in the sub-board.
var doSearch = true;
if (this.msgSearchHdrs.hasOwnProperty(subCode) &&
(typeof(this.msgSearchHdrs[subCode]) == "object") &&
(typeof(this.msgSearchHdrs[subCode].indexed) != "undefined"))
{
doSearch = (this.msgSearchHdrs[subCode].indexed.length == 0);
}
if (doSearch)
{
if (useCurPos)
{
console.gotoxy(pCurPos);
console.cleartoeol("\1n");
console.gotoxy(pCurPos);
}
console.print("\1n\1h\1wSearching\1i...\1n");
var msgBase = new MsgBase(subCode);
if (msgBase.open())
{
this.msgSearchHdrs[subCode] = searchMsgbase(subCode, msgBase, this.searchType, this.searchString, this.readingPersonalEmailFromUser);
msgBase.close();
// If there are no messages, then set the return object variables to indicate so.
if (this.msgSearchHdrs[subCode].indexed.length == 0)
{
retObj.msgAreaGood = false;
retObj.errorMsg = "No search results found";
}
}
else
{
retObj.msgAreaGood = false;
retObj.errorMsg = "Unable to open message base (for searching)!";
}
}
}
else
{
// No search is specified. Just check to see if there are any messages
// to read in the given sub-board.
var msgBase = new MsgBase(subCode);
if (msgBase.open())
{
if (msgBase.total_msgs == 0)
{
retObj.msgAreaGood = false;
retObj.errorMsg = "No messages in that message area";
}
msgBase.close();
}
}
return retObj;
}
13342
13343
13344
13345
13346
13347
13348
13349
13350
13351
13352
13353
13354
13355
13356
13357
13358
13359
13360
13361
13362
13363
13364
13365
13366
13367
13368
13369
13370
13371
13372
13373
13374
13375
13376
13377
13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
13402
13403
13404
13405
13406
13407
13408
13409
13410
13411
13412
13413
13414
13415
13416
13417
13418
13419
13420
13421
13422
13423
13424
13425
13426
13427
13428
13429
13430
13431
13432
13433
13434
13435
13436
13437
13438
13439
13440
13441
13442
13443
13444
13445
13446
13447
13448
13449
13450
13451
13452
13453
13454
13455
13456
13457
13458
13459
13460
13461
13462
13463
13464
13465
13466
13467
13468
13469
13470
13471
13472
13473
13474
13475
13476
13477
13478
13479
13480
13481
13482
13483
13484
13485
13486
13487
13488
13489
13490
13491
13492
13493
13494
13495
13496
// For the DigDistMsgReader class: Writes message lines to a file on the BBS
// machine.
//
// 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)
{
// Sanity checking
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 = new Object();
retObj.succeeded = true;
retObj.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))
{
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 });
}
var messageSaveFile = new File(msgTextFilename);
if (messageSaveFile.open("w"))
{
// Write some header information to the file
if (typeof(pMsgHdr) == "object")
{
if (pMsgHdr.hasOwnProperty("from"))
messageSaveFile.writeln("From: " + pMsgHdr.from);
if (pMsgHdr.hasOwnProperty("to"))
messageSaveFile.writeln(" To: " + pMsgHdr.to);
if (pMsgHdr.hasOwnProperty("subject"))
messageSaveFile.writeln("Subj: " + pMsgHdr.subject);
/*
if (pMsgHdr.hasOwnProperty("when_written_time"))
messageSaveFile.writeln(strftime("Date: %Y-%m-%d %H:%M:%S", msgHeader.when_written_time));
*/
if (pMsgHdr.hasOwnProperty("date"))
messageSaveFile.writeln("Date: " + pMsgHdr.date);
if (pMsgHdr.hasOwnProperty("from_net_addr"))
messageSaveFile.writeln("From net address: " + pMsgHdr.from_net_addr);
if (pMsgHdr.hasOwnProperty("to_net_addr"))
messageSaveFile.writeln("To net address: " + pMsgHdr.to_net_addr);
if (pMsgHdr.hasOwnProperty("id"))
messageSaveFile.writeln("ID: " + pMsgHdr.id);
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]));
}
else
{
for (var msgLineIdx = 0; msgLineIdx < msgLines.length; ++msgLineIdx)
messageSaveFile.writeln(msgLines[msgLineIdx]);
}
messageSaveFile.close();
}
else
{
retObj.succeeded = false;
retObj.errorMsg = "Unable to open the message file for writing";
}
return retObj;
}
13497
13498
13499
13500
13501
13502
13503
13504
13505
13506
13507
13508
13509
13510
13511
13512
13513
13514
13515
13516
13517
13518
13519
13520
13521
13522
13523
13524
13525
13526
13527
13528
13529
13530
13531
13532
13533
13534
13535
13536
13537
13538
13539
13540
13541
13542
13543
13544
13545
13546
13547
13548
13549
13550
13551
13552
13553
13554
13555
13556
13557
13558
13559
13560
13561
13562
13563
13564
13565
13566
13567
13568
13569
13570
13571
13572
13573
13574
13575
13576
13577
13578
13579
13580
13581
13582
13583
13584
13585
13586
13587
13588
13589
13590
13591
13592
13593
13594
13595
13596
13597
13598
13599
13600
13601
13602
13603
13604
13605
13606
13607
13608
13609
13610
13611
13612
13613
13614
13615
// For the DigDistMsgReader class: Toggles whether a message has been 'selected'
// (i.e., for things like batch delete, etc.)
//
// Parameters:
// pSubCode: The internal sub-board code of the message sub-board where the
// message resides
// pMsgIdx: The index of the message to toggle
// pSelected: Optional boolean to explictly specify whether the message should
// be selected. If this is not provided (or is null), then this
// message will simply toggle the selection state of the message.
function DigDistMsgReader_ToggleSelectedMessage(pSubCode, pMsgIdx, pSelected)
{
// Sanity checking
if (typeof(pSubCode) != "string") return;
if (typeof(pMsgIdx) != "number") return;
// If the 'selected message' object doesn't have the sub code index,
// then add it.
if (!this.selectedMessages.hasOwnProperty(pSubCode))
this.selectedMessages[pSubCode] = new Object();
// If pSelected is a boolean, then it specifies the specific selection
// state of the message (true = selected, false = not selected).
if (typeof(pSelected) == "boolean")
{
if (pSelected)
{
if (!this.selectedMessages[pSubCode].hasOwnProperty(pMsgIdx))
this.selectedMessages[pSubCode][pMsgIdx] = true;
}
else
{
if (this.selectedMessages[pSubCode].hasOwnProperty(pMsgIdx))
delete this.selectedMessages[pSubCode][pMsgIdx];
}
}
else
{
// pSelected is not a boolean, so simply toggle the selection state of
// the message.
// If the object for the given sub-board code contains the message
// index, then remove it. Otherwise, add it.
if (this.selectedMessages[pSubCode].hasOwnProperty(pMsgIdx))
delete this.selectedMessages[pSubCode][pMsgIdx];
else
this.selectedMessages[pSubCode][pMsgIdx] = true;
}
}
// For the DigDistMsgReader class: Returns whether a message (by sub-board code & index)
// is selected (i.e., for batch delete, etc.).
//
// Parameters:
// pSubCode: The internal sub-board code of the message sub-board where the
// message resides
// pMsgIdx: The index of the message to toggle
//
// Return value: Boolean - Whether or not the given message has been selected
function DigDistMsgReader_MessageIsSelected(pSubCode, pMsgIdx)
{
return (this.selectedMessages.hasOwnProperty(pSubCode) && this.selectedMessages[pSubCode].hasOwnProperty(pMsgIdx));
}
// For the DigDistMsgReader class: Checks to see if all selected messages can
// be deleted (i.e., whether the user has permission to delete all of them).
function DigDistMsgReader_AllSelectedMessagesCanBeDeleted()
{
// If the user has sysop access, then they should be able to delete messages.
if (gIsSysop)
return true;
var userCanDeleteAllSelectedMessages = true;
var msgBase = null;
for (var subBoardCode in this.selectedMessages)
{
// If the current sub-board is personal mail, then the user can delete
// those messages. Otherwise, check the sub-board configuration to see
// if the user can delete messages in the sub-board.
if (subBoardCode != "mail")
{
msgBase = new MsgBase(subBoardCode);
if (msgBase.open())
{
userCanDeleteAllSelectedMessages = userCanDeleteAllSelectedMessages && ((msgBase.cfg.settings & SUB_DEL) == SUB_DEL);
msgBase.close();
}
}
if (!userCanDeleteAllSelectedMessages)
break;
}
return userCanDeleteAllSelectedMessages;
}
// For the DigDistMsgReader class: Marks the 'selected messages' (in
// this.selecteMessages) as deleted. Returns an object with the following
// properties:
// deletedAll: Boolean - Whether or not all messages were successfully marked
// for deletion
// failureList: An object containing indexes of messages that failed to get
// marked for deletion, indexed by internal sub-board code, then
// containing messages indexes as properties. Reasons for failing
// to mark messages deleted can include the user not having permission
// to delete in a sub-board, failure to open the sub-board, etc.
function DigDistMsgReader_DeleteSelectedMessages()
{
var retObj = new Object();
retObj.deletedAll = true;
retObj.failureList = new Object();
var msgBase = null;
var msgHdr = null;
var msgWasDeleted = false;
for (var subBoardCode in this.selectedMessages)
{
msgBase = new MsgBase(subBoardCode);
if (msgBase.open())
{
// Allow the user to delete the messages if they're the sysop, they're
// reading their personal mail, or the sub-board allows deleting messages.
if (gIsSysop || (subBoardCode == "mail") || ((msgBase.cfg.settings & SUB_DEL) == SUB_DEL))
{
for (var msgIdx in this.selectedMessages[subBoardCode])
{
// It seems that msgIdx is a string, so make sure we have a
// numeric version.
var msgIdxNumber = +msgIdx;
// If doing a search (this.msgSearchHdrs has the sub-board code),
// then get the message header by index from there. Otherwise,
// use the message base object to get the message by index.
if (this.msgSearchHdrs.hasOwnProperty(subBoardCode) &&
(this.msgSearchHdrs[subBoardCode].indexed.length > 0))
{
if ((msgIdxNumber >= 0) && (msgIdxNumber < this.msgSearchHdrs[subBoardCode].indexed.length))
msgHdr = this.msgSearchHdrs[subBoardCode].indexed[msgIdxNumber];
}

nightfox
committed
else if (this.hdrsForCurrentSubBoard.length > 0)
{
if ((msgIdxNumber >= 0) && (msgIdxNumber < this.hdrsForCurrentSubBoard.length))
msgHdr = this.hdrsForCurrentSubBoard[msgIdxNumber];
}
else
{
if ((msgIdxNumber >= 0) && (msgIdxNumber < msgBase.total_msgs))
msgHdr = msgBase.get_msg_header(true, msgIdxNumber, false);
}
// If we got the message header, then mark it for deletion.
// If the message header wasn't marked as deleted, then add
// the message index to the return object.
if (msgHdr != null)
{
//msgWasDeleted = msgBase.remove_msg(true, msgHdr.offset);
msgWasDeleted = msgBase.remove_msg(false, msgHdr.number);
}
else
msgWasDeleted = false;
if (msgWasDeleted)
{

nightfox
committed
// Refresh the message header in the header arrays (if it
// exists there) and remove the message index from the
// selectedMessages object

nightfox
committed
this.RefreshHdrInSavedArrays(msgIdxNumber, MSG_DELETE, subBoardCode);
delete this.selectedMessages[subBoardCode][msgIdx];

nightfox
committed
// Delete any vote response messages that may exist for this message
var voteDelRetObj = deleteVoteMsgs(msgBase, msgHdr.number, (subBoardCode == "mail"));
// TODO: If the main messages was deleted, does it matter if vote response messages
// are deleted?
if (!voteDelRetObj.allVoteMsgsDeleted)
{
retObj.deletedAll = false;
if (!retObj.failureList.hasOwnProperty(subBoardCode))
retObj.failureList[subBoardCode] = new Array();
retObj.failureList[subBoardCode].push(msgIdxNumber);
}
}
else
{
retObj.deletedAll = false;
if (!retObj.failureList.hasOwnProperty(subBoardCode))

nightfox
committed
retObj.failureList[subBoardCode] = new Array();
13679
13680
13681
13682
13683
13684
13685
13686
13687
13688
13689
13690
13691
13692
13693
13694
13695
retObj.failureList[subBoardCode].push(msgIdxNumber);
}
}
// If the sub-board index array no longer has any properties (i.e.,
// all messages in the sub-board were marked as deleted), then remove
// the sub-board property from this.selectedMessages
if (Object.keys(this.selectedMessages[subBoardCode]).length == 0)
delete this.selectedMessages[subBoardCode];
}
else
{
// The user doesn't have permission to delete messages
// in this sub-board.
// Create an entry in retObj.failureList indexed by the
// sub-board code to indicate failure to delete all
// messages in the sub-board.
retObj.deletedAll = false;

nightfox
committed
retObj.failureList[subBoardCode] = new Array();
}
msgBase.close();
}
else
{
// Failure to open the sub-board.
// Create an entry in retObj.failureList indexed by the
// sub-board code to indicate failure to delete all messages
// in the sub-board.
retObj.deletedAll = false;

nightfox
committed
retObj.failureList[subBoardCode] = new Array();
13709
13710
13711
13712
13713
13714
13715
13716
13717
13718
13719
13720
13721
13722
13723
13724
13725
}
}
return retObj;
}
// For the DigDistMsgReader class: Returns the number of selected messages
function DigDistMsgReader_NumSelectedMessages()
{
var numSelectedMsgs = 0;
for (var subBoardCode in this.selectedMessages)
numSelectedMsgs += Object.keys(this.selectedMessages[subBoardCode]).length;
return numSelectedMsgs;
}

nightfox
committed
13726
13727
13728
13729
13730
13731
13732
13733
13734
13735
13736
13737
13738
13739
13740
13741
13742
13743
13744
13745
13746
13747
13748
13749
13750
13751
13752
13753
13754
13755
13756
// Allows the user to forward a message to an email address or
// another user. This function is interactive with the user.
//
// Parameters:
// pMsgHeader: The header of the message being forwarded
// pMsgBody: The body text of the message
//
// Return value: A blank string on success or a string containing a
// message upon failure.
function DigDistMsgReader_ForwardMessage(pMsgHdr, pMsgBody)
{
if (typeof(pMsgHdr) != "object")
return "Invalid message header given";
var retStr = "";
console.print("\1n");
console.crlf();
console.print("\1cUser name/number/email address\1h:\1n");
console.crlf();
var msgDest = console.getstr(console.screen_columns - 1, K_LINE);
console.print("\1n");
console.crlf();
if (msgDest.length > 0)
{
var tmpMsgbase = new MsgBase("mail");
if (tmpMsgbase.open())
{
// If the given message body is not a string, then get the
// message body from the messagebase.
if (typeof(pMsgBody) != "string")
pMsgBody = this.msgbase.get_msg_body(false, pMsgHdr.number);

nightfox
committed
13758
13759
13760
13761
13762
13763
13764
13765
13766
13767
13768
13769
13770
13771
13772
13773
13774
13775
13776
13777
13778
13779
13780
13781
13782
13783
13784
13785
13786
13787
13788
13789
13790
13791
13792
13793
13794
13795
13796
13797
13798
13799
13800
13801
13802
13803
13804
13805
13806
13807
13808
13809
13810
13811
13812
13813
13814
13815
13816
13817
13818
13819
13820
13821
13822
13823
13824
13825
13826
13827
13828
13829
13830
13831
13832
13833
13834
13835
13836
13837
13838
13839
13840
13841
13842
13843
13844
13845
13846
13847
13848
13849
13850
13851
13852
13853
13854
13855
13856
13857
13858
13859
13860
13861
13862
13863
13864
13865
13866
13867
13868
13869
13870
13871
13872
13873
13874
13875
13876
13877
13878
13879
13880
13881
13882
13883
13884
13885
13886
13887
13888
13889
13890
13891
13892
13893
13894
13895
13896
// Prepend some lines to the message body to describe where
// the message came from originally.
var newMsgBody = "This is a forwarded message from " + system.name + "\n";
newMsgBody += "Forwarded by: " + user.alias;
if (user.alias != user.name)
newMsgBody += " (" + user.name + ")";
newMsgBody += "\n";
if (this.subBoardCode == "mail")
newMsgBody += "From " + user.name + "'s personal email\n";
else
{
newMsgBody += "From sub-board: "
+ msg_area.grp_list[msg_area.sub[this.subBoardCode].grp_index].description
+ ", " + msg_area.sub[this.subBoardCode].description + "\n";
}
newMsgBody += "From: " + pMsgHdr.from + "\n";
newMsgBody += "To: " + pMsgHdr.to + "\n";
newMsgBody += "Subject: " + pMsgHdr.subject + "\n";
newMsgBody += "==================================\n\n";
newMsgBody += pMsgBody;
// Create part of a header object which will be used when saving/sending
// the message. The destination ("to" informatoin) will be filled in
// according to the destination type.
var destMsgHdr = { to_net_type: NET_NONE, from: user.name,
replyto: user.name, subject: "Fwd: " + pMsgHdr.subject };
if (user.netmail.length > 0)
destMsgHdr.replyto_net_addr = user.netmail;
else
destMsgHdr.replyto_net_addr = user.email;
//destMsgHdr.when_written_time =
//destMsgHdr.when_written_zone = system.timezone;
//destMsgHdr.when_written_zone_offset =
var confirmedForwardMsg = true;
// If the user entered an email address, then forward the message
// via Internet email.
if (gEmailRegex.test(msgDest))
{
confirmedForwardMsg = console.yesno("Forward via email to " + msgDest);
if (confirmedForwardMsg)
{
console.print("\1n\1cForwarding via email to " + msgDest + "\1n");
console.crlf();
destMsgHdr.to = msgDest;
destMsgHdr.to_net_addr = msgDest;
destMsgHdr.to_net_type = NET_INTERNET;
}
}
else
{
// See if what the user entered is a user number/name/alias
var userNum = 0;
if (/^[0-9]+$/.test(msgDest))
{
userNum = +msgDest;
// Determine if the user entered a valid user number
var lastUserNum = (system.lastuser == undefined ? system.stats.total_users : system.lastuser + 1);
if ((userNum < 1) || (userNum >= lastUserNum))
{
userNum = 0;
console.print("\1h\1y* Invalid user number (" + msgDest + ")\1n");
console.crlf();
}
}
else // Try to get a user number assuming msgDest is a username/alias
userNum = system.matchuser(msgDest, true);
// If we have a valid user number, then we can forward the message.
if (userNum > 0)
{
var destUser = new User(userNum);
confirmedForwardMsg = console.yesno("Forward to " + destUser.alias + " (user " + destUser.number + ")");
if (confirmedForwardMsg)
{
destMsgHdr.to = destUser.alias;
// If the destination user has an Internet email address,
// ask the user if they want to send to the destination
// user's Internet email address
var sendToNetEmail = false;
if (destUser.netmail.length > 0)
{
sendToNetEmail = !console.noyes("Send to the user's Internet email (" + destUser.netmail + ")");
if (sendToNetEmail)
{
console.print("\1n\1cForwarding to " + destUser.netmail + "\1n");
console.crlf();
destMsgHdr.to = destUser.name;
destMsgHdr.to_net_addr = destUser.netmail;
destMsgHdr.to_net_type = NET_INTERNET;
}
}
if (!sendToNetEmail)
{
console.print("\1n\1cForwarding to " + destUser.alias + "\1n");
console.crlf();
destMsgHdr.to_ext = destUser.number;
}
}
}
else
{
confirmedForwardMsg = false;
console.print("\1h\1y* Unknown destination\1n");
console.crlf();
}
}
var savedMsg = true;
if (confirmedForwardMsg)
savedMsg = tmpMsgbase.save_msg(destMsgHdr, newMsgBody);
else
{
console.print("\1n\1cCanceled\1n");
console.crlf();
}
tmpMsgbase.close();
if (!savedMsg)
{
console.print("\1h\1y* Failed to send the message!\1n");
console.crlf();
}
// Pause for user input so the user can see the messages written
console.pause();
}
else
retStr = "Failed to open email messagebase";
}
else
{
console.print("\1n\1cCanceled\1n");
console.crlf();
console.pause();
}
return retStr;
}
// For the DigDistMsgReader class: Lets the user vote on a message
//
// Parameters:
// pMsgHdr: The header of the mesasge being voted on
// pRemoveNLsFromVoteText: Optional boolean - Whether or not to remove newlines
// (and carriage returns) from the voting text from
// text.dat. Defaults to false.
//
// Return value: An object with the following properties:
// BBSHasVoteFunction: Boolean - Whether or not the system has
// the vote_msg function
// savedVote: Boolean - Whether or not the vote was saved
// userQuit: Boolean - Whether or not the user quit and didn't vote
// errorMsg: String - An error message, if something went wrong
// mnemonicsRequiredForErrorMsg: Boolean - Whether or not mnemonics is required to print the error message
// updatedHdr: The updated message header containing vote information.
// If something went wrong, this will be null.
function DigDistMsgReader_VoteOnMessage(pMsgHdr, pRemoveNLsFromVoteText)
{
var retObj = new Object();
retObj.BBSHasVoteFunction = (typeof(this.msgbase.vote_msg) === "function");
retObj.savedVote = false;
retObj.userQuit = false;
retObj.errorMsg = "";
retObj.mnemonicsRequiredForErrorMsg = false;
retObj.updatedHdr = null;

nightfox
committed
// Don't allow voting for personal email
if (this.subBoardCode == "mail")
{
retObj.errorMsg = "Can not vote on personal email";
return retObj;
}
// Check whether the user has the voting restiction
if ((user.security.restrictions & UFLAG_V) == UFLAG_V)
{
// Use the line from text.dat that says the user is not allowed to vote,
// and remove newlines from it.
retObj.errorMsg = "\1n" + bbs.text(typeof(R_Voting) != "undefined" ? R_Voting : 781).replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
return retObj;
}

nightfox
committed
// If the message vote function is not defined in the running verison of Synchronet,
// then just return.
if (!retObj.BBSHasVoteFunction)
return retObj;
var removeNLsFromVoteText = (typeof(pRemoveNLsFromVoteText) === "boolean" ? pRemoveNLsFromVoteText : false)
// See if voting is allowed in the current sub-board
if ((msg_area.sub[this.subBoardCode].settings & SUB_NOVOTING) == SUB_NOVOTING)
{
retObj.errorMsg = bbs.text(typeof(VotingNotAllowed) != "undefined" ? VotingNotAllowed : 779);
if (removeNLsFromVoteText)
retObj.errorMsg = retObj.errorMsg.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");
retObj.mnemonicsRequiredForErrorMsg = true;
return retObj;
}
// If the message is a poll question and has the maximum number of votes
// already or is closed for voting, then don't let the user vote on it.
if ((pMsgHdr.attr & MSG_POLL) == MSG_POLL)
{
var userVotedMaxVotes = false;
var numVotes = (pMsgHdr.hasOwnProperty("votes") ? pMsgHdr.votes : 0);
if (typeof(this.msgbase.how_user_voted) === "function")
{
var votes = this.msgbase.how_user_voted(pMsgHdr.number, (this.msgbase.cfg.settings & SUB_NAME) == SUB_NAME ? user.name : user.alias);
13966
13967
13968
13969
13970
13971
13972
13973
13974
13975
13976
13977
13978
13979
13980
13981
13982
13983
13984
13985
13986
13987
13988
13989
13990
13991
13992
13993
if (votes >= 0)
{
if ((votes == 0) || (votes == 1))
userVotedMaxVotes = (votes == 3); // (1 << 0) | (1 << 1);
else
{
userVotedMaxVotes = true;
for (var voteIdx = 0; voteIdx <= numVotes; ++voteIdx)
{
if (votes && (1 << voteIdx) == 0)
{
userVotedMaxVotes = false;
break;
}
}
}
}
}
var pollIsClosed = ((pMsgHdr.auxattr & POLL_CLOSED) == POLL_CLOSED);
if (pollIsClosed)
{
retObj.errorMsg = "This poll is closed";
return retObj;
}
else if (userVotedMaxVotes)
{
retObj.errorMsg = bbs.text(typeof(VotedAlready) != "undefined" ? VotedAlready : 780);
if (removeNLsFromVoteText)
retObj.errorMsg = retObj.errorMsg.replace("\r\n", "").replace("\n", "").replace("\N", "").replace("\r", "").replace("\R", "").replace("\R\n", "").replace("\r\N", "").replace("\R\N", "");