Newer
Older
12001
12002
12003
12004
12005
12006
12007
12008
12009
12010
12011
12012
12013
12014
12015
12016
12017
12018
12019
12020
12021
12022
12023
12024
12025
12026
12027
12028
12029
12030
12031
12032
12033
12034
12035
12036
12037
12038
12039
12040
// (according to this.searchType) and the search result objects are defined for
// the current sub-board (as specified by this.subBoardCode).
//
// Return value: Boolean - Whether or not there is a search specified and the
// search result objects are defined for the current sub-board
// (as specified by this.subBoardCode).
function DigDistMsgReader_SearchingAndResultObjsDefinedForCurSub()
{
return (this.SearchTypePopulatesSearchResults() && (this.msgSearchHdrs != null) &&
this.msgSearchHdrs.hasOwnProperty(this.subBoardCode) &&
(typeof(this.msgSearchHdrs[this.subBoardCode]) == "object") &&
(typeof(this.msgSearchHdrs[this.subBoardCode].indexed) != "undefined"));
}
// For the DigDistMsgReader class: Removes a message header from the search
// results array for the current sub-board.
//
// Parameters:
// pMsgIdx: The index of the message header to remove (in the indexed messages,
// not necessarily the actual message offset in the messagebase)
function DigDistMsgReader_RemoveFromSearchResults(pMsgIdx)
{
if (typeof(pMsgIdx) != "number")
return;
if ((typeof(this.msgSearchHdrs) == "object") &&
this.msgSearchHdrs.hasOwnProperty(this.subBoardCode) &&
(typeof(this.msgSearchHdrs[this.subBoardCode].indexed) != "undefined"))
{
if ((pMsgIdx >= 0) && (pMsgIdx < this.msgSearchHdrs[this.subBoardCode].indexed.length))
this.msgSearchHdrs[this.subBoardCode].indexed.splice(pMsgIdx, 1);
}
}
// For the DigDistMsgReader class: Looks for the next message in the thread of
// a given message (by its header).
//
// Paramters:
// pMsgHdr: A message header object - The next message in the thread will be
// searched starting from this message

nightfox
committed
// pThreadType: The type of threading to use. Can be THREAD_BY_ID, THREAD_BY_TITLE,
// THREAD_BY_AUTHOR, or THREAD_BY_TO_USER.
// pPositionCursorForStatus: Optional boolean - Whether or not to move the cursor
// to the bottom row before outputting status messages.
// Defaults to false.
//
// Return value: The offset (index) of the next message thread, or -1 if none
// was found.

nightfox
committed
function DigDistMsgReader_FindThreadNextOffset(pMsgHdr, pThreadType, pPositionCursorForStatus)
{
if ((this.msgbase == null) || (!this.msgbase.is_open))
return -1;
if ((pMsgHdr == null) || (typeof(pMsgHdr) != "object"))
return -1;
var newMsgOffset = -1;

nightfox
committed
switch (pThreadType)
{

nightfox
committed
case THREAD_BY_ID:
default:
12062
12063
12064
12065
12066
12067
12068
12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
12082
12083
12084
12085
12086
12087
12088
12089
12090
12091
12092
12093
12094
12095
12096
12097
12098
12099
// The thread_id field was introduced in Synchronet 3.16. So, if
// the Synchronet version is 3.16 or higher and the message header
// has a thread_id field, then look for the next message with the
// same thread ID. If the Synchronet version is below 3.16 or there
// is no thread ID, then fall back to using the header's thread_next,
// if it exists.
if ((system.version_num >= 31600) && (typeof(pMsgHdr.thread_id) == "number"))
{
// Look for the next message with the same thread ID.
// 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 in the thread
var nextMsgOffset = -1;
var numOfMessages = this.NumMessages();
if (pMsgHdr.offset < numOfMessages - 1)
{
var nextMsgHdr;
for (var messageIdx = pMsgHdr.offset+1; (messageIdx < numOfMessages) && (nextMsgOffset == -1); ++messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (((nextMsgHdr.attr & MSG_DELETE) == 0) && (typeof(nextMsgHdr.thread_id) == "number") && (nextMsgHdr.thread_id == pMsgHdr.thread_id))
nextMsgOffset = nextMsgHdr.offset;
}
}
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_next) == "number") && (pMsgHdr.thread_next > 0))
newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_next);

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
12112
12113
12114
12115
12116
12117
12118
12119
12120
12121
12122
12123
12124
12125
12126
12127
12128
12129
12130
12131
12132
12133
12134
12135
12136
12137
12138
12139
12140
12141
12142
12143
12144
12145
12146
12147
12148
12149
12150
12151
12152
12153
12154
12155
12156
12157
12158
12159
12160
12161
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;
var numOfMessages = this.NumMessages();
if (pMsgHdr.offset < numOfMessages - 1)
{

nightfox
committed
var nextMsgHdr;
for (var messageIdx = pMsgHdr.offset+1; (messageIdx < numOfMessages) && (nextMsgOffset == -1); ++messageIdx)
{
nextMsgHdr = this.GetMsgHdrByIdx(messageIdx);
if (msgHdrMatch(nextMsgHdr))
nextMsgOffset = nextMsgHdr.offset;
}
}

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: Looks for the previous message in the thread of
// a given message (by its header).
//
// Paramters:
// pMsgHdr: A message header object - The previous message in the thread will be
// searched starting from this message

nightfox
committed
// pThreadType: The type of threading to use. Can be THREAD_BY_ID, THREAD_BY_TITLE,
// THREAD_BY_AUTHOR, or THREAD_BY_TO_USER.
// pPositionCursorForStatus: Optional boolean - Whether or not to move the cursor
// to the bottom row before outputting status messages.
// Defaults to false.
//
// Return value: The offset (index) of the previous message thread, or -1 if
// none was found.

nightfox
committed
function DigDistMsgReader_FindThreadPrevOffset(pMsgHdr, pThreadType, pPositionCursorForStatus)
{
if ((this.msgbase == null) || (!this.msgbase.is_open))
return -1;
if ((pMsgHdr == null) || (typeof(pMsgHdr) != "object"))
return -1;
var newMsgOffset = -1;

nightfox
committed
switch (pThreadType)
{

nightfox
committed
case THREAD_BY_ID:
default:
12222
12223
12224
12225
12226
12227
12228
12229
12230
12231
12232
12233
12234
12235
12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252
12253
12254
12255
12256
12257
12258
12259
12260
// The thread_id field was introduced in Synchronet 3.16. So, if
// the Synchronet version is 3.16 or higher and the message header
// has a thread_id field, then look for the previous message with the
// same thread ID. If the Synchronet version is below 3.16 or there
// is no thread ID, then fall back to using the header's thread_next,
// if it exists.
if ((system.version_num >= 31600) && (typeof(pMsgHdr.thread_id) == "number"))
{
// Look for the previous message with the same thread ID.
// 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)
{
var prevMsgHdr;
for (var messageIdx = pMsgHdr.offset-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;
}
}
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))
newMsgOffset = this.AbsMsgNumToIdx(pMsgHdr.thread_back);
/*

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

nightfox
committed
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277
12278
12279
12280
12281
12282
12283
12284
12285
12286
12287
12288
12289
12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
// 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.
var firstThreadMsgIdx = this.AbsMsgNumToIdx(pMsgHdr.thread_first);
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
12318
12319
12320
12321
12322
12323
12324
12325
12326
12327
12328
12329
12330
12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342
12343
12344
12345
12346
12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364
12365
12366
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;
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;
}
}

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)
12421
12422
12423
12424
12425
12426
12427
12428
12429
12430
12431
12432
12433
12434
12435
12436
12437
12438
this.lightbarListTopMsgIdx = this.NumMessages() - (this.lightbarMsgListNumLines * (pPageNum-1)) - 1;
else
this.lightbarListTopMsgIdx = (this.lightbarMsgListNumLines * (pPageNum-1));
}
// 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);
12440
12441
12442
12443
12444
12445
12446
12447
12448
12449
12450
12451
12452
12453
12454
12455
12456
12457
12458
12459
12460
12461
12462
12463
12464
12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
12479
12480
12481
12482
12483
12484
12485
12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
12507
12508
12509
12510
12511
12512
12513
12514
12515
12516
12517
12518
12519
12520
12521
12522
12523
12524
12525
12526
12527
12528
12529
12530
12531
12532
12533
12534
12535
12536
12537
12538
12539
12540
12541
12542
12543
12544
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;
}
12545
12546
12547
12548
12549
12550
12551
12552
12553
12554
12555
12556
12557
12558
12559
12560
12561
12562
12563
12564
12565
12566
12567
12568
12569
12570
12571
12572
12573
12574
12575
12576
12577
12578
12579
12580
12581
12582
12583
12584
12585
12586
12587
12588
12589
12590
12591
12592
12593
12594
12595
12596
12597
12598
12599
12600
12601
12602
12603
12604
12605
12606
12607
12608
12609
12610
12611
12612
12613
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
12627
12628
12629
12630
12631
12632
12633
12634
12635
12636
12637
12638
12639
12640
12641
12642
12643
12644
12645
12646
12647
12648
12649
12650
12651
12652
12653
12654
12655
12656
12657
12658
12659
12660
12661
12662
12663
12664
12665
12666
12667
12668
12669
12670
12671
12672
12673
12674
12675
12676
12677
12678
12679
12680
12681
12682
12683
12684
12685
12686
12687
12688
12689
12690
12691
12692
12693
12694
12695
12696
12697
12698
12699
// 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;
}
12700
12701
12702
12703
12704
12705
12706
12707
12708
12709
12710
12711
12712
12713
12714
12715
12716
12717
12718
12719
12720
12721
12722
12723
12724
12725
12726
12727
12728
12729
12730
12731
12732
12733
12734
12735
12736
12737
12738
12739
12740
12741
12742
12743
12744
12745
12746
12747
12748
12749
12750
12751
12752
12753
12754
12755
12756
12757
12758
12759
12760
12761
12762
12763
12764
12765
12766
12767
12768
12769
12770
12771
12772
12773
12774
12775
12776
12777
12778
12779
12780
12781
12782
12783
12784
12785
12786
12787
12788
12789
12790
12791
12792
12793
12794
12795
12796
12797
12798
12799
12800
12801
12802
12803
12804
12805
12806
12807
12808
12809
12810
12811
12812
12813
12814
12815
12816
12817
12818
// 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))
12822
12823
12824
12825
12826
12827
12828
12829
12830
12831
12832
12833
12834
12835
12836
12837
12838
12839
{
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];
}
else
{
if ((msgIdxNumber >= 0) && (msgIdxNumber < msgBase.total_msgs))
msgHdr = msgBase.get_msg_header(true, msgIdxNumber, false);
12841
12842
12843
12844
12845
12846
12847
12848
12849
12850
12851
12852
12853
12854
12855
12856
12857
12858
12859
12860
12861
12862
12863
12864
12865
12866
12867
12868
12869
12870
12871
12872
12873
12874
12875
12876
12877
12878
12879
12880
12881
12882
12883
12884
12885
12886
12887
12888
12889
12890
12891
12892
12893
12894
12895
12896
12897
12898
12899
12900
12901
12902
12903
12904
12905
12906
12907
12908
}
// 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);
else
msgWasDeleted = false;
if (msgWasDeleted)
{
// Refresh the message header in the search results (if it
// exists there) and remove the message index from the
// selectedMessages object
this.RefreshSearchResultMsgHdr(msgIdxNumber, MSG_DELETE, subBoardCode);
delete this.selectedMessages[subBoardCode][msgIdx];
}
else
{
retObj.deletedAll = false;
if (!retObj.failureList.hasOwnProperty(subBoardCode))
retObj.failureList[subBoardCode] = new Object();
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;
retObj.failureList[subBoardCode] = new Object();
}
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;
retObj.failureList[subBoardCode] = new Object();
}
}
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;
}
12909
12910
12911
12912
12913
12914
12915
12916
12917
12918
12919
12920
12921
12922
12923
12924
12925
12926
12927
12928
12929
12930
12931
12932
12933
12934
12935
12936
12937
12938
12939
12940
12941
12942
12943
12944
12945
12946
12947
12948
12949
12950
12951
12952
12953
12954
12955
12956
12957
12958
12959
12960
12961
12962
12963
12964
12965
12966
12967
12968
12969
12970
12971
12972
12973
12974
12975
12976
12977
12978
12979
12980
12981
12982
12983
12984
12985
12986
12987
12988
12989
12990
12991
12992
12993
12994
12995
12996
12997
12998
12999
13000
///////////////////////////////////////////////////////////////////////////////////
// Helper functions
// Displays the program information.
function DisplayProgramInfo()
{
displayTextWithLineBelow("Digital Distortion Message Reader", true, "\1n\1c\1h", "\1k\1h")
console.center("\1n\1cVersion \1g" + READER_VERSION + " \1w\1h(\1b" + READER_DATE + "\1w)");
console.crlf();
}
// This function returns an array of default colors used in the
// DigDistMessageReader class.
function getDefaultColors()
{
var colorArray = new Array();
// Header line: "Current msg group:"
colorArray["msgListHeaderMsgGroupTextColor"] = "\1n\1" + "4\1c"; // Normal cyan on blue background
//colorArray["msgListHeaderMsgGroupTextColor"] = "\1n\1" + "4\1w"; // Normal white on blue background
// Header line: Message group name
colorArray["msgListHeaderMsgGroupNameColor"] = "\1h\1c"; // High cyan
//colorArray["msgListHeaderMsgGroupNameColor"] = "\1h\1w"; // High white
// Header line: "Current sub-board:"
colorArray["msgListHeaderSubBoardTextColor"] = "\1n\1" + "4\1c"; // Normal cyan on blue background
//colorArray["msgListHeaderSubBoardTextColor"] = "\1n\1" + "4\1w"; // Normal white on blue background
// Header line: Message sub-board name
colorArray["msgListHeaderMsgSubBoardName"] = "\1h\1c"; // High cyan
//colorArray["msgListHeaderMsgSubBoardName"] = "\1h\1w"; // High white
// Line with column headers
//colorArray["msgListColHeader"] = "\1h\1w"; // High white (keep blue background)
colorArray["msgListColHeader"] = "\1n\1h\1w"; // High white on black background
//colorArray["msgListColHeader"] = "\1h\1c"; // High cyan (keep blue background)
//colorArray["msgListColHeader"] = "\1" + "4\1h\1y"; // High yellow (keep blue background)
// Message list information
colorArray["msgListMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListFromColor"] = "\1n\1c";
colorArray["msgListToColor"] = "\1n\1c";
colorArray["msgListSubjectColor"] = "\1n\1c";
colorArray["msgListDateColor"] = "\1h\1b";
colorArray["msgListTimeColor"] = "\1h\1b";
// Message information for messages written to the user
colorArray["msgListToUserMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListToUserFromColor"] = "\1h\1g";
colorArray["msgListToUserToColor"] = "\1h\1g";
colorArray["msgListToUserSubjectColor"] = "\1h\1g";
colorArray["msgListToUserDateColor"] = "\1h\1b";
colorArray["msgListToUserTimeColor"] = "\1h\1b";
// Message information for messages from the user
colorArray["msgListFromUserMsgNumColor"] = "\1n\1h\1y";
colorArray["msgListFromUserFromColor"] = "\1n\1c";
colorArray["msgListFromUserToColor"] = "\1n\1c";
colorArray["msgListFromUserSubjectColor"] = "\1n\1c";
colorArray["msgListFromUserDateColor"] = "\1h\1b";
colorArray["msgListFromUserTimeColor"] = "\1h\1b";
// Message list highlight colors
colorArray["msgListHighlightBkgColor"] = "\1" + "4"; // Background
colorArray["msgListMsgNumHighlightColor"] = "\1h\1y";
colorArray["msgListFromHighlightColor"] = "\1h\1c";
colorArray["msgListToHighlightColor"] = "\1h\1c";
colorArray["msgListSubjHighlightColor"] = "\1h\1c";
colorArray["msgListDateHighlightColor"] = "\1h\1w";
colorArray["msgListTimeHighlightColor"] = "\1h\1w";
// Lightbar message list help line colors
colorArray["lightbarMsgListHelpLineBkgColor"] = "\1" + "7"; // Background
colorArray["lightbarMsgListHelpLineGeneralColor"] = "\1b";
colorArray["lightbarMsgListHelpLineHotkeyColor"] = "\1r";
colorArray["lightbarMsgListHelpLineParenColor"] = "\1m";
// Continue prompt colors
colorArray["tradInterfaceContPromptMainColor"] = "\1n\1g"; // Main text color
colorArray["tradInterfaceContPromptHotkeyColor"] = "\1h\1c"; // Hotkey color
colorArray["tradInterfaceContPromptUserInputColor"] = "\1h\1g"; // User input color
// Message body color
colorArray["msgBodyColor"] = "\1n\1w";
// Read message confirmation colors
colorArray["readMsgConfirmColor"] = "\1n\1c";
colorArray["readMsgConfirmNumberColor"] = "\1h\1c";
// Prompt for continuing to list messages after reading a message
colorArray["afterReadMsg_ListMorePromptColor"] = "\1n\1c";
// Help screen text color
colorArray["tradInterfaceHelpScreenColor"] = "\1n\1h\1w";