add support for dealing with duplicate incoming messages, 9413 sr=mscott
git-svn-id: svn://10.0.0.236/trunk@179303 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
d4fdf966bd
commit
5539f9a6b0
@ -49,14 +49,14 @@ interface nsIMsgDownloadSettings;
|
||||
interface nsISpamSettings;
|
||||
interface nsIMsgFilterPlugin;
|
||||
interface nsIUrlListener;
|
||||
|
||||
interface nsIMsgDBHdr;
|
||||
/*
|
||||
* Interface for incoming mail/news host
|
||||
* this is the base interface for all mail server types (imap, pop, nntp, etc)
|
||||
* often you will want to add extra interfaces that give you server-specific
|
||||
* attributes and methods.
|
||||
*/
|
||||
[scriptable, uuid(dc2f06f9-0ee8-47ad-901e-168d14323576)]
|
||||
[scriptable, uuid(e0446c10-1bb1-4282-be4b-7fdadce4717a)]
|
||||
interface nsIMsgIncomingServer : nsISupports {
|
||||
|
||||
/**
|
||||
@ -407,6 +407,16 @@ interface nsIMsgIncomingServer : nsISupports {
|
||||
nsIMsgFolder getMsgFolderFromURI(in nsIMsgFolder aFolderResource, in string aURI);
|
||||
|
||||
readonly attribute boolean isDeferredTo;
|
||||
|
||||
const long keepDups = 0;
|
||||
const long deleteDups = 1;
|
||||
const long moveDupsToTrash = 2;
|
||||
const long markDupsRead = 3;
|
||||
|
||||
attribute long incomingDuplicateAction;
|
||||
|
||||
// check if new hdr is a duplicate of a recently arrived header
|
||||
boolean isNewHdrDuplicate(in nsIMsgDBHdr aNewHdr);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsIStringBundle.h"
|
||||
|
||||
#include "nsIMsgHdr.h"
|
||||
#include "nsIRDFService.h"
|
||||
#include "nsRDFCID.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
@ -91,6 +91,7 @@ nsMsgIncomingServer::nsMsgIncomingServer():
|
||||
m_prefBranch(0),
|
||||
m_biffState(nsIMsgFolder::nsMsgBiffState_NoMail),
|
||||
m_serverBusy(PR_FALSE),
|
||||
m_numMsgsDownloaded(0),
|
||||
m_canHaveFilters(PR_TRUE),
|
||||
m_displayStartupPage(PR_TRUE),
|
||||
mPerformingBiff(PR_FALSE)
|
||||
@ -1752,6 +1753,8 @@ NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer,
|
||||
|
||||
NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, MaxMessageSize, "max_size")
|
||||
|
||||
NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, IncomingDuplicateAction, "dup_action");
|
||||
|
||||
NS_IMETHODIMP nsMsgIncomingServer::SetUnicharAttribute(const char *aName, const PRUnichar *val)
|
||||
{
|
||||
return SetUnicharValue(aName, val);
|
||||
@ -2515,3 +2518,52 @@ NS_IMETHODIMP nsMsgIncomingServer::GetIsDeferredTo(PRBool *aIsDeferredTo)
|
||||
*aIsDeferredTo = PR_FALSE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
const long kMaxDownloadTableSize = 500;
|
||||
|
||||
// aData is the server, from that we get the cutoff point, below which we evict
|
||||
// element is the arrival index of the msg.
|
||||
/* static */PRBool nsMsgIncomingServer::evictOldEntries(nsHashKey *aKey, void *element, void *aData)
|
||||
{
|
||||
nsMsgIncomingServer *server = (nsMsgIncomingServer *)aData;
|
||||
if ((PRInt32) element < server->m_numMsgsDownloaded - kMaxDownloadTableSize/2)
|
||||
return kHashEnumerateRemove;
|
||||
return server->m_downloadedHdrs.Count() > kMaxDownloadTableSize/2;
|
||||
}
|
||||
|
||||
// hash the concatenation of the message-id and subject as the hash table key,
|
||||
// and store the arrival index as the value. To limit the size of the hash table,
|
||||
// we just throw out ones with a lower ordinal value than the cut-off point.
|
||||
NS_IMETHODIMP nsMsgIncomingServer::IsNewHdrDuplicate(nsIMsgDBHdr *aNewHdr, PRBool *aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
*aResult = PR_FALSE;
|
||||
nsCAutoString strHashKey;
|
||||
nsXPIDLCString messageId, subject;
|
||||
aNewHdr->GetMessageId(getter_Copies(messageId));
|
||||
strHashKey.Append(messageId);
|
||||
aNewHdr->GetSubject(getter_Copies(subject));
|
||||
// err on the side of caution and ignore messages w/o subject or messageid.
|
||||
if (subject.IsEmpty() || messageId.IsEmpty())
|
||||
return NS_OK;
|
||||
strHashKey.Append(subject);
|
||||
nsCStringKey hashKey(strHashKey);
|
||||
PRInt32 hashValue = (PRInt32) m_downloadedHdrs.Get(&hashKey);
|
||||
if (hashValue)
|
||||
{
|
||||
*aResult = PR_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we store the current size of the hash table as the hash
|
||||
// value - this allows us to delete older entries.
|
||||
m_downloadedHdrs.Put(&hashKey, (void *) ++m_numMsgsDownloaded);
|
||||
// Check if hash table is larger than some reasonable size
|
||||
// and if is it, iterate over hash table deleting messages
|
||||
// with an arrival index < number of msgs downloaded - half the reasonable size.
|
||||
if (m_downloadedHdrs.Count() >= kMaxDownloadTableSize)
|
||||
m_downloadedHdrs.Enumerate(evictOldEntries, this);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@
|
||||
#include "nsIFileSpec.h"
|
||||
#include "nsISpamSettings.h"
|
||||
#include "nsIMsgFilterPlugin.h"
|
||||
#include "nsHashtable.h"
|
||||
|
||||
class nsIMsgFolderCache;
|
||||
class nsIMsgProtocolInfo;
|
||||
@ -106,7 +107,10 @@ protected:
|
||||
nsresult getProtocolInfo(nsIMsgProtocolInfo **aResult);
|
||||
nsCOMPtr <nsIFileSpec> mFilterFile;
|
||||
nsCOMPtr <nsIMsgFilterList> mFilterList;
|
||||
|
||||
// these allow us to handle duplicate incoming messages, e.g. delete them.
|
||||
nsHashtable m_downloadedHdrs;
|
||||
PRInt32 m_numMsgsDownloaded;
|
||||
static PRBool evictOldEntries(nsHashKey *aKey, void *element, void *aData);
|
||||
private:
|
||||
nsIPrefBranch *m_prefBranch;
|
||||
nsCString m_password;
|
||||
|
||||
@ -2909,6 +2909,20 @@ nsresult nsImapMailFolder::NormalEndHeaderParseStream(nsIImapProtocol*
|
||||
char *headers;
|
||||
PRInt32 headersSize;
|
||||
|
||||
nsCOMPtr <nsIMsgWindow> msgWindow;
|
||||
if (aProtocol)
|
||||
{
|
||||
nsCOMPtr <nsIImapUrl> aImapUrl;
|
||||
nsCOMPtr <nsIMsgMailNewsUrl> msgUrl;
|
||||
rv = aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
|
||||
if (NS_SUCCEEDED(rv) && aImapUrl)
|
||||
{
|
||||
msgUrl = do_QueryInterface(aImapUrl);
|
||||
if (msgUrl)
|
||||
msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
|
||||
}
|
||||
|
||||
}
|
||||
nsCOMPtr<nsIMsgIncomingServer> server;
|
||||
rv = GetServer(getter_AddRefs(server));
|
||||
if (NS_SUCCEEDED(rv)) // don't use NS_ENSURE_SUCCESS here; it's not a fatal error
|
||||
@ -2934,26 +2948,69 @@ nsresult nsImapMailFolder::NormalEndHeaderParseStream(nsIImapProtocol*
|
||||
newMsgHdr->GetFlags(&msgFlags);
|
||||
if (!(msgFlags & (MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED))) // only fire on unread msgs that haven't been deleted
|
||||
{
|
||||
PRInt32 duplicateAction = nsIMsgIncomingServer::keepDups;
|
||||
if (server)
|
||||
server->GetIncomingDuplicateAction(&duplicateAction);
|
||||
if (duplicateAction != nsIMsgIncomingServer::keepDups)
|
||||
{
|
||||
PRBool isDup;
|
||||
server->IsNewHdrDuplicate(newMsgHdr, &isDup);
|
||||
if (isDup)
|
||||
{
|
||||
// we want to do something similar to applying filter hits.
|
||||
// if a dup is marked read, it shouldn't trigger biff.
|
||||
// Same for deleting it or moving it to trash.
|
||||
switch (duplicateAction)
|
||||
{
|
||||
case nsIMsgIncomingServer::deleteDups:
|
||||
{
|
||||
PRUint32 newFlags;
|
||||
newMsgHdr->OrFlags(MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED, &newFlags);
|
||||
nsMsgKeyArray keysToFlag;
|
||||
|
||||
keysToFlag.Add(m_curMsgUid);
|
||||
StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, PR_TRUE, keysToFlag.GetArray(),
|
||||
keysToFlag.GetSize(), nsnull);
|
||||
m_msgMovedByFilter = PR_TRUE;
|
||||
|
||||
}
|
||||
break;
|
||||
case nsIMsgIncomingServer::moveDupsToTrash:
|
||||
{
|
||||
nsCOMPtr <nsIMsgFolder> trash;
|
||||
GetTrashFolder(getter_AddRefs(trash));
|
||||
if (trash)
|
||||
{
|
||||
nsXPIDLCString trashUri;
|
||||
trash->GetURI(getter_Copies(trashUri));
|
||||
nsresult err = MoveIncorporatedMessage(newMsgHdr, mDatabase, trashUri, nsnull, msgWindow);
|
||||
if (NS_SUCCEEDED(err))
|
||||
m_msgMovedByFilter = PR_TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case nsIMsgIncomingServer::markDupsRead:
|
||||
{
|
||||
PRUint32 newFlags;
|
||||
nsMsgKeyArray keysToFlag;
|
||||
keysToFlag.Add(m_curMsgUid);
|
||||
newMsgHdr->OrFlags(MSG_FLAG_READ, &newFlags);
|
||||
StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull);
|
||||
}
|
||||
break;
|
||||
}
|
||||
PRInt32 numNewMessages;
|
||||
GetNumNewMessages(PR_FALSE, &numNewMessages);
|
||||
SetNumNewMessages(numNewMessages - 1);
|
||||
|
||||
}
|
||||
}
|
||||
rv = m_msgParser->GetAllHeaders(&headers, &headersSize);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && headers)
|
||||
if (NS_SUCCEEDED(rv) && headers && !m_msgMovedByFilter)
|
||||
{
|
||||
if (m_filterList)
|
||||
{
|
||||
nsCOMPtr <nsIMsgWindow> msgWindow;
|
||||
if (aProtocol)
|
||||
{
|
||||
nsCOMPtr <nsIImapUrl> aImapUrl;
|
||||
nsCOMPtr <nsIMsgMailNewsUrl> msgUrl;
|
||||
rv = aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
|
||||
if (NS_SUCCEEDED(rv) && aImapUrl)
|
||||
{
|
||||
msgUrl = do_QueryInterface(aImapUrl);
|
||||
if (msgUrl)
|
||||
msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
|
||||
}
|
||||
|
||||
}
|
||||
GetMoveCoalescer(); // not sure why we're doing this here.
|
||||
m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr, this, mDatabase,
|
||||
headers, headersSize, this, msgWindow);
|
||||
@ -3789,7 +3846,7 @@ nsresult nsImapMailFolder::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
|
||||
destIFolder->GetParent(getter_AddRefs(parentFolder));
|
||||
if (parentFolder)
|
||||
destIFolder->GetCanFileMessages(&canFileMessages);
|
||||
if (!parentFolder || !canFileMessages)
|
||||
if (filter && (!parentFolder || !canFileMessages))
|
||||
{
|
||||
filter->SetEnabled(PR_FALSE);
|
||||
m_filterList->SaveToDefaultFile();
|
||||
|
||||
@ -249,7 +249,6 @@ public:
|
||||
NS_IMETHOD MarkThreadRead(nsIMsgThread *thread);
|
||||
NS_IMETHOD SetLabelForMessages(nsISupportsArray *aMessages, nsMsgLabelValue aLabel);
|
||||
NS_IMETHOD SetJunkScoreForMessages(nsISupportsArray *aMessages, const char *aJunkScore);
|
||||
|
||||
NS_IMETHOD DeleteSubFolders(nsISupportsArray *folders, nsIMsgWindow *msgWindow);
|
||||
NS_IMETHOD ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element);
|
||||
NS_IMETHOD WriteToFolderCacheElem(nsIMsgFolderCacheElement *element);
|
||||
|
||||
@ -1561,6 +1561,65 @@ PRInt32 nsParseNewMailState::PublishMsgHeader(nsIMsgWindow *msgWindow)
|
||||
m_inboxFileStream->flush();
|
||||
PRUint32 msgOffset;
|
||||
(void) m_newMsgHdr->GetMessageOffset(&msgOffset);
|
||||
|
||||
nsCOMPtr<nsIMsgIncomingServer> server;
|
||||
nsresult rv = m_rootFolder->GetServer(getter_AddRefs(server));
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
PRInt32 duplicateAction;
|
||||
server->GetIncomingDuplicateAction(&duplicateAction);
|
||||
if (duplicateAction != nsIMsgIncomingServer::keepDups)
|
||||
{
|
||||
PRBool isDup;
|
||||
server->IsNewHdrDuplicate(m_newMsgHdr, &isDup);
|
||||
if (isDup)
|
||||
{
|
||||
// we want to do something similar to applying filter hits.
|
||||
// if a dup is marked read, it shouldn't trigger biff.
|
||||
// Same for deleting it or moving it to trash.
|
||||
switch (duplicateAction)
|
||||
{
|
||||
case nsIMsgIncomingServer::deleteDups:
|
||||
{
|
||||
m_inboxFileStream->close();
|
||||
|
||||
nsresult truncRet = m_inboxFileSpec.Truncate(msgOffset);
|
||||
NS_ASSERTION(NS_SUCCEEDED(truncRet), "unable to truncate file");
|
||||
if (NS_FAILED(truncRet))
|
||||
m_rootFolder->ThrowAlertMsg("dupDeleteFolderTruncateFailed", msgWindow);
|
||||
|
||||
// need to re-open the inbox file stream.
|
||||
m_inboxFileStream->Open(m_inboxFileSpec, (PR_RDWR | PR_CREATE_FILE));
|
||||
if (m_inboxFileStream)
|
||||
m_inboxFileStream->seek(m_inboxFileSpec.GetFileSize());
|
||||
|
||||
m_mailDB->RemoveHeaderMdbRow(m_newMsgHdr);
|
||||
// tell parser we've truncated the inbox.
|
||||
nsParseMailMessageState::Init(msgOffset);
|
||||
|
||||
}
|
||||
break;
|
||||
case nsIMsgIncomingServer::moveDupsToTrash:
|
||||
{
|
||||
nsCOMPtr <nsIMsgFolder> trash;
|
||||
GetTrashFolder(getter_AddRefs(trash));
|
||||
if (trash)
|
||||
MoveIncorporatedMessage(m_newMsgHdr, m_mailDB, trash,
|
||||
nsnull, msgWindow);
|
||||
}
|
||||
break;
|
||||
case nsIMsgIncomingServer::markDupsRead:
|
||||
MarkFilteredMessageRead(m_newMsgHdr);
|
||||
break;
|
||||
}
|
||||
PRInt32 numNewMessages;
|
||||
m_downloadFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
|
||||
m_downloadFolder->SetNumNewMessages(numNewMessages - 1);
|
||||
|
||||
m_newMsgHdr = nsnull;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ApplyFilters(&moved, msgWindow, msgOffset);
|
||||
}
|
||||
if (!moved)
|
||||
@ -1854,7 +1913,7 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
|
||||
break;
|
||||
case nsMsgFilterAction::FetchBodyFromPop3Server:
|
||||
{
|
||||
PRUint32 flags = 0;
|
||||
PRUint32 flags = 0;
|
||||
nsCOMPtr <nsIMsgFolder> downloadFolder;
|
||||
msgHdr->GetFolder(getter_AddRefs(downloadFolder));
|
||||
nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(downloadFolder);
|
||||
@ -1867,13 +1926,13 @@ NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWi
|
||||
nsCOMPtr<nsISupports> iSupports = do_QueryInterface(msgHdr);
|
||||
messages->AppendElement(iSupports);
|
||||
localFolder->MarkMsgsOnPop3Server(messages, POP3_FETCH_BODY);
|
||||
// Don't add this header to the DB, we're going to replace it
|
||||
// with the full message.
|
||||
// Don't add this header to the DB, we're going to replace it
|
||||
// with the full message.
|
||||
m_msgMovedByFilter = PR_TRUE;
|
||||
msgIsNew = PR_FALSE;
|
||||
// Don't do anything else in this filter, wait until we
|
||||
// have the full message.
|
||||
*applyMore = PR_FALSE;
|
||||
// Don't do anything else in this filter, wait until we
|
||||
// have the full message.
|
||||
*applyMore = PR_FALSE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -2052,11 +2111,14 @@ nsresult nsParseNewMailState::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
|
||||
destIFolder->GetCanFileMessages(&canFileMessages);
|
||||
if (!parentFolder || !canFileMessages)
|
||||
{
|
||||
filter->SetEnabled(PR_FALSE);
|
||||
// we need to explicitly save the filter file.
|
||||
if (m_filterList)
|
||||
m_filterList->SaveToDefaultFile();
|
||||
destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
|
||||
if (filter)
|
||||
{
|
||||
filter->SetEnabled(PR_FALSE);
|
||||
// we need to explicitly save the filter file.
|
||||
if (m_filterList)
|
||||
m_filterList->SaveToDefaultFile();
|
||||
destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
|
||||
}
|
||||
return NS_MSG_NOT_A_MAIL_FOLDER;
|
||||
}
|
||||
|
||||
|
||||
@ -430,6 +430,7 @@ pref("mail.server.default.max_articles", 500);
|
||||
pref("mail.server.default.notify.on", true);
|
||||
pref("mail.server.default.mark_old_read", false);
|
||||
pref("mail.server.default.empty_trash_on_exit", false);
|
||||
pref("mail.server.default.dup_action", 0);
|
||||
|
||||
pref("mail.server.default.using_subscription", true);
|
||||
pref("mail.server.default.dual_use_folders", true);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user