diff --git a/mail/base/content/mailWindowOverlay.js b/mail/base/content/mailWindowOverlay.js index d2203efefe6..dce593e7afc 100644 --- a/mail/base/content/mailWindowOverlay.js +++ b/mail/base/content/mailWindowOverlay.js @@ -493,24 +493,44 @@ function RemoveAllMessageTags() var selectedMsgUris = GetSelectedMessages(); if (!selectedMsgUris.length) return; - - var msg = Components.classes["@mozilla.org/supports-array;1"] - .createInstance(Components.interfaces.nsISupportsArray); - + + var messages = Components.classes["@mozilla.org/supports-array;1"] + .createInstance(Components.interfaces.nsISupportsArray); + var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"] + .getService(Components.interfaces.nsIMsgTagService); + var tagArray = tagService.getAllTags({}); + + var allKeys = ""; + for (j = 0; j < tagArray.length; ++j) + { + if (j) + allKeys += " "; + allKeys += tagArray[j].key; + } + + var prevHdrFolder = null; + // this crudely handles cross-folder virtual folders with selected messages + // that spans folders, by coalescing consecutive messages in the selection + // that happen to be in the same folder. nsMsgSearchDBView does this better, + // but nsIMsgDBView doesn't handle commands with arguments, and untag takes a + // key argument. Furthermore, we only delete legacy labels and known tags, + // keeping other keywords like (non)junk intact. + var j; for (var i = 0; i < selectedMsgUris.length; ++i) { - // remove all tags by removing all their tag keys, all at once. - // (using a msgHdr's setStringProperty won't notify the threadPane!) var msgHdr = messenger.msgHdrFromURI(selectedMsgUris[i]); msgHdr.label = 0; // remove legacy label - msg.Clear(); - msg.AppendElement(msgHdr); - - var keywords = msgHdr.getStringProperty("keywords"); - // this will remove all keywords at once... - if (keywords.length > 0) - msgHdr.folder.removeKeywordFromMessages(msg, keywords); + if (prevHdrFolder != msgHdr.folder) + { + if (prevHdrFolder) + prevHdrFolder.removeKeywordsFromMessages(messages, allKeys); + messages.Clear(); + prevHdrFolder = msgHdr.folder; + } + messages.AppendElement(msgHdr); } + if (prevHdrFolder) + prevHdrFolder.removeKeywordsFromMessages(messages, allKeys); OnTagsChange(); } @@ -554,7 +574,7 @@ function ToggleMessageTag(key, addKey) var msg = Components.classes["@mozilla.org/supports-array;1"] .createInstance(Components.interfaces.nsISupportsArray); var selectedMsgUris = GetSelectedMessages(); - var toggler = addKey ? "addKeywordToMessages" : "removeKeywordFromMessages"; + var toggler = addKey ? "addKeywordsToMessages" : "removeKeywordsFromMessages"; var prevHdrFolder = null; // this crudely handles cross-folder virtual folders with selected messages // that spans folders, by coalescing consecutive msgs in the selection @@ -571,7 +591,7 @@ function ToggleMessageTag(key, addKey) // because resetting a label doesn't update the tree anymore... msg.Clear(); msg.AppendElement(msgHdr); - msgHdr.folder.addKeywordToMessages(msg, "$label" + msgHdr.label); + msgHdr.folder.addKeywordsToMessages(msg, "$label" + msgHdr.label); msgHdr.label = 0; // remove legacy label } if (prevHdrFolder != msgHdr.folder) diff --git a/mailnews/base/public/nsIMsgFolder.idl b/mailnews/base/public/nsIMsgFolder.idl index 5c580d26e64..e0fb23d3833 100644 --- a/mailnews/base/public/nsIMsgFolder.idl +++ b/mailnews/base/public/nsIMsgFolder.idl @@ -71,7 +71,7 @@ typedef long nsMsgBiffState; // enumerated type for determining if a message has been replied to, forwarded, etc. typedef long nsMsgDispositionState; -[scriptable, uuid(913D683A-206F-4c91-9B10-3FC4CAEA644E)] +[scriptable, uuid(83d2454c-632e-4d6e-900a-7236a6d2aa9a)] interface nsIMsgFolder : nsICollection { const nsMsgBiffState nsMsgBiffState_NewMail = 0; // User has new mail waiting. @@ -495,9 +495,9 @@ const nsMsgBiffState nsMsgBiffState_Unknown = 2; // We dunno whether there is ne // used to set/clear tags - we could have a single method to setKeywords which // would figure out the diffs, but these methods might be more convenient. - void addKeywordToMessages(in nsISupportsArray aMessages, in string aKeyword); - void removeKeywordFromMessages(in nsISupportsArray aMessages, in string aKeyword); - + // keywords are space delimited, in the case of multiple keywords + void addKeywordsToMessages(in nsISupportsArray aMessages, in string aKeywords); + void removeKeywordsFromMessages(in nsISupportsArray aMessages, in string aKeywords); /** * Extract the message preview text from aStream, storing it as a string property * on aMsgHdr. diff --git a/mailnews/base/resources/content/mailWindowOverlay.js b/mailnews/base/resources/content/mailWindowOverlay.js index e3312dd9d91..1a4ae5d9182 100644 --- a/mailnews/base/resources/content/mailWindowOverlay.js +++ b/mailnews/base/resources/content/mailWindowOverlay.js @@ -473,16 +473,44 @@ function RemoveAllMessageTags() var selectedMsgUris = GetSelectedMessages(); if (!selectedMsgUris.length) return; - + + var messages = Components.classes["@mozilla.org/supports-array;1"] + .createInstance(Components.interfaces.nsISupportsArray); + var tagService = Components.classes["@mozilla.org/messenger/tagservice;1"] + .getService(Components.interfaces.nsIMsgTagService); + var tagArray = tagService.getAllTags({}); + + var allKeys = ""; + for (j = 0; j < tagArray.length; ++j) + { + if (j) + allKeys += " "; + allKeys += tagArray[j].key; + } + + var prevHdrFolder = null; + // this crudely handles cross-folder virtual folders with selected messages + // that spans folders, by coalescing consecutive messages in the selection + // that happen to be in the same folder. nsMsgSearchDBView does this better, + // but nsIMsgDBView doesn't handle commands with arguments, and untag takes a + // key argument. Furthermore, we only delete legacy labels and known tags, + // keeping other keywords like (non)junk intact. + var j; for (var i = 0; i < selectedMsgUris.length; ++i) { - // remove all tags by removing all their tag keys at once - // (using a msgHdr's setStringProperty won't notify the threadPane!) var msgHdr = messenger.msgHdrFromURI(selectedMsgUris[i]); msgHdr.label = 0; // remove legacy label - msgHdr.folder.getMsgDatabase(msgWindow) - .setStringProperty(msgHdr.messageKey, "keywords", ""); + if (prevHdrFolder != msgHdr.folder) + { + if (prevHdrFolder) + prevHdrFolder.removeKeywordsFromMessages(messages, allKeys); + messages.Clear(); + prevHdrFolder = msgHdr.folder; + } + messages.AppendElement(msgHdr); } + if (prevHdrFolder) + prevHdrFolder.removeKeywordsFromMessages(messages, allKeys); OnTagsChange(); } @@ -547,7 +575,7 @@ function ToggleMessageTag(key, addKey) var msg = Components.classes["@mozilla.org/supports-array;1"] .createInstance(Components.interfaces.nsISupportsArray); var selectedMsgUris = GetSelectedMessages(); - var toggler = addKey ? "addKeywordToMessages" : "removeKeywordFromMessages"; + var toggler = addKey ? "addKeywordsToMessages" : "removeKeywordsFromMessages"; var prevHdrFolder = null; // this crudely handles cross-folder virtual folders with selected messages // that spans folders, by coalescing consecutive msgs in the selection @@ -564,7 +592,7 @@ function ToggleMessageTag(key, addKey) // because resetting a label doesn't update the tree anymore... msg.Clear(); msg.AppendElement(msgHdr); - msgHdr.folder.addKeywordToMessages(msg, "$label" + msgHdr.label); + msgHdr.folder.addKeywordsToMessages(msg, "$label" + msgHdr.label); msgHdr.label = 0; // remove legacy label } if (prevHdrFolder != msgHdr.folder) diff --git a/mailnews/base/search/src/nsMsgFilterService.cpp b/mailnews/base/search/src/nsMsgFilterService.cpp index 4fe5a6a9b19..6ee7aee96c4 100644 --- a/mailnews/base/search/src/nsMsgFilterService.cpp +++ b/mailnews/base/search/src/nsMsgFilterService.cpp @@ -651,7 +651,7 @@ nsresult nsMsgFilterAfterTheFact::ApplyFilter() { nsXPIDLCString keyword; filterAction->GetStrValue(getter_Copies(keyword)); - m_curFolder->AddKeywordToMessages(m_searchHitHdrs, keyword.get()); + m_curFolder->AddKeywordsToMessages(m_searchHitHdrs, keyword.get()); } break; case nsMsgFilterAction::JunkScore: diff --git a/mailnews/base/util/nsMsgDBFolder.cpp b/mailnews/base/util/nsMsgDBFolder.cpp index 2bfbcd74c3a..a73f31b6084 100644 --- a/mailnews/base/util/nsMsgDBFolder.cpp +++ b/mailnews/base/util/nsMsgDBFolder.cpp @@ -5469,7 +5469,7 @@ void nsMsgDBFolder::SetMRUTime() } -NS_IMETHODIMP nsMsgDBFolder::AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword) +NS_IMETHODIMP nsMsgDBFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords) { nsresult rv = NS_OK; GetDatabase(nsnull); @@ -5489,20 +5489,25 @@ NS_IMETHODIMP nsMsgDBFolder::AddKeywordToMessages(nsISupportsArray *aMessages, c (void) message->GetMessageKey(&msgKey); message->GetStringProperty("keywords", getter_Copies(keywords)); - nsACString::const_iterator start, end; - if (!MsgFindKeyword(nsDependentCString(aKeyword), keywords, start, end)) + nsCStringArray keywordArray; + keywordArray.ParseString(aKeywords, " "); + for (PRInt32 j = 0; j < keywordArray.Count(); j++) { - if (!keywords.IsEmpty()) - keywords.Append(' '); - keywords.Append(aKeyword); - mDatabase->SetStringProperty(msgKey, "keywords", keywords); + nsACString::const_iterator start, end; + if (!MsgFindKeyword(*(keywordArray[j]), keywords, start, end)) + { + if (!keywords.IsEmpty()) + keywords.Append(' '); + keywords.Append(keywordArray[j]->get()); + } } + mDatabase->SetStringProperty(msgKey, "keywords", keywords); } } return rv; } -NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword) +NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords) { nsresult rv = NS_OK; GetDatabase(nsnull); @@ -5514,7 +5519,6 @@ NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessag NS_ENSURE_SUCCESS(rv, rv); nsXPIDLCString keywords; // If the tag is also a label, we should remove the label too... - PRBool keywordIsLabel = (!PL_strncasecmp(aKeyword, "$label", 6) && aKeyword[6] >= '1' && aKeyword[6] <= '5'); for(PRUint32 i = 0; i < count; i++) { @@ -5522,31 +5526,38 @@ NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessag nsCOMPtr message = do_QueryElementAt(aMessages, i, &rv); NS_ENSURE_SUCCESS(rv, rv); (void) message->GetMessageKey(&msgKey); - if (keywordIsLabel) - { - nsMsgLabelValue labelValue; - message->GetLabel(&labelValue); - // if we're removing the keyword that corresponds to a pre 2.0 label, - // we need to clear the old label attribute on the messsage. - if (labelValue == (nsMsgLabelValue) (aKeyword[6] - '0')) - message->SetLabel((nsMsgLabelValue) 0); - } - rv = message->GetStringProperty("keywords", getter_Copies(keywords)); - nsACString::const_iterator start, end; - nsACString::const_iterator saveStart; - keywords.BeginReading(saveStart); - if (MsgFindKeyword(nsDependentCString(aKeyword), keywords, start, end)) + nsCStringArray keywordArray; + keywordArray.ParseString(aKeywords, " "); + for (PRInt32 j = 0; j < keywordArray.Count(); j++) { - keywords.Cut(Distance(saveStart, start), Distance(start, end)); - mDatabase->SetStringProperty(msgKey, "keywords", keywords); + PRBool keywordIsLabel = (StringBeginsWith(*(keywordArray[j]), NS_LITERAL_CSTRING("$label")) + && keywordArray[j]->CharAt(6) >= '1' && keywordArray[j]->CharAt(6) <= '5'); + if (keywordIsLabel) + { + nsMsgLabelValue labelValue; + message->GetLabel(&labelValue); + // if we're removing the keyword that corresponds to a pre 2.0 label, + // we need to clear the old label attribute on the messsage. + if (labelValue == (nsMsgLabelValue) (keywordArray[j]->CharAt(6) - '0')) + message->SetLabel((nsMsgLabelValue) 0); + } + + nsACString::const_iterator start, end; + nsACString::const_iterator saveStart; + keywords.BeginReading(saveStart); + if (MsgFindKeyword(*(keywordArray[j]), keywords, start, end)) + { + keywords.Cut(Distance(saveStart, start), Distance(start, end)); + NS_ASSERTION(keywords.IsEmpty() || keywords.CharAt(0) != ' ', "space only keyword"); + } } + mDatabase->SetStringProperty(msgKey, "keywords", keywords); } } return rv; } - NS_IMETHODIMP nsMsgDBFolder::GetCustomIdentity(nsIMsgIdentity **aIdentity) { NS_ENSURE_ARG_POINTER(aIdentity); diff --git a/mailnews/imap/src/nsImapMailFolder.cpp b/mailnews/imap/src/nsImapMailFolder.cpp index bccc08b625c..e7b58714554 100644 --- a/mailnews/imap/src/nsImapMailFolder.cpp +++ b/mailnews/imap/src/nsImapMailFolder.cpp @@ -3478,7 +3478,7 @@ NS_IMETHODIMP nsImapMailFolder::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWindo nsCOMPtr messageArray; NS_NewISupportsArray(getter_AddRefs(messageArray)); messageArray->AppendElement(msgHdr); - AddKeywordToMessages(messageArray, keyword.get()); + AddKeywordsToMessages(messageArray, keyword.get()); break; } case nsMsgFilterAction::JunkScore: @@ -8601,32 +8601,32 @@ NS_IMETHODIMP nsImapMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUi return NS_OK; } -NS_IMETHODIMP nsImapMailFolder::AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword) +NS_IMETHODIMP nsImapMailFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords) { - nsresult rv = nsMsgDBFolder::AddKeywordToMessages(aMessages, aKeyword); + nsresult rv = nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keys; rv = BuildIdsAndKeyArray(aMessages, messageIds, keys); NS_ENSURE_SUCCESS(rv, rv); - rv = StoreCustomKeywords(nsnull, aKeyword, nsnull, keys.GetArray(), keys.GetSize(), nsnull); + rv = StoreCustomKeywords(nsnull, aKeywords, nsnull, keys.GetArray(), keys.GetSize(), nsnull); if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } -NS_IMETHODIMP nsImapMailFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword) +NS_IMETHODIMP nsImapMailFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords) { - nsresult rv = nsMsgDBFolder::RemoveKeywordFromMessages(aMessages, aKeyword); + nsresult rv = nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keys; nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keys); NS_ENSURE_SUCCESS(rv, rv); - rv = StoreCustomKeywords(nsnull, nsnull, aKeyword, keys.GetArray(), keys.GetSize(), nsnull); + rv = StoreCustomKeywords(nsnull, nsnull, aKeywords, keys.GetArray(), keys.GetSize(), nsnull); if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } diff --git a/mailnews/imap/src/nsImapMailFolder.h b/mailnews/imap/src/nsImapMailFolder.h index c6c8e9a9b16..0b10575a390 100644 --- a/mailnews/imap/src/nsImapMailFolder.h +++ b/mailnews/imap/src/nsImapMailFolder.h @@ -294,8 +294,8 @@ public: PRBool aLocalOnly, nsIUrlListener *aUrlListener, PRBool *aAsyncResults); - NS_IMETHOD AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword); - NS_IMETHOD RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword); + NS_IMETHOD AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords); + NS_IMETHOD RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords); // nsIMsgImapMailFolder methods NS_DECL_NSIMSGIMAPMAILFOLDER diff --git a/mailnews/local/src/nsLocalMailFolder.cpp b/mailnews/local/src/nsLocalMailFolder.cpp index 58a51dbf5a8..b1ff0d2ce85 100644 --- a/mailnews/local/src/nsLocalMailFolder.cpp +++ b/mailnews/local/src/nsLocalMailFolder.cpp @@ -3921,14 +3921,14 @@ NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, return rv; } -NS_IMETHODIMP nsMsgLocalMailFolder::AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword) +NS_IMETHODIMP nsMsgLocalMailFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords) { - return ChangeKeywordForMessages(aMessages, aKeyword, PR_TRUE /* add */); + return ChangeKeywordForMessages(aMessages, aKeywords, PR_TRUE /* add */); } -nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessages, const char *aKeyword, PRBool add) +nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessages, const char *aKeywords, PRBool add) { - nsresult rv = (add) ? nsMsgDBFolder::AddKeywordToMessages(aMessages, aKeyword) - : nsMsgDBFolder::RemoveKeywordFromMessages(aMessages, aKeyword); + nsresult rv = (add) ? nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords) + : nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords); if (NS_SUCCEEDED(rv)) { @@ -3945,8 +3945,6 @@ nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessa nsresult rv = aMessages->Count(&count); NS_ENSURE_SUCCESS(rv, rv); nsXPIDLCString keywords; - nsCAutoString keywordToWrite(" "); - keywordToWrite.Append(aKeyword); // for each message, we seek to the beginning of the x-mozilla-status header, and // start reading lines, looking for x-mozilla-keys: headers; If we're adding // the keyword and we find @@ -3968,85 +3966,94 @@ nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessa nsCOMPtr message = do_QueryElementAt(aMessages, i, &rv); NS_ENSURE_SUCCESS(rv, rv); PRUint32 messageOffset; - PRUint32 len = 0; - nsCAutoString header; - nsCAutoString keywords; message->GetMessageOffset(&messageOffset); - PRBool done = PR_FALSE; PRUint32 statusOffset = 0; (void)message->GetStatusOffset(&statusOffset); PRUint32 desiredOffset = messageOffset + statusOffset; - fileStream->seek(PR_SEEK_SET, desiredOffset); - PRBool inKeywordHeader = PR_FALSE; - PRBool foundKeyword = PR_FALSE; - PRUint32 offsetToAddKeyword = 0; - message->GetMessageSize(&len); - // loop through - while (!done) - { - lineBuff[0] = '\0'; - PRInt32 lineStartPos = fileStream->tell(); - // readLine won't return line termination chars. - if (fileStream->readline(lineBuff, sizeof(lineBuff))) - { - if (EMPTY_MESSAGE_LINE(lineBuff)) - break; // passed headers; no x-mozilla-keywords header; give up. - nsCString keywordHeaders; - if (!strncmp(lineBuff, HEADER_X_MOZILLA_KEYWORDS, sizeof(HEADER_X_MOZILLA_KEYWORDS) - 1)) - { - inKeywordHeader = PR_TRUE; - keywordHeaders = lineBuff; - } - else if (inKeywordHeader && (lineBuff[0] == ' ' || lineBuff[0] == '\t')) - keywordHeaders = lineBuff; - else if (inKeywordHeader) - break; - else - continue; - PRInt32 keywordHdrLength = keywordHeaders.Length(); - nsACString::const_iterator start, end; - nsACString::const_iterator keywordHdrStart; - keywordHeaders.BeginReading(keywordHdrStart); - // check if we have the keyword - if (MsgFindKeyword(nsDependentCString(aKeyword), keywordHeaders, start, end)) + nsCStringArray keywordArray; + keywordArray.ParseString(aKeywords, " "); + for (PRInt32 j = 0; j < keywordArray.Count(); j++) + { + nsCAutoString header; + nsCAutoString keywords; + PRBool done = PR_FALSE; + PRUint32 len = 0; + nsCAutoString keywordToWrite(" "); + + keywordToWrite.Append(*(keywordArray[j])); + fileStream->seek(PR_SEEK_SET, desiredOffset); + PRBool inKeywordHeader = PR_FALSE; + PRBool foundKeyword = PR_FALSE; + PRUint32 offsetToAddKeyword = 0; + message->GetMessageSize(&len); + // loop through + while (!done) + { + lineBuff[0] = '\0'; + PRInt32 lineStartPos = fileStream->tell(); + // readLine won't return line termination chars. + if (fileStream->readline(lineBuff, sizeof(lineBuff))) { - foundKeyword = PR_TRUE; - if (!add) // if we're removing, remove it, and break; + if (EMPTY_MESSAGE_LINE(lineBuff)) + break; // passed headers; no x-mozilla-keywords header; give up. + nsCString keywordHeaders; + if (!strncmp(lineBuff, HEADER_X_MOZILLA_KEYWORDS, sizeof(HEADER_X_MOZILLA_KEYWORDS) - 1)) { - PRInt32 keywordStartOffset = Distance(keywordHdrStart, start); - keywordHeaders.Cut(keywordStartOffset, Distance(start, end)); - for (PRInt32 i = Distance(start, end); i > 0; i--) - keywordHeaders.Append(' '); - fileStream->seek(PR_SEEK_SET, lineStartPos); - fileStream->write(keywordHeaders.get(), keywordHeaders.Length()); + inKeywordHeader = PR_TRUE; + keywordHeaders = lineBuff; + } + else if (inKeywordHeader && (lineBuff[0] == ' ' || lineBuff[0] == '\t')) + keywordHeaders = lineBuff; + else if (inKeywordHeader) + break; + else + continue; + + PRInt32 keywordHdrLength = keywordHeaders.Length(); + nsACString::const_iterator start, end; + nsACString::const_iterator keywordHdrStart; + keywordHeaders.BeginReading(keywordHdrStart); + // check if we have the keyword + if (MsgFindKeyword(*(keywordArray[j]), keywordHeaders, start, end)) + { + foundKeyword = PR_TRUE; + if (!add) // if we're removing, remove it, and break; + { + PRInt32 keywordStartOffset = Distance(keywordHdrStart, start); + keywordHeaders.Cut(keywordStartOffset, Distance(start, end)); + for (PRInt32 i = Distance(start, end); i > 0; i--) + keywordHeaders.Append(' '); + fileStream->seek(PR_SEEK_SET, lineStartPos); + fileStream->write(keywordHeaders.get(), keywordHeaders.Length()); + } + offsetToAddKeyword = 0; + // if adding and we already have the keyword, done + done = PR_TRUE; + break; + } + // argh, we need to check all the lines to see if we already have the + // keyword, but if we don't find it, we want to remember the line and + // position where we have room to add the keyword. + if (add) + { + nsCAutoString curKeywordHdr(lineBuff); + // strip off line ending spaces. + curKeywordHdr.Trim(" ", PR_FALSE, PR_TRUE); + if (!offsetToAddKeyword && curKeywordHdr.Length() + keywordToWrite.Length() < keywordHdrLength) + offsetToAddKeyword = lineStartPos + curKeywordHdr.Length(); } - offsetToAddKeyword = 0; - // if adding and we already have the keyword, done - done = PR_TRUE; - break; - } - // argh, we need to check all the lines to see if we already have the - // keyword, but if we don't find it, we want to remember the line and - // position where we have room to add the keyword. - if (add) - { - nsCAutoString curKeywordHdr(lineBuff); - // strip off line ending spaces. - curKeywordHdr.Trim(" ", PR_FALSE, PR_TRUE); - if (!offsetToAddKeyword && curKeywordHdr.Length() + keywordToWrite.Length() < keywordHdrLength) - offsetToAddKeyword = lineStartPos + curKeywordHdr.Length(); } } - } - if (add && !foundKeyword) - { - if (!offsetToAddKeyword) - message->SetUint32Property("growKeywords", 1); - else + if (add && !foundKeyword) { - fileStream->seek(PR_SEEK_SET, offsetToAddKeyword); - fileStream->write(keywordToWrite.get(), keywordToWrite.Length()); + if (!offsetToAddKeyword) + message->SetUint32Property("growKeywords", 1); + else + { + fileStream->seek(PR_SEEK_SET, offsetToAddKeyword); + fileStream->write(keywordToWrite.get(), keywordToWrite.Length()); + } } } } @@ -4055,7 +4062,7 @@ nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsISupportsArray *aMessa return rv; } -NS_IMETHODIMP nsMsgLocalMailFolder::RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword) +NS_IMETHODIMP nsMsgLocalMailFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords) { - return ChangeKeywordForMessages(aMessages, aKeyword, PR_FALSE /* remove */); + return ChangeKeywordForMessages(aMessages, aKeywords, PR_FALSE /* remove */); } diff --git a/mailnews/local/src/nsLocalMailFolder.h b/mailnews/local/src/nsLocalMailFolder.h index d7006c54532..2cf77b0a543 100644 --- a/mailnews/local/src/nsLocalMailFolder.h +++ b/mailnews/local/src/nsLocalMailFolder.h @@ -197,8 +197,8 @@ public: NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, PRBool aLocalOnly, nsIUrlListener *aUrlListener, PRBool *aAsyncResults); - NS_IMETHOD AddKeywordToMessages(nsISupportsArray *aMessages, const char *aKeyword); - NS_IMETHOD RemoveKeywordFromMessages(nsISupportsArray *aMessages, const char *aKeyword); + NS_IMETHOD AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords); + NS_IMETHOD RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords); protected: nsresult CopyFolderAcrossServer(nsIMsgFolder *srcFolder, nsIMsgWindow *msgWindow,nsIMsgCopyServiceListener* listener); diff --git a/mailnews/local/src/nsParseMailbox.cpp b/mailnews/local/src/nsParseMailbox.cpp index 452f6ce8dec..d0f992c1ac9 100644 --- a/mailnews/local/src/nsParseMailbox.cpp +++ b/mailnews/local/src/nsParseMailbox.cpp @@ -1959,7 +1959,7 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi nsCOMPtr messageArray; NS_NewISupportsArray(getter_AddRefs(messageArray)); messageArray->AppendElement(msgHdr); - m_downloadFolder->AddKeywordToMessages(messageArray, keyword.get()); + m_downloadFolder->AddKeywordsToMessages(messageArray, keyword.get()); break; } case nsMsgFilterAction::Label: diff --git a/mailnews/local/src/nsPop3Protocol.cpp b/mailnews/local/src/nsPop3Protocol.cpp index 87897069cc3..686e62cfe9b 100644 --- a/mailnews/local/src/nsPop3Protocol.cpp +++ b/mailnews/local/src/nsPop3Protocol.cpp @@ -402,6 +402,12 @@ net_pop3_write_state(Pop3UidlHost* host, nsIFileSpec *mailDirectory) nsFileSpec fileSpec; mailDirectory->GetFileSpec(&fileSpec); fileSpec += "popstate.dat"; +#ifdef DEBUG_David_Bienvenu + PRUint32 fileSize = fileSpec.GetFileSize(); + // check if popstate.dat is getting emptied out. + if (!host || (hash_empty(host->hash) && fileSize > 80)) + NS_ASSERTION(PR_FALSE, "bad/empty pop3 uidl hash table"); +#endif nsOutputFileStream outFileStream(fileSpec, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE); diff --git a/mailnews/news/src/nsNNTPNewsgroupList.cpp b/mailnews/news/src/nsNNTPNewsgroupList.cpp index 33cfb063d94..0d141cf242e 100644 --- a/mailnews/news/src/nsNNTPNewsgroupList.cpp +++ b/mailnews/news/src/nsNNTPNewsgroupList.cpp @@ -846,7 +846,7 @@ NS_IMETHODIMP nsNNTPNewsgroupList::ApplyFilterHit(nsIMsgFilter *aFilter, nsIMsgW messageArray->AppendElement(m_newMsgHdr); nsCOMPtr folder = do_QueryInterface(m_newsFolder, &rv); if (folder) - folder->AddKeywordToMessages(messageArray, keyword.get()); + folder->AddKeywordsToMessages(messageArray, keyword.get()); break; } case nsMsgFilterAction::Label: