mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
5604 lines
162 KiB
C++
5604 lines
162 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "msgCore.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsUnicharUtils.h"
|
|
#include "nsMsgDBFolder.h"
|
|
#include "nsMsgFolderFlags.h"
|
|
#include "nsIPrefBranch.h"
|
|
#include "nsIPrefService.h"
|
|
#include "nsRDFCID.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsIMsgFolderCache.h"
|
|
#include "nsIMsgFolderCacheElement.h"
|
|
#include "nsMsgBaseCID.h"
|
|
#include "nsIMsgMailNewsUrl.h"
|
|
#include "nsMsgDatabase.h"
|
|
#include "nsIMsgAccountManager.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsEscape.h"
|
|
#include "nsLocalFolderSummarySpec.h"
|
|
#include "nsMsgI18N.h"
|
|
#include "nsNativeCharsetUtils.h"
|
|
#include "nsIFileStream.h"
|
|
#include "nsIChannel.h"
|
|
#include "nsITransport.h"
|
|
#include "nsIMsgFolderCompactor.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsIMsgWindow.h"
|
|
#include "nsIPrompt.h"
|
|
#include "nsIInterfaceRequestor.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsILocale.h"
|
|
#include "nsILocaleService.h"
|
|
#include "nsCollationCID.h"
|
|
#include "nsAbBaseCID.h"
|
|
#include "nsIAbMDBDirectory.h"
|
|
#include "nsISpamSettings.h"
|
|
#include "nsIMsgFilterPlugin.h"
|
|
#include "nsIMsgMailSession.h"
|
|
#include "nsIRDFService.h"
|
|
#include "nsTextFormatter.h"
|
|
#include "nsCPasswordManager.h"
|
|
#include "nsMsgDBCID.h"
|
|
#include "nsInt64.h"
|
|
#include "nsReadLine.h"
|
|
#include "nsParserCIID.h"
|
|
#include "nsIParser.h"
|
|
#include "nsIHTMLContentSink.h"
|
|
#include "nsIContentSerializer.h"
|
|
#include "nsLayoutCID.h"
|
|
#include "nsIHTMLToTextSink.h"
|
|
#include "nsIDocumentEncoder.h"
|
|
#include "nsIMIMEHeaderParam.h"
|
|
#include "plbase64.h"
|
|
#include <time.h>
|
|
#include "nsIMsgFolderNotificationService.h"
|
|
|
|
#define oneHour 3600000000U
|
|
#include "nsMsgUtils.h"
|
|
|
|
static PRTime gtimeOfLastPurgeCheck; //variable to know when to check for purge_threshhold
|
|
|
|
#define PREF_MAIL_PROMPT_PURGE_THRESHOLD "mail.prompt_purge_threshhold"
|
|
#define PREF_MAIL_PURGE_THRESHOLD "mail.purge_threshhold"
|
|
#define PREF_MAIL_PURGE_ASK "mail.purge.ask"
|
|
#define PREF_MAIL_WARN_FILTER_CHANGED "mail.warn_filter_changed"
|
|
|
|
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
|
|
static NS_DEFINE_CID(kCollationFactoryCID, NS_COLLATIONFACTORY_CID);
|
|
static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID);
|
|
static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
|
|
static NS_DEFINE_CID(kNavDTDCID, NS_CNAVDTD_CID);
|
|
|
|
nsIAtom* nsMsgDBFolder::mFolderLoadedAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::mDeleteOrMoveMsgCompletedAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::mDeleteOrMoveMsgFailedAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::mJunkStatusChangedAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kTotalMessagesAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kFolderSizeAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kBiffStateAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kNewMessagesAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kInVFEditSearchScopeAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kNumNewBiffMessagesAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kTotalUnreadMessagesAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kFlaggedAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kStatusAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kNameAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kSynchronizeAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kOpenAtom=nsnull;
|
|
nsIAtom* nsMsgDBFolder::kIsDeferred=nsnull;
|
|
|
|
nsICollation * nsMsgDBFolder::gCollationKeyGenerator = nsnull;
|
|
|
|
PRUnichar *nsMsgDBFolder::kLocalizedInboxName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedTrashName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedSentName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedDraftsName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedTemplatesName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedUnsentName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedJunkName;
|
|
PRUnichar *nsMsgDBFolder::kLocalizedBrandShortName;
|
|
|
|
nsrefcnt nsMsgDBFolder::mInstanceCount=0;
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED6(nsMsgDBFolder, nsRDFResource,
|
|
nsISupportsWeakReference,
|
|
nsIMsgFolder,
|
|
nsICollection,
|
|
nsISerializable,
|
|
nsIDBChangeListener,
|
|
nsIUrlListener)
|
|
|
|
const nsStaticAtom nsMsgDBFolder::folder_atoms[] = {
|
|
{ "FolderLoaded", &nsMsgDBFolder::mFolderLoadedAtom },
|
|
{ "DeleteOrMoveMsgCompleted", &nsMsgDBFolder::mDeleteOrMoveMsgCompletedAtom },
|
|
{ "DeleteOrMoveMsgFailed", &nsMsgDBFolder::mDeleteOrMoveMsgFailedAtom },
|
|
{ "JunkStatusChanged", &nsMsgDBFolder::mJunkStatusChangedAtom },
|
|
{ "BiffState", &nsMsgDBFolder::kBiffStateAtom },
|
|
{ "NewMessages", &nsMsgDBFolder::kNewMessagesAtom },
|
|
{ "inVFEditSearchScope", &nsMsgDBFolder::kInVFEditSearchScopeAtom },
|
|
{ "NumNewBiffMessages", &nsMsgDBFolder::kNumNewBiffMessagesAtom },
|
|
{ "Name", &nsMsgDBFolder::kNameAtom },
|
|
{ "TotalUnreadMessages", &nsMsgDBFolder::kTotalUnreadMessagesAtom },
|
|
{ "TotalMessages", &nsMsgDBFolder::kTotalMessagesAtom },
|
|
{ "FolderSize", &nsMsgDBFolder::kFolderSizeAtom },
|
|
{ "Status", &nsMsgDBFolder::kStatusAtom },
|
|
{ "Flagged", &nsMsgDBFolder::kFlaggedAtom },
|
|
{ "Synchronize", &nsMsgDBFolder::kSynchronizeAtom },
|
|
{ "open", &nsMsgDBFolder::kOpenAtom },
|
|
{ "isDeferred", &nsMsgDBFolder::kIsDeferred }
|
|
};
|
|
|
|
nsMsgDBFolder::nsMsgDBFolder(void)
|
|
: mAddListener(PR_TRUE),
|
|
mNewMessages(PR_FALSE),
|
|
mGettingNewMessages(PR_FALSE),
|
|
mLastMessageLoaded(nsMsgKey_None),
|
|
mFlags(0),
|
|
mNumUnreadMessages(-1),
|
|
mNumTotalMessages(-1),
|
|
mNotifyCountChanges(PR_TRUE),
|
|
mExpungedBytes(0),
|
|
mInitializedFromCache(PR_FALSE),
|
|
mSemaphoreHolder(nsnull),
|
|
mNumPendingUnreadMessages(0),
|
|
mNumPendingTotalMessages(0),
|
|
mFolderSize(0),
|
|
mNumNewBiffMessages(0),
|
|
mIsCachable(PR_TRUE),
|
|
mHaveParsedURI(PR_FALSE),
|
|
mIsServerIsValid(PR_FALSE),
|
|
mIsServer(PR_FALSE),
|
|
mBaseMessageURI(nsnull),
|
|
mInVFEditSearchScope (PR_FALSE)
|
|
{
|
|
NS_NewISupportsArray(getter_AddRefs(mSubFolders));
|
|
if (mInstanceCount++ <=0) {
|
|
NS_RegisterStaticAtoms(folder_atoms, NS_ARRAY_LENGTH(folder_atoms));
|
|
initializeStrings();
|
|
createCollationKeyGenerator();
|
|
#ifdef MSG_FASTER_URI_PARSING
|
|
mParsingURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID);
|
|
#endif
|
|
LL_I2L(gtimeOfLastPurgeCheck, 0);
|
|
}
|
|
}
|
|
|
|
nsMsgDBFolder::~nsMsgDBFolder(void)
|
|
{
|
|
CRTFREEIF(mBaseMessageURI);
|
|
|
|
if (--mInstanceCount == 0) {
|
|
NS_IF_RELEASE(gCollationKeyGenerator);
|
|
CRTFREEIF(kLocalizedInboxName);
|
|
CRTFREEIF(kLocalizedTrashName);
|
|
CRTFREEIF(kLocalizedSentName);
|
|
CRTFREEIF(kLocalizedDraftsName);
|
|
CRTFREEIF(kLocalizedTemplatesName);
|
|
CRTFREEIF(kLocalizedUnsentName);
|
|
CRTFREEIF(kLocalizedJunkName);
|
|
CRTFREEIF(kLocalizedBrandShortName);
|
|
#ifdef MSG_FASTER_URI_PARSING
|
|
mParsingURL = nsnull;
|
|
#endif
|
|
}
|
|
//shutdown but don't shutdown children.
|
|
Shutdown(PR_FALSE);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::Shutdown(PRBool shutdownChildren)
|
|
{
|
|
if(mDatabase)
|
|
{
|
|
mDatabase->RemoveListener(this);
|
|
mDatabase->Close(PR_TRUE);
|
|
mDatabase = nsnull;
|
|
|
|
}
|
|
|
|
if(shutdownChildren)
|
|
{
|
|
PRUint32 count;
|
|
nsresult rv = mSubFolders->Count(&count);
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> childFolder = do_QueryElementAt(mSubFolders, i);
|
|
if(childFolder)
|
|
childFolder->Shutdown(PR_TRUE);
|
|
}
|
|
}
|
|
// Reset incoming server pointer and pathname.
|
|
mServer = nsnull;
|
|
mPath = nsnull;
|
|
mHaveParsedURI = PR_FALSE;
|
|
mName.SetLength(0);
|
|
mSubFolders->Clear();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ForceDBClosed()
|
|
{
|
|
PRUint32 cnt = 0, i;
|
|
if (mSubFolders)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> child;
|
|
mSubFolders->Count(&cnt);
|
|
if (cnt > 0)
|
|
for (i = 0; i < cnt; i++)
|
|
{
|
|
child = do_QueryElementAt(mSubFolders, i);
|
|
if (child)
|
|
child->ForceDBClosed();
|
|
}
|
|
}
|
|
if (mDatabase)
|
|
{
|
|
mDatabase->ForceClosed();
|
|
mDatabase = nsnull;
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr<nsIMsgDatabase> mailDBFactory = do_CreateInstance(kCMailDB);
|
|
if (mailDBFactory)
|
|
mailDBFactory->ForceFolderDBClosed(this);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::StartFolderLoading(void)
|
|
{
|
|
if(mDatabase)
|
|
mDatabase->RemoveListener(this);
|
|
mAddListener = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::EndFolderLoading(void)
|
|
{
|
|
if(mDatabase)
|
|
mDatabase->AddListener(this);
|
|
mAddListener = PR_TRUE;
|
|
UpdateSummaryTotals(PR_TRUE);
|
|
|
|
//GGGG check for new mail here and call SetNewMessages...?? -- ONE OF THE 2 PLACES
|
|
if(mDatabase)
|
|
m_newMsgs.RemoveAll();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetExpungedBytes(PRUint32 *count)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(count);
|
|
|
|
if (mDatabase)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
rv = mDatabase->GetDBFolderInfo(getter_AddRefs(folderInfo));
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = folderInfo->GetExpungedBytes((PRInt32 *) count);
|
|
if (NS_SUCCEEDED(rv))
|
|
mExpungedBytes = *count; // sync up with the database
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
ReadDBFolderInfo(PR_FALSE);
|
|
*count = mExpungedBytes;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetCharset(char * *aCharset)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aCharset);
|
|
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if(NS_SUCCEEDED(rv))
|
|
return folderInfo->GetCharPtrCharacterSet(aCharset);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetCharset(const char * aCharset)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
rv = folderInfo->SetCharacterSet(aCharset);
|
|
db->Commit(nsMsgDBCommitType::kLargeCommit);
|
|
mCharset.AssignASCII(aCharset); // synchronize member variable
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetCharsetOverride(PRBool *aCharsetOverride)
|
|
{
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if(NS_SUCCEEDED(rv))
|
|
rv = folderInfo->GetCharacterSetOverride(aCharsetOverride);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetCharsetOverride(PRBool aCharsetOverride)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
rv = folderInfo->SetCharacterSetOverride(aCharsetOverride);
|
|
db->Commit(nsMsgDBCommitType::kLargeCommit);
|
|
mCharsetOverride = aCharsetOverride; // synchronize member variable
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetHasNewMessages(PRBool *hasNewMessages)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(hasNewMessages);
|
|
|
|
*hasNewMessages = mNewMessages;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetHasNewMessages(PRBool curNewMessages)
|
|
{
|
|
if (curNewMessages != mNewMessages)
|
|
{
|
|
// Only change mru time if we're going from doesn't have new to has new.
|
|
// technically, we should probably update mru time for every new message
|
|
// but we would pay a performance penalty for that. If the user
|
|
// opens the folder, the mrutime will get updated anyway.
|
|
if (curNewMessages)
|
|
SetMRUTime();
|
|
/** @params
|
|
* nsIAtom* property, PRBool oldValue, PRBool newValue
|
|
*/
|
|
PRBool oldNewMessages = mNewMessages;
|
|
mNewMessages = curNewMessages;
|
|
NotifyBoolPropertyChanged(kNewMessagesAtom, oldNewMessages, curNewMessages);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetGettingNewMessages(PRBool *gettingNewMessages)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(gettingNewMessages);
|
|
|
|
*gettingNewMessages = mGettingNewMessages;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetGettingNewMessages(PRBool gettingNewMessages)
|
|
{
|
|
mGettingNewMessages = gettingNewMessages;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetFirstNewMessage(nsIMsgDBHdr **firstNewMessage)
|
|
{
|
|
//If there's not a db then there can't be new messages. Return failure since you
|
|
//should use HasNewMessages first.
|
|
if(!mDatabase)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsresult rv;
|
|
nsMsgKey key;
|
|
rv = mDatabase->GetFirstNew(&key);
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIMsgDBHdr> hdr;
|
|
rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(hdr));
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
|
|
return mDatabase->GetMsgHdrForKey(key, firstNewMessage);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ClearNewMessages()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
//If there's no db then there's nothing to clear.
|
|
if(mDatabase)
|
|
{
|
|
PRUint32 numNewKeys;
|
|
PRUint32 *newMessageKeys;
|
|
rv = mDatabase->GetNewList(&numNewKeys, &newMessageKeys);
|
|
if (NS_SUCCEEDED(rv) && newMessageKeys)
|
|
{
|
|
m_saveNewMsgs.RemoveAll();
|
|
m_saveNewMsgs.Add(newMessageKeys, numNewKeys);
|
|
}
|
|
mDatabase->ClearNewList(PR_TRUE);
|
|
}
|
|
m_newMsgs.RemoveAll();
|
|
mNumNewBiffMessages = 0;
|
|
return rv;
|
|
}
|
|
|
|
void nsMsgDBFolder::UpdateNewMessages()
|
|
{
|
|
if (! (mFlags & MSG_FOLDER_FLAG_VIRTUAL))
|
|
{
|
|
PRBool hasNewMessages = PR_FALSE;
|
|
for (PRUint32 keyIndex = 0; keyIndex < m_newMsgs.GetSize(); keyIndex++)
|
|
{
|
|
PRBool containsKey = PR_FALSE;
|
|
mDatabase->ContainsKey(m_newMsgs[keyIndex], &containsKey);
|
|
if (!containsKey)
|
|
continue;
|
|
PRBool isRead = PR_FALSE;
|
|
nsresult rv2 = mDatabase->IsRead(m_newMsgs[keyIndex], &isRead);
|
|
if (NS_SUCCEEDED(rv2) && !isRead)
|
|
{
|
|
hasNewMessages = PR_TRUE;
|
|
mDatabase->AddToNewList(m_newMsgs[keyIndex]);
|
|
}
|
|
}
|
|
SetHasNewMessages(hasNewMessages);
|
|
}
|
|
}
|
|
|
|
// helper function that gets the cache element that corresponds to the passed in file spec.
|
|
// This could be static, or could live in another class - it's not specific to the current
|
|
// nsMsgDBFolder. If it lived at a higher level, we could cache the account manager and folder cache.
|
|
nsresult nsMsgDBFolder::GetFolderCacheElemFromFileSpec(nsIFileSpec *fileSpec, nsIMsgFolderCacheElement **cacheElement)
|
|
{
|
|
nsresult result;
|
|
if (!fileSpec || !cacheElement)
|
|
return NS_ERROR_NULL_POINTER;
|
|
nsCOMPtr <nsIMsgFolderCache> folderCache;
|
|
#ifdef DEBUG_bienvenu1
|
|
PRBool exists;
|
|
NS_ASSERTION(NS_SUCCEEDED(fileSpec->Exists(&exists)) && exists, "whoops, file doesn't exist, mac will break");
|
|
#endif
|
|
nsCOMPtr<nsIMsgAccountManager> accountMgr =
|
|
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &result);
|
|
if(NS_SUCCEEDED(result))
|
|
{
|
|
result = accountMgr->GetFolderCache(getter_AddRefs(folderCache));
|
|
if (NS_SUCCEEDED(result) && folderCache)
|
|
{
|
|
nsXPIDLCString persistentPath;
|
|
fileSpec->GetPersistentDescriptorString(getter_Copies(persistentPath));
|
|
result = folderCache->GetCacheElement(persistentPath, PR_FALSE, cacheElement);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::ReadDBFolderInfo(PRBool force)
|
|
{
|
|
// Since it turns out to be pretty expensive to open and close
|
|
// the DBs all the time, if we have to open it once, get everything
|
|
// we might need while we're here
|
|
|
|
nsresult result=NS_ERROR_FAILURE;
|
|
|
|
// don't need to reload from cache if we've already read from cache,
|
|
// and, we might get stale info, so don't do it.
|
|
if (!mInitializedFromCache)
|
|
{
|
|
nsCOMPtr <nsIFileSpec> dbPath;
|
|
|
|
result = GetFolderCacheKey(getter_AddRefs(dbPath), PR_TRUE /* createDBIfMissing */);
|
|
|
|
if (dbPath)
|
|
{
|
|
nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
|
|
result = GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement));
|
|
if (NS_SUCCEEDED(result) && cacheElement)
|
|
{
|
|
result = ReadFromFolderCacheElem(cacheElement);
|
|
}
|
|
}
|
|
}
|
|
// if (m_master->InitFolderFromCache (this))
|
|
// return err;
|
|
|
|
if (force || !mInitializedFromCache)
|
|
{
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
result = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if(NS_SUCCEEDED(result))
|
|
{
|
|
mIsCachable = PR_TRUE;
|
|
if (folderInfo)
|
|
{
|
|
if (!mInitializedFromCache)
|
|
{
|
|
folderInfo->GetFlags((PRInt32 *)&mFlags);
|
|
#ifdef DEBUG_bienvenu1
|
|
nsXPIDLString name;
|
|
GetName(getter_Copies(name));
|
|
NS_ASSERTION(Compare(name, kLocalizedTrashName) || (mFlags & MSG_FOLDER_FLAG_TRASH), "lost trash flag");
|
|
#endif
|
|
mInitializedFromCache = PR_TRUE;
|
|
}
|
|
|
|
folderInfo->GetNumMessages(&mNumTotalMessages);
|
|
folderInfo->GetNumUnreadMessages(&mNumUnreadMessages);
|
|
folderInfo->GetExpungedBytes((PRInt32 *)&mExpungedBytes);
|
|
|
|
nsXPIDLCString utf8Name;
|
|
folderInfo->GetFolderName(getter_Copies(utf8Name));
|
|
if (!utf8Name.IsEmpty())
|
|
CopyUTF8toUTF16(utf8Name, mName);
|
|
|
|
//These should be put in IMAP folder only.
|
|
//folderInfo->GetImapTotalPendingMessages(&mNumPendingTotalMessages);
|
|
//folderInfo->GetImapUnreadPendingMessages(&mNumPendingUnreadMessages);
|
|
|
|
PRBool defaultUsed;
|
|
folderInfo->GetCharacterSet(mCharset, &defaultUsed);
|
|
if (defaultUsed)
|
|
mCharset.Truncate();
|
|
folderInfo->GetCharacterSetOverride(&mCharsetOverride);
|
|
|
|
if (db) {
|
|
PRBool hasnew;
|
|
nsresult rv;
|
|
rv = db->HasNew(&hasnew);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!hasnew && mNumPendingUnreadMessages <= 0) {
|
|
ClearFlag(MSG_FOLDER_FLAG_GOT_NEW);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
folderInfo = nsnull;
|
|
if (db)
|
|
db->Close(PR_FALSE);
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::SendFlagNotifications(nsIMsgDBHdr *item, PRUint32 oldFlags, PRUint32 newFlags)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
PRUint32 changedFlags = oldFlags ^ newFlags;
|
|
|
|
if((changedFlags & MSG_FLAG_READ) && (changedFlags & MSG_FLAG_NEW))
|
|
{
|
|
//..so..if the msg is read in the folder and the folder has new msgs clear the account level and status bar biffs.
|
|
rv = NotifyPropertyFlagChanged(item, kStatusAtom, oldFlags, newFlags);
|
|
rv = SetBiffState(nsMsgBiffState_NoMail);
|
|
}
|
|
else if(changedFlags & (MSG_FLAG_READ | MSG_FLAG_REPLIED | MSG_FLAG_FORWARDED
|
|
| MSG_FLAG_IMAP_DELETED | MSG_FLAG_NEW | MSG_FLAG_OFFLINE))
|
|
{
|
|
rv = NotifyPropertyFlagChanged(item, kStatusAtom, oldFlags, newFlags);
|
|
}
|
|
else if((changedFlags & MSG_FLAG_MARKED))
|
|
{
|
|
rv = NotifyPropertyFlagChanged(item, kFlaggedAtom, oldFlags, newFlags);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::DownloadMessagesForOffline(nsISupportsArray *messages, nsIMsgWindow *)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "imap and news need to override this");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::DownloadAllForOffline(nsIUrlListener *listener, nsIMsgWindow *msgWindow)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "imap and news need to override this");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetOfflineStoreInputStream(nsIInputStream **stream)
|
|
{
|
|
nsresult rv = NS_ERROR_NULL_POINTER;
|
|
if (mPath)
|
|
rv = mPath->GetInputStream(stream);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetOfflineFileStream(nsMsgKey msgKey, PRUint32 *offset, PRUint32 *size, nsIInputStream **aFileStream)
|
|
{
|
|
NS_ENSURE_ARG(aFileStream);
|
|
|
|
*offset = *size = 0;
|
|
|
|
nsXPIDLCString nativePath;
|
|
mPath->GetNativePath(getter_Copies(nativePath));
|
|
|
|
nsCOMPtr <nsILocalFile> localStore;
|
|
nsresult rv = NS_NewNativeLocalFile(nativePath, PR_TRUE, getter_AddRefs(localStore));
|
|
if (NS_SUCCEEDED(rv) && localStore)
|
|
{
|
|
rv = NS_NewLocalFileInputStream(aFileStream, localStore);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
|
|
rv = GetDatabase(nsnull);
|
|
NS_ENSURE_SUCCESS(rv, NS_OK);
|
|
nsCOMPtr<nsIMsgDBHdr> hdr;
|
|
rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
|
|
if (hdr && NS_SUCCEEDED(rv))
|
|
{
|
|
hdr->GetMessageOffset(offset);
|
|
hdr->GetOfflineMessageSize(size);
|
|
}
|
|
// check if offline store really has the correct offset into the offline
|
|
// store by reading the first few bytes. If it doesn't, clear the offline
|
|
// flag on the msg and return false, which will fall back to reading the message
|
|
// from the server.
|
|
nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(*aFileStream);
|
|
if (seekableStream)
|
|
{
|
|
rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, *offset);
|
|
char startOfMsg[10];
|
|
PRUint32 bytesRead;
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = (*aFileStream)->Read(startOfMsg, sizeof(startOfMsg), &bytesRead);
|
|
|
|
// check if message starts with From, or is a draft and starts with FCC
|
|
if (NS_FAILED(rv) || bytesRead != sizeof(startOfMsg) ||
|
|
(strncmp(startOfMsg, "From ", 5) && (! (mFlags & MSG_FOLDER_FLAG_DRAFTS) || strncmp(startOfMsg, "FCC", 3))))
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
if (NS_FAILED(rv) && mDatabase)
|
|
mDatabase->MarkOffline(msgKey, PR_FALSE, nsnull);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetOfflineStoreOutputStream(nsIOutputStream **outputStream)
|
|
{
|
|
nsresult rv = NS_ERROR_NULL_POINTER;
|
|
if (mPath)
|
|
{
|
|
// the following code doesn't work for a host of reasons - the transfer offset
|
|
// is ignored for output streams. The buffering used by file channels does not work
|
|
// if transfer offsets are coerced to work, etc.
|
|
#if 0
|
|
nsCOMPtr<nsIFileChannel> fileChannel = do_CreateInstance(NS_LOCALFILECHANNEL_CONTRACTID);
|
|
if (fileChannel)
|
|
{
|
|
nsCOMPtr <nsILocalFile> localStore;
|
|
rv = NS_NewLocalFile(nativePath, PR_TRUE, getter_AddRefs(localStore));
|
|
if (NS_SUCCEEDED(rv) && localStore)
|
|
{
|
|
rv = fileChannel->Init(localStore, PR_CREATE_FILE | PR_RDWR, 0);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
rv = fileChannel->Open(outputStream);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
}
|
|
#endif
|
|
nsCOMPtr<nsISupports> supports;
|
|
nsFileSpec fileSpec;
|
|
mPath->GetFileSpec(&fileSpec);
|
|
rv = NS_NewIOFileStream(getter_AddRefs(supports), fileSpec, PR_WRONLY | PR_CREATE_FILE, 00700);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
supports->QueryInterface(NS_GET_IID(nsIOutputStream), (void **) outputStream);
|
|
|
|
nsCOMPtr <nsISeekableStream> seekable = do_QueryInterface(supports);
|
|
if (seekable)
|
|
seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// XXX todo
|
|
// move these to a common location and remove all the hard coded ".msf"
|
|
#define SUMMARY_SUFFIX ".msf"
|
|
#define SUMMARY_SUFFIX_LEN 4
|
|
|
|
// path coming in is the root path without the leaf name,
|
|
// on the way out, it's the whole path.
|
|
nsresult nsMsgDBFolder::CreateFileSpecForDB(const char *userLeafName, nsFileSpec &path, nsIFileSpec **dbFileSpec)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(dbFileSpec);
|
|
NS_ENSURE_ARG_POINTER(userLeafName);
|
|
|
|
// XXX : This function is only called by nsImapMailFolder which calls
|
|
// this function with UTF-7 (ASCII only) userLeafName so that we can
|
|
// use 'char' version of NS_MsgHasIfNcessary (bug 264071).
|
|
// If this becomes not the case any more, we should use PRUnichar-version,
|
|
// instead.
|
|
nsCAutoString proposedDBName(userLeafName);
|
|
NS_MsgHashIfNecessary(proposedDBName);
|
|
|
|
// (note, the caller of this will be using the dbFileSpec to call db->Open()
|
|
// will turn the path into summary spec, and append the ".msf" extension)
|
|
//
|
|
// we want db->Open() to create a new summary file
|
|
// so we have to jump through some hoops to make sure the .msf it will
|
|
// create is unique. now that we've got the "safe" proposedDBName,
|
|
// we append ".msf" to see if the file exists. if so, we make the name
|
|
// unique and then string off the ".msf" so that we pass the right thing
|
|
// into Open(). this isn't ideal, since this is not atomic
|
|
// but it will make do.
|
|
proposedDBName+= SUMMARY_SUFFIX;
|
|
path += proposedDBName.get();
|
|
if (path.Exists())
|
|
{
|
|
path.MakeUnique();
|
|
proposedDBName = path.GetLeafName();
|
|
}
|
|
// now, take the ".msf" off
|
|
proposedDBName.Truncate(proposedDBName.Length() - SUMMARY_SUFFIX_LEN);
|
|
path.SetLeafName(proposedDBName.get());
|
|
|
|
NS_NewFileSpecWithSpec(path, dbFileSpec);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetMsgDatabase(nsIMsgWindow *aMsgWindow,
|
|
nsIMsgDatabase** aMsgDatabase)
|
|
{
|
|
GetDatabase(aMsgWindow);
|
|
|
|
if (!aMsgDatabase || !mDatabase)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
NS_ADDREF(*aMsgDatabase = mDatabase);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetMsgDatabase(nsIMsgDatabase *aMsgDatabase)
|
|
{
|
|
if (mDatabase)
|
|
{
|
|
// commit here - db might go away when all these refs are released.
|
|
mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
|
|
mDatabase->RemoveListener(this);
|
|
mDatabase->ClearCachedHdrs();
|
|
if (!aMsgDatabase)
|
|
{
|
|
PRUint32 numNewKeys;
|
|
PRUint32 *newMessageKeys;
|
|
nsresult rv = mDatabase->GetNewList(&numNewKeys, &newMessageKeys);
|
|
if (NS_SUCCEEDED(rv) && newMessageKeys)
|
|
{
|
|
m_newMsgs.RemoveAll();
|
|
m_newMsgs.Add(newMessageKeys, numNewKeys);
|
|
}
|
|
nsMemory::Free (newMessageKeys);
|
|
}
|
|
}
|
|
mDatabase = aMsgDatabase;
|
|
|
|
if (aMsgDatabase)
|
|
aMsgDatabase->AddListener(this);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **database)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::OnReadChanged(nsIDBChangeListener * aInstigator)
|
|
{
|
|
/* do nothing. if you care about this, override it. see nsNewsFolder.cpp */
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::OnJunkScoreChanged(nsIDBChangeListener * aInstigator)
|
|
{
|
|
NotifyFolderEvent(mJunkStatusChangedAtom);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// 1. When the status of a message changes.
|
|
NS_IMETHODIMP nsMsgDBFolder::OnHdrChange(nsIMsgDBHdr *aHdrChanged, PRUint32 aOldFlags, PRUint32 aNewFlags,
|
|
nsIDBChangeListener * aInstigator)
|
|
{
|
|
if(aHdrChanged)
|
|
{
|
|
SendFlagNotifications(aHdrChanged, aOldFlags, aNewFlags);
|
|
UpdateSummaryTotals(PR_TRUE);
|
|
}
|
|
|
|
// The old state was new message state
|
|
// We check and see if this state has changed
|
|
if(aOldFlags & MSG_FLAG_NEW)
|
|
{
|
|
// state changing from new to something else
|
|
if (!(aNewFlags & MSG_FLAG_NEW))
|
|
{
|
|
CheckWithNewMessagesStatus(PR_FALSE);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::CheckWithNewMessagesStatus(PRBool messageAdded)
|
|
{
|
|
nsresult rv;
|
|
|
|
PRBool hasNewMessages;
|
|
|
|
if (messageAdded)
|
|
{
|
|
SetHasNewMessages(PR_TRUE);
|
|
}
|
|
else // message modified or deleted
|
|
{
|
|
if(mDatabase)
|
|
{
|
|
rv = mDatabase->HasNew(&hasNewMessages);
|
|
SetHasNewMessages(hasNewMessages);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// 3. When a message gets deleted, we need to see if it was new
|
|
// When we lose a new message we need to check if there are still new messages
|
|
NS_IMETHODIMP nsMsgDBFolder::OnHdrDeleted(nsIMsgDBHdr *aHdrChanged, nsMsgKey aParentKey, PRInt32 aFlags,
|
|
nsIDBChangeListener * aInstigator)
|
|
{
|
|
// check to see if a new message is being deleted
|
|
// as in this case, if there is only one new message and it's being deleted
|
|
// the folder newness has to be cleared.
|
|
CheckWithNewMessagesStatus(PR_FALSE);
|
|
|
|
return OnHdrAddedOrDeleted(aHdrChanged, PR_FALSE);
|
|
}
|
|
|
|
// 2. When a new messages gets added, we need to see if it's new.
|
|
NS_IMETHODIMP nsMsgDBFolder::OnHdrAdded(nsIMsgDBHdr *aHdrChanged, nsMsgKey aParentKey , PRInt32 aFlags,
|
|
nsIDBChangeListener * aInstigator)
|
|
{
|
|
if(aFlags & MSG_FLAG_NEW)
|
|
CheckWithNewMessagesStatus(PR_TRUE);
|
|
|
|
return OnHdrAddedOrDeleted(aHdrChanged, PR_TRUE);
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::OnHdrAddedOrDeleted(nsIMsgDBHdr *aHdrChanged, PRBool added)
|
|
{
|
|
if(added)
|
|
NotifyItemAdded(aHdrChanged);
|
|
else
|
|
NotifyItemRemoved(aHdrChanged);
|
|
UpdateSummaryTotals(PR_TRUE);
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent, nsMsgKey newParent,
|
|
nsIDBChangeListener * aInstigator)
|
|
{
|
|
nsCOMPtr<nsIMsgDBHdr> hdrChanged;
|
|
mDatabase->GetMsgHdrForKey(aKeyChanged, getter_AddRefs(hdrChanged));
|
|
//In reality we probably want to just change the parent because otherwise we will lose things like
|
|
//selection.
|
|
|
|
if (hdrChanged)
|
|
{
|
|
//First delete the child from the old threadParent
|
|
OnHdrAddedOrDeleted(hdrChanged, PR_FALSE);
|
|
//Then add it to the new threadParent
|
|
OnHdrAddedOrDeleted(hdrChanged, PR_TRUE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
|
|
{
|
|
if (mDatabase)
|
|
{
|
|
mDatabase->RemoveListener(this);
|
|
mDatabase = nsnull;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetManyHeadersToDownload(PRBool *retval)
|
|
{
|
|
PRInt32 numTotalMessages;
|
|
|
|
NS_ENSURE_ARG_POINTER(retval);
|
|
|
|
// is there any reason to return false?
|
|
if (!mDatabase)
|
|
*retval = PR_TRUE;
|
|
else if (NS_SUCCEEDED(GetTotalMessages(PR_FALSE, &numTotalMessages)) && numTotalMessages <= 0)
|
|
*retval = PR_TRUE;
|
|
else
|
|
*retval = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::MsgFitsDownloadCriteria(nsMsgKey msgKey, PRBool *result)
|
|
{
|
|
if(!mDatabase)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgDBHdr> hdr;
|
|
rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (hdr)
|
|
{
|
|
PRUint32 msgFlags = 0;
|
|
|
|
hdr->GetFlags(&msgFlags);
|
|
// check if we already have this message body offline
|
|
if (! (msgFlags & MSG_FLAG_OFFLINE))
|
|
{
|
|
*result = PR_TRUE;
|
|
// check against the server download size limit .
|
|
nsCOMPtr <nsIMsgIncomingServer> incomingServer;
|
|
rv = GetServer(getter_AddRefs(incomingServer));
|
|
if (NS_SUCCEEDED(rv) && incomingServer)
|
|
{
|
|
PRBool limitDownloadSize = PR_FALSE;
|
|
rv = incomingServer->GetLimitOfflineMessageSize(&limitDownloadSize);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (limitDownloadSize)
|
|
{
|
|
PRInt32 maxDownloadMsgSize = 0;
|
|
PRUint32 msgSize;
|
|
hdr->GetMessageSize(&msgSize);
|
|
rv = incomingServer->GetMaxMessageSize(&maxDownloadMsgSize);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
maxDownloadMsgSize *= 1024;
|
|
if (msgSize > (PRUint32) maxDownloadMsgSize)
|
|
*result = PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetSupportsOffline(PRBool *aSupportsOffline)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aSupportsOffline);
|
|
|
|
if (mFlags & MSG_FOLDER_FLAG_VIRTUAL)
|
|
{
|
|
*aSupportsOffline = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
if (!server) return NS_ERROR_FAILURE;
|
|
|
|
PRInt32 offlineSupportLevel;
|
|
rv = server->GetOfflineSupportLevel(&offlineSupportLevel);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
*aSupportsOffline = (offlineSupportLevel >= OFFLINE_SUPPORT_LEVEL_REGULAR);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ShouldStoreMsgOffline(nsMsgKey msgKey, PRBool *result)
|
|
{
|
|
NS_ENSURE_ARG(result);
|
|
PRUint32 flags = 0;
|
|
*result = PR_FALSE;
|
|
GetFlags(&flags);
|
|
|
|
if (flags & MSG_FOLDER_FLAG_OFFLINE)
|
|
return MsgFitsDownloadCriteria(msgKey, result);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::HasMsgOffline(nsMsgKey msgKey, PRBool *result)
|
|
{
|
|
NS_ENSURE_ARG(result);
|
|
*result = PR_FALSE;
|
|
if(!mDatabase)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgDBHdr> hdr;
|
|
rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (hdr)
|
|
{
|
|
PRUint32 msgFlags = 0;
|
|
|
|
hdr->GetFlags(&msgFlags);
|
|
// check if we already have this message body offline
|
|
if ((msgFlags & MSG_FLAG_OFFLINE))
|
|
*result = PR_TRUE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetFlags(PRUint32 *_retval)
|
|
{
|
|
ReadDBFolderInfo(PR_FALSE);
|
|
*_retval = mFlags;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsXPIDLCString charset;
|
|
|
|
element->GetInt32Property("flags", (PRInt32 *) &mFlags);
|
|
|
|
PRBool persistElided = PR_TRUE;
|
|
rv = GetPersistElided(&persistElided);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
// we aren't persisting elided, set the folder as closed
|
|
if (!persistElided) {
|
|
mFlags |= MSG_FOLDER_FLAG_ELIDED;
|
|
}
|
|
|
|
element->GetInt32Property("totalMsgs", &mNumTotalMessages);
|
|
element->GetInt32Property("totalUnreadMsgs", &mNumUnreadMessages);
|
|
element->GetInt32Property("pendingUnreadMsgs", &mNumPendingUnreadMessages);
|
|
element->GetInt32Property("pendingMsgs", &mNumPendingTotalMessages);
|
|
element->GetInt32Property("expungedBytes", (PRInt32 *) &mExpungedBytes);
|
|
element->GetInt32Property("folderSize", (PRInt32 *) &mFolderSize);
|
|
|
|
element->GetStringProperty("charset", getter_Copies(charset));
|
|
|
|
#ifdef DEBUG_bienvenu1
|
|
char *uri;
|
|
|
|
GetURI(&uri);
|
|
printf("read total %ld for %s\n", mNumTotalMessages, uri);
|
|
PR_Free(uri);
|
|
#endif
|
|
mCharset.AssignASCII(charset);
|
|
|
|
mInitializedFromCache = PR_TRUE;
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::GetFolderCacheKey(nsIFileSpec **aFileSpec, PRBool createDBIfMissing /* = PR_FALSE */)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr <nsIFileSpec> path;
|
|
rv = GetPath(getter_AddRefs(path));
|
|
|
|
// now we put a new file spec in aFileSpec, because we're going to change it.
|
|
rv = NS_NewFileSpec(aFileSpec);
|
|
|
|
if (NS_SUCCEEDED(rv) && *aFileSpec)
|
|
{
|
|
nsIFileSpec *dbPath = *aFileSpec;
|
|
dbPath->FromFileSpec(path);
|
|
// if not a server, we need to convert to a db Path with .msf on the end
|
|
PRBool isServer = PR_FALSE;
|
|
GetIsServer(&isServer);
|
|
|
|
// if it's a server, we don't need the .msf appended to the name
|
|
if (!isServer)
|
|
{
|
|
nsFileSpec folderName;
|
|
dbPath->GetFileSpec(&folderName);
|
|
nsLocalFolderSummarySpec summarySpec(folderName);
|
|
|
|
dbPath->SetFromFileSpec(summarySpec);
|
|
|
|
// create the .msf file
|
|
// see bug #244217 for details
|
|
PRBool exists;
|
|
if (createDBIfMissing && NS_SUCCEEDED(dbPath->Exists(&exists)) && !exists)
|
|
dbPath->Touch();
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::FlushToFolderCache()
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgAccountManager> accountManager =
|
|
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && accountManager)
|
|
{
|
|
nsCOMPtr<nsIMsgFolderCache> folderCache;
|
|
|
|
rv = accountManager->GetFolderCache(getter_AddRefs(folderCache));
|
|
if (NS_SUCCEEDED(rv) && folderCache)
|
|
rv = WriteToFolderCache(folderCache, PR_FALSE);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::WriteToFolderCache(nsIMsgFolderCache *folderCache, PRBool deep)
|
|
{
|
|
nsCOMPtr <nsIEnumerator> aEnumerator;
|
|
nsresult rv;
|
|
|
|
if (folderCache)
|
|
{
|
|
nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
|
|
nsCOMPtr <nsIFileSpec> dbPath;
|
|
|
|
rv = GetFolderCacheKey(getter_AddRefs(dbPath));
|
|
#ifdef DEBUG_bienvenu1
|
|
PRBool exists;
|
|
NS_ASSERTION(NS_SUCCEEDED(dbPath->Exists(&exists)) && exists, "file spec we're adding to cache should exist");
|
|
#endif
|
|
if (NS_SUCCEEDED(rv) && dbPath)
|
|
{
|
|
nsXPIDLCString persistentPath;
|
|
dbPath->GetPersistentDescriptorString(getter_Copies(persistentPath));
|
|
rv = folderCache->GetCacheElement(persistentPath, PR_TRUE, getter_AddRefs(cacheElement));
|
|
if (NS_SUCCEEDED(rv) && cacheElement)
|
|
rv = WriteToFolderCacheElem(cacheElement);
|
|
}
|
|
}
|
|
|
|
if (!deep)
|
|
return rv;
|
|
|
|
rv = GetSubFolders(getter_AddRefs(aEnumerator));
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsISupports> aItem;
|
|
|
|
rv = aEnumerator->First();
|
|
if (NS_FAILED(rv))
|
|
return NS_OK; // it's OK, there are no sub-folders.
|
|
|
|
while(NS_SUCCEEDED(rv))
|
|
{
|
|
rv = aEnumerator->CurrentItem(getter_AddRefs(aItem));
|
|
if (NS_FAILED(rv)) break;
|
|
nsCOMPtr<nsIMsgFolder> aMsgFolder(do_QueryInterface(aItem, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
if (folderCache)
|
|
{
|
|
rv = aMsgFolder->WriteToFolderCache(folderCache, PR_TRUE);
|
|
if (NS_FAILED(rv))
|
|
break;
|
|
}
|
|
}
|
|
rv = aEnumerator->Next();
|
|
if (NS_FAILED(rv))
|
|
{
|
|
rv = NS_OK;
|
|
break;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::WriteToFolderCacheElem(nsIMsgFolderCacheElement *element)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
element->SetInt32Property("flags", (PRInt32) mFlags);
|
|
element->SetInt32Property("totalMsgs", mNumTotalMessages);
|
|
element->SetInt32Property("totalUnreadMsgs", mNumUnreadMessages);
|
|
element->SetInt32Property("pendingUnreadMsgs", mNumPendingUnreadMessages);
|
|
element->SetInt32Property("pendingMsgs", mNumPendingTotalMessages);
|
|
element->SetInt32Property("expungedBytes", mExpungedBytes);
|
|
element->SetInt32Property("folderSize", mFolderSize);
|
|
|
|
element->SetStringProperty("charset", mCharset.get());
|
|
|
|
#ifdef DEBUG_bienvenu1
|
|
char *uri;
|
|
|
|
GetURI(&uri);
|
|
printf("writing total %ld for %s\n", mNumTotalMessages, uri);
|
|
PR_Free(uri);
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::AddMessageDispositionState(nsIMsgDBHdr *aMessage, nsMsgDispositionState aDispositionFlag)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aMessage);
|
|
|
|
nsresult rv = GetDatabase(nsnull);
|
|
NS_ENSURE_SUCCESS(rv, NS_OK);
|
|
|
|
nsMsgKey msgKey;
|
|
aMessage->GetMessageKey(&msgKey);
|
|
|
|
if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Replied)
|
|
mDatabase->MarkReplied(msgKey, PR_TRUE, nsnull);
|
|
else if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Forwarded)
|
|
mDatabase->MarkForwarded(msgKey, PR_TRUE, nsnull);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::MarkAllMessagesRead(void)
|
|
{
|
|
// ### fix me need nsIMsgWindow
|
|
nsresult rv = GetDatabase(nsnull);
|
|
|
|
m_newMsgs.RemoveAll();
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/);
|
|
rv = mDatabase->MarkAllRead(nsnull);
|
|
EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/);
|
|
}
|
|
SetHasNewMessages(PR_FALSE);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::MarkThreadRead(nsIMsgThread *thread)
|
|
{
|
|
|
|
nsresult rv = GetDatabase(nsnull);
|
|
if(NS_SUCCEEDED(rv))
|
|
return mDatabase->MarkThreadRead(thread, nsnull, nsnull);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::OnStartRunningUrl(nsIURI *aUrl)
|
|
{
|
|
NS_PRECONDITION(aUrl, "just a sanity check");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
|
|
{
|
|
NS_PRECONDITION(aUrl, "just a sanity check");
|
|
nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
|
|
if (mailUrl)
|
|
{
|
|
PRBool updatingFolder = PR_FALSE;
|
|
if (NS_SUCCEEDED(mailUrl->GetUpdatingFolder(&updatingFolder)) && updatingFolder)
|
|
NotifyFolderEvent(mFolderLoadedAtom);
|
|
|
|
// be sure to remove ourselves as a url listener
|
|
mailUrl->UnRegisterListener(this);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetRetentionSettings(nsIMsgRetentionSettings **settings)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(settings);
|
|
nsresult rv = NS_OK;
|
|
if (!m_retentionSettings)
|
|
{
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
{
|
|
// get the settings from the db - if the settings from the db say the folder
|
|
// is not overriding the incoming server settings, get the settings from the
|
|
// server.
|
|
rv = mDatabase->GetMsgRetentionSettings(getter_AddRefs(m_retentionSettings));
|
|
if (NS_SUCCEEDED(rv) && m_retentionSettings)
|
|
{
|
|
PRBool useServerDefaults;
|
|
m_retentionSettings->GetUseServerDefaults(&useServerDefaults);
|
|
|
|
if (useServerDefaults)
|
|
{
|
|
nsCOMPtr <nsIMsgIncomingServer> incomingServer;
|
|
rv = GetServer(getter_AddRefs(incomingServer));
|
|
if (NS_SUCCEEDED(rv) && incomingServer)
|
|
incomingServer->GetRetentionSettings(getter_AddRefs(m_retentionSettings));
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
*settings = m_retentionSettings;
|
|
NS_IF_ADDREF(*settings);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetRetentionSettings(nsIMsgRetentionSettings *settings)
|
|
{
|
|
m_retentionSettings = settings;
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
mDatabase->SetMsgRetentionSettings(settings);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetDownloadSettings(nsIMsgDownloadSettings **settings)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(settings);
|
|
nsresult rv = NS_OK;
|
|
if (!m_downloadSettings)
|
|
{
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
{
|
|
// get the settings from the db - if the settings from the db say the folder
|
|
// is not overriding the incoming server settings, get the settings from the
|
|
// server.
|
|
rv = mDatabase->GetMsgDownloadSettings(getter_AddRefs(m_downloadSettings));
|
|
if (NS_SUCCEEDED(rv) && m_downloadSettings)
|
|
{
|
|
PRBool useServerDefaults;
|
|
m_downloadSettings->GetUseServerDefaults(&useServerDefaults);
|
|
|
|
if (useServerDefaults)
|
|
{
|
|
nsCOMPtr <nsIMsgIncomingServer> incomingServer;
|
|
rv = GetServer(getter_AddRefs(incomingServer));
|
|
if (NS_SUCCEEDED(rv) && incomingServer)
|
|
incomingServer->GetDownloadSettings(getter_AddRefs(m_downloadSettings));
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
*settings = m_downloadSettings;
|
|
NS_IF_ADDREF(*settings);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetDownloadSettings(nsIMsgDownloadSettings *settings)
|
|
{
|
|
m_downloadSettings = settings;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::IsCommandEnabled(const char *command, PRBool *result)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(result);
|
|
*result = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult nsMsgDBFolder::WriteStartOfNewLocalMessage()
|
|
{
|
|
nsCAutoString result;
|
|
char *ct;
|
|
PRUint32 writeCount;
|
|
time_t now = time ((time_t*) 0);
|
|
ct = ctime(&now);
|
|
ct[24] = 0;
|
|
result = "From - ";
|
|
result += ct;
|
|
result += MSG_LINEBREAK;
|
|
|
|
nsCOMPtr <nsISeekableStream> seekable;
|
|
nsInt64 curStorePos;
|
|
|
|
if (m_offlineHeader)
|
|
seekable = do_QueryInterface(m_tempMessageStream);
|
|
|
|
if (seekable)
|
|
{
|
|
PRInt64 tellPos;
|
|
seekable->Tell(&tellPos);
|
|
curStorePos = tellPos;
|
|
// ### todo - need to convert this to 64 bits
|
|
m_offlineHeader->SetMessageOffset((PRUint32) curStorePos);
|
|
}
|
|
m_tempMessageStream->Write(result.get(), result.Length(),
|
|
&writeCount);
|
|
if (seekable)
|
|
{
|
|
PRInt64 tellPos;
|
|
seekable->Seek(PR_SEEK_CUR, 0); // seeking causes a flush, w/o syncing
|
|
seekable->Tell(&tellPos);
|
|
curStorePos = tellPos;
|
|
m_offlineHeader->SetStatusOffset((PRUint32) curStorePos);
|
|
}
|
|
|
|
result = "X-Mozilla-Status: 0001";
|
|
result += MSG_LINEBREAK;
|
|
m_tempMessageStream->Write(result.get(), result.Length(),
|
|
&writeCount);
|
|
result = "X-Mozilla-Status2: 00000000";
|
|
result += MSG_LINEBREAK;
|
|
nsresult rv = m_tempMessageStream->Write(result.get(), result.Length(),
|
|
&writeCount);
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::StartNewOfflineMessage()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (!m_tempMessageStream)
|
|
rv = GetOfflineStoreOutputStream(getter_AddRefs(m_tempMessageStream));
|
|
else
|
|
{
|
|
nsCOMPtr <nsISeekableStream> seekable;
|
|
|
|
seekable = do_QueryInterface(m_tempMessageStream);
|
|
|
|
if (seekable)
|
|
seekable->Seek(PR_SEEK_END, 0);
|
|
}
|
|
if (NS_SUCCEEDED(rv))
|
|
WriteStartOfNewLocalMessage();
|
|
m_numOfflineMsgLines = 0;
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::EndNewOfflineMessage()
|
|
{
|
|
nsCOMPtr <nsISeekableStream> seekable;
|
|
nsInt64 curStorePos;
|
|
PRUint32 messageOffset;
|
|
nsMsgKey messageKey;
|
|
|
|
nsresult rv = GetDatabase(nsnull);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
m_offlineHeader->GetMessageKey(&messageKey);
|
|
if (m_tempMessageStream)
|
|
seekable = do_QueryInterface(m_tempMessageStream);
|
|
|
|
mDatabase->MarkOffline(messageKey, PR_TRUE, nsnull);
|
|
if (seekable)
|
|
{
|
|
seekable->Seek(PR_SEEK_CUR, 0); // seeking causes a flush, w/o syncing
|
|
PRInt64 tellPos;
|
|
seekable->Tell(&tellPos);
|
|
curStorePos = tellPos;
|
|
|
|
m_offlineHeader->GetMessageOffset(&messageOffset);
|
|
curStorePos -= messageOffset;
|
|
m_offlineHeader->SetOfflineMessageSize(curStorePos);
|
|
m_offlineHeader->SetLineCount(m_numOfflineMsgLines);
|
|
}
|
|
m_offlineHeader = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::CompactOfflineStore(nsIMsgWindow *inWindow)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr <nsIMsgFolderCompactor> folderCompactor = do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && folderCompactor)
|
|
rv = folderCompactor->Compact(this, PR_TRUE, inWindow);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::AutoCompact(nsIMsgWindow *aWindow)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aWindow);
|
|
PRBool prompt;
|
|
nsresult rv = GetPromptPurgeThreshold(&prompt);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
PRTime timeNow = PR_Now(); //time in microseconds
|
|
PRTime timeAfterOneHourOfLastPurgeCheck;
|
|
LL_ADD(timeAfterOneHourOfLastPurgeCheck, gtimeOfLastPurgeCheck, oneHour);
|
|
if (LL_CMP(timeAfterOneHourOfLastPurgeCheck, <, timeNow) && prompt)
|
|
{
|
|
gtimeOfLastPurgeCheck = timeNow;
|
|
nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsISupportsArray> allServers;
|
|
accountMgr->GetAllServers(getter_AddRefs(allServers));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
PRUint32 numServers, serverIndex=0;
|
|
rv = allServers->Count(&numServers);
|
|
PRInt32 offlineSupportLevel;
|
|
if ( numServers > 0 )
|
|
{
|
|
nsCOMPtr<nsIMsgIncomingServer> server =
|
|
do_QueryElementAt(allServers, serverIndex);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
nsCOMPtr<nsISupportsArray> folderArray;
|
|
nsCOMPtr<nsISupportsArray> offlineFolderArray;
|
|
NS_NewISupportsArray(getter_AddRefs(folderArray));
|
|
NS_NewISupportsArray(getter_AddRefs(offlineFolderArray));
|
|
PRInt32 totalExpungedBytes=0;
|
|
PRInt32 offlineExpungedBytes =0;
|
|
PRInt32 localExpungedBytes = 0;
|
|
do
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> rootFolder;
|
|
rv = server->GetRootFolder(getter_AddRefs(rootFolder));
|
|
if(NS_SUCCEEDED(rv) && rootFolder)
|
|
{
|
|
rv = server->GetOfflineSupportLevel(&offlineSupportLevel);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
nsCOMPtr<nsISupportsArray> allDescendents;
|
|
NS_NewISupportsArray(getter_AddRefs(allDescendents));
|
|
rootFolder->ListDescendents(allDescendents);
|
|
PRUint32 cnt=0;
|
|
rv = allDescendents->Count(&cnt);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
PRUint32 expungedBytes=0;
|
|
|
|
if (offlineSupportLevel > 0)
|
|
{
|
|
PRUint32 flags;
|
|
for (PRUint32 i=0; i< cnt;i++)
|
|
{
|
|
nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
|
|
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
|
|
expungedBytes = 0;
|
|
folder->GetFlags(&flags);
|
|
if (flags & MSG_FOLDER_FLAG_OFFLINE)
|
|
folder->GetExpungedBytes(&expungedBytes);
|
|
if (expungedBytes > 0 )
|
|
{
|
|
offlineFolderArray->AppendElement(supports);
|
|
offlineExpungedBytes += expungedBytes;
|
|
}
|
|
}
|
|
}
|
|
else //pop or local
|
|
{
|
|
for (PRUint32 i=0; i< cnt;i++)
|
|
{
|
|
nsCOMPtr<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
|
|
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
|
|
folder->GetExpungedBytes(&expungedBytes);
|
|
if (expungedBytes > 0 )
|
|
{
|
|
folderArray->AppendElement(supports);
|
|
localExpungedBytes += expungedBytes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
server = do_QueryElementAt(allServers, ++serverIndex);
|
|
}
|
|
while (serverIndex < numServers);
|
|
totalExpungedBytes = localExpungedBytes + offlineExpungedBytes;
|
|
PRInt32 purgeThreshold;
|
|
rv = GetPurgeThreshold(&purgeThreshold);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (totalExpungedBytes > (purgeThreshold*1024))
|
|
{
|
|
PRBool okToCompact = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIPrefService> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
|
nsCOMPtr<nsIPrefBranch> branch;
|
|
pref->GetBranch("", getter_AddRefs(branch));
|
|
|
|
PRBool askBeforePurge;
|
|
branch->GetBoolPref(PREF_MAIL_PURGE_ASK, &askBeforePurge);
|
|
if (askBeforePurge)
|
|
{
|
|
nsCOMPtr <nsIStringBundle> bundle;
|
|
rv = GetBaseStringBundle(getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsXPIDLString dialogTitle;
|
|
nsXPIDLString confirmString;
|
|
nsXPIDLString checkboxText;
|
|
|
|
rv = bundle->GetStringFromName(NS_LITERAL_STRING("autoCompactAllFoldersTitle").get(), getter_Copies(dialogTitle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = bundle->GetStringFromName(NS_LITERAL_STRING("autoCompactAllFolders").get(), getter_Copies(confirmString));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = bundle->GetStringFromName(NS_LITERAL_STRING("autoCompactAllFoldersCheckbox").get(), getter_Copies(checkboxText));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
PRBool checkValue = PR_FALSE;
|
|
PRInt32 buttonPressed = 0;
|
|
|
|
nsCOMPtr<nsIPrompt> dialog;
|
|
rv = aWindow->GetPromptDialog(getter_AddRefs(dialog));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = dialog->ConfirmEx(dialogTitle.get(), confirmString.get(), nsIPrompt::STD_OK_CANCEL_BUTTONS,
|
|
nsnull, nsnull, nsnull, checkboxText.get(), &checkValue, &buttonPressed);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!buttonPressed)
|
|
{
|
|
okToCompact = PR_TRUE;
|
|
|
|
if (checkValue)
|
|
branch->SetBoolPref(PREF_MAIL_PURGE_ASK, PR_FALSE);
|
|
}
|
|
}
|
|
else
|
|
okToCompact = PR_TRUE;
|
|
|
|
if (okToCompact)
|
|
{
|
|
nsCOMPtr <nsIAtom> aboutToCompactAtom = do_GetAtom("AboutToCompact");
|
|
NotifyFolderEvent(aboutToCompactAtom);
|
|
|
|
if ( localExpungedBytes > 0)
|
|
{
|
|
nsCOMPtr <nsIMsgFolder> msgFolder =
|
|
do_QueryElementAt(folderArray, 0, &rv);
|
|
if (msgFolder && NS_SUCCEEDED(rv))
|
|
if (offlineExpungedBytes > 0)
|
|
msgFolder->CompactAll(nsnull, aWindow, folderArray, PR_TRUE, offlineFolderArray);
|
|
else
|
|
msgFolder->CompactAll(nsnull, aWindow, folderArray, PR_FALSE, nsnull);
|
|
}
|
|
else if (offlineExpungedBytes > 0)
|
|
CompactAllOfflineStores(aWindow, offlineFolderArray);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::CompactAllOfflineStores(nsIMsgWindow *aWindow, nsISupportsArray *aOfflineFolderArray)
|
|
{
|
|
nsresult rv= NS_OK;
|
|
nsCOMPtr <nsIMsgFolderCompactor> folderCompactor;
|
|
folderCompactor = do_CreateInstance(NS_MSGOFFLINESTORECOMPACTOR_CONTRACTID, &rv);
|
|
|
|
if (NS_SUCCEEDED(rv) && folderCompactor)
|
|
rv = folderCompactor->CompactAll(nsnull, aWindow, PR_TRUE, aOfflineFolderArray);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::GetPromptPurgeThreshold(PRBool *aPrompt)
|
|
{
|
|
NS_ENSURE_ARG(aPrompt);
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && prefBranch)
|
|
{
|
|
rv = prefBranch->GetBoolPref(PREF_MAIL_PROMPT_PURGE_THRESHOLD, aPrompt);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
*aPrompt = PR_FALSE;
|
|
rv = NS_OK;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::GetPurgeThreshold(PRInt32 *aThreshold)
|
|
{
|
|
NS_ENSURE_ARG(aThreshold);
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && prefBranch)
|
|
{
|
|
rv = prefBranch->GetIntPref(PREF_MAIL_PURGE_THRESHOLD, aThreshold);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
*aThreshold = 0;
|
|
rv = NS_OK;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP //called on the folder that is renamed or about to be deleted
|
|
nsMsgDBFolder::MatchOrChangeFilterDestination(nsIMsgFolder *newFolder, PRBool caseInsensitive, PRBool *found)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsXPIDLCString oldUri;
|
|
rv = GetURI(getter_Copies(oldUri));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsXPIDLCString newUri;
|
|
if (newFolder) //for matching uri's this will be null
|
|
{
|
|
rv = newFolder->GetURI(getter_Copies(newUri));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
|
|
nsCOMPtr<nsIMsgFilterList> filterList;
|
|
nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsISupportsArray> allServers;
|
|
rv = accountMgr->GetAllServers(getter_AddRefs(allServers));
|
|
if (NS_SUCCEEDED(rv) && allServers)
|
|
{
|
|
PRUint32 numServers;
|
|
rv = allServers->Count(&numServers);
|
|
for (PRUint32 serverIndex=0; serverIndex < numServers; serverIndex++)
|
|
{
|
|
nsCOMPtr <nsIMsgIncomingServer> server =
|
|
do_QueryElementAt(allServers, serverIndex, &rv);
|
|
if (server && NS_SUCCEEDED(rv))
|
|
{
|
|
PRBool canHaveFilters;
|
|
rv = server->GetCanHaveFilters(&canHaveFilters);
|
|
if (NS_SUCCEEDED(rv) && canHaveFilters)
|
|
{
|
|
rv = server->GetFilterList(nsnull, getter_AddRefs(filterList));
|
|
if (filterList && NS_SUCCEEDED(rv))
|
|
{
|
|
rv = filterList->MatchOrChangeFilterTarget(oldUri, newUri, caseInsensitive, found);
|
|
if (found && newFolder && newUri)
|
|
rv = filterList->SaveToDefaultFile();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetDBTransferInfo(nsIDBFolderInfo **aTransferInfo)
|
|
{
|
|
nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
|
|
nsCOMPtr <nsIMsgDatabase> db;
|
|
GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(db));
|
|
if (dbFolderInfo)
|
|
dbFolderInfo->GetTransferInfo(aTransferInfo);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetDBTransferInfo(nsIDBFolderInfo *aTransferInfo)
|
|
{
|
|
NS_ENSURE_ARG(aTransferInfo);
|
|
nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
|
|
nsCOMPtr <nsIMsgDatabase> db;
|
|
GetMsgDatabase(nsnull, getter_AddRefs(db));
|
|
if (db)
|
|
{
|
|
db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
|
|
if(dbFolderInfo)
|
|
dbFolderInfo->InitFromTransferInfo(aTransferInfo);
|
|
db->SetSummaryValid(PR_TRUE);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetStringProperty(const char *propertyName, char **propertyValue)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(propertyName);
|
|
NS_ENSURE_ARG_POINTER(propertyValue);
|
|
nsCOMPtr <nsIFileSpec> dbPath;
|
|
nsresult rv = GetFolderCacheKey(getter_AddRefs(dbPath));
|
|
if (dbPath)
|
|
{
|
|
nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
|
|
rv = GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement));
|
|
if (cacheElement) //try to get from cache
|
|
rv = cacheElement->GetStringProperty(propertyName, propertyValue);
|
|
if (NS_FAILED(rv)) //if failed, then try to get from db
|
|
{
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
PRBool exists;
|
|
rv = dbPath->Exists(&exists);
|
|
if (NS_FAILED(rv) || !exists)
|
|
return NS_MSG_ERROR_FOLDER_MISSING;
|
|
rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = folderInfo->GetCharPtrProperty(propertyName, propertyValue);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetStringProperty(const char *propertyName, const char *propertyValue)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(propertyName);
|
|
NS_ENSURE_ARG_POINTER(propertyValue);
|
|
|
|
nsCOMPtr <nsIFileSpec> dbPath;
|
|
GetFolderCacheKey(getter_AddRefs(dbPath));
|
|
if (dbPath)
|
|
{
|
|
nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
|
|
GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement));
|
|
if (cacheElement) //try to set in the cache
|
|
cacheElement->SetStringProperty(propertyName, propertyValue);
|
|
}
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
folderInfo->SetCharPtrProperty(propertyName, propertyValue);
|
|
db->Commit(nsMsgDBCommitType::kLargeCommit); //commiting the db also commits the cache
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
// sub-classes need to override
|
|
nsresult
|
|
nsMsgDBFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
|
|
{
|
|
return aJunkMailPlugin->ClassifyMessage(aURI, aMsgWindow, nsnull);
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin)
|
|
{
|
|
return aJunkMailPlugin->ClassifyMessages(aURICount, aURIArray, aMsgWindow, nsnull);
|
|
}
|
|
|
|
/**
|
|
* Call the filter plugins (XXX currently just one)
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::CallFilterPlugins(nsIMsgWindow *aMsgWindow, PRBool *aFiltersRun)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aFiltersRun);
|
|
*aFiltersRun = PR_FALSE;
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsCOMPtr<nsISpamSettings> spamSettings;
|
|
nsCOMPtr<nsIAbMDBDirectory> whiteListDirectory;
|
|
nsCOMPtr<nsIMsgHeaderParser> headerParser;
|
|
PRBool useWhiteList = PR_FALSE;
|
|
PRInt32 spamLevel = 0;
|
|
nsXPIDLCString whiteListAbURI;
|
|
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsXPIDLCString serverType;
|
|
server->GetType(getter_Copies(serverType));
|
|
|
|
|
|
// if this is the junk folder, or the trash folder
|
|
// don't analyze for spam, because we don't care
|
|
//
|
|
// if it's the sent, unsent, templates, or drafts,
|
|
// don't analyze for spam, because the user
|
|
// created that message
|
|
//
|
|
// if it's a public imap folder, or another users
|
|
// imap folder, don't analyze for spam, because
|
|
// it's not ours to analyze
|
|
if ( !(nsCRT::strcmp(serverType.get(), "rss")) ||
|
|
(mFlags & (MSG_FOLDER_FLAG_JUNK | MSG_FOLDER_FLAG_TRASH |
|
|
MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_QUEUE |
|
|
MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_TEMPLATES |
|
|
MSG_FOLDER_FLAG_IMAP_PUBLIC | MSG_FOLDER_FLAG_IMAP_OTHER_USER)
|
|
&& !(mFlags & MSG_FOLDER_FLAG_INBOX)) )
|
|
return NS_OK;
|
|
|
|
|
|
rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
|
|
nsCOMPtr <nsIMsgFilterPlugin> filterPlugin;
|
|
server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin));
|
|
if (!filterPlugin) // it's not an error not to have the filter plugin.
|
|
return NS_OK;
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr <nsIJunkMailPlugin> junkMailPlugin = do_QueryInterface(filterPlugin);
|
|
|
|
if (junkMailPlugin)
|
|
{
|
|
PRBool userHasClassified = PR_FALSE;
|
|
// if the user has not classified any messages yet, then we shouldn't bother
|
|
// running the junk mail controls. This creates a better first use experience.
|
|
// See Bug #250084.
|
|
junkMailPlugin->GetUserHasClassified(&userHasClassified);
|
|
if (!userHasClassified)
|
|
return NS_OK;
|
|
}
|
|
|
|
spamSettings->GetLevel(&spamLevel);
|
|
if (!spamLevel)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIMsgMailSession> mailSession =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!mDatabase)
|
|
{
|
|
rv = GetDatabase(nsnull); // XXX is nsnull a reasonable arg here?
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// get the list of new messages
|
|
//
|
|
PRUint32 numNewKeys;
|
|
PRUint32 *newKeys;
|
|
rv = mDatabase->GetNewList(&numNewKeys, &newKeys);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsMsgKeyArray newMessageKeys;
|
|
if (numNewKeys)
|
|
newMessageKeys.Add(newKeys, numNewKeys);
|
|
|
|
newMessageKeys.InsertAt(0, &m_saveNewMsgs);
|
|
// if there weren't any, just return
|
|
//
|
|
if (!newMessageKeys.GetSize())
|
|
return NS_OK;
|
|
|
|
spamSettings->GetUseWhiteList(&useWhiteList);
|
|
if (useWhiteList)
|
|
{
|
|
spamSettings->GetWhiteListAbURI(getter_Copies(whiteListAbURI));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!whiteListAbURI.IsEmpty())
|
|
{
|
|
nsCOMPtr <nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr <nsIRDFResource> resource;
|
|
rv = rdfService->GetResource(whiteListAbURI, getter_AddRefs(resource));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
whiteListDirectory = do_QueryInterface(resource, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
// if we can't get the db, we probably want to continue firing spam filters.
|
|
}
|
|
|
|
nsXPIDLCString trustedMailDomains;
|
|
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
|
|
if (prefBranch)
|
|
prefBranch->GetCharPref("mail.trusteddomains", getter_Copies(trustedMailDomains));
|
|
|
|
if (whiteListDirectory || !trustedMailDomains.IsEmpty())
|
|
{
|
|
headerParser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
// build up list of keys to classify
|
|
//
|
|
nsXPIDLCString uri;
|
|
nsMsgKeyArray keysToClassify;
|
|
|
|
PRUint32 numNewMessages = newMessageKeys.GetSize();
|
|
for ( PRUint32 i=0 ; i < numNewMessages ; ++i )
|
|
{
|
|
nsXPIDLCString junkScore;
|
|
nsCOMPtr <nsIMsgDBHdr> msgHdr;
|
|
nsMsgKey msgKey = newMessageKeys.GetAt(i);
|
|
rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(msgHdr));
|
|
if (!NS_SUCCEEDED(rv))
|
|
continue;
|
|
nsXPIDLCString author;
|
|
nsXPIDLCString authorEmailAddress;
|
|
if (whiteListDirectory || !trustedMailDomains.IsEmpty())
|
|
{
|
|
msgHdr->GetAuthor(getter_Copies(author));
|
|
rv = headerParser->ExtractHeaderAddressMailboxes(nsnull, author.get(), getter_Copies(authorEmailAddress));
|
|
}
|
|
|
|
if (!trustedMailDomains.IsEmpty())
|
|
{
|
|
nsCAutoString domain;
|
|
PRInt32 atPos = authorEmailAddress.FindChar('@');
|
|
if (atPos >= 0)
|
|
authorEmailAddress.Right(domain, authorEmailAddress.Length() - atPos - 1);
|
|
if (!domain.IsEmpty() && MsgHostDomainIsTrusted(domain, trustedMailDomains))
|
|
{
|
|
// mark this msg as non-junk, because we whitelisted it.
|
|
mDatabase->SetStringProperty(msgKey, "junkscore", "0");
|
|
mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
|
|
continue; // skip this msg since it's in the white list
|
|
}
|
|
}
|
|
msgHdr->GetStringProperty("junkscore", getter_Copies(junkScore));
|
|
if (!junkScore.IsEmpty()) // ignore already scored messages.
|
|
continue;
|
|
// check whitelist first:
|
|
if (whiteListDirectory)
|
|
{
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRBool cardExists = PR_FALSE;
|
|
// don't want to abort the rest of the scoring.
|
|
if (!authorEmailAddress.IsEmpty())
|
|
rv = whiteListDirectory->HasCardForEmailAddress(authorEmailAddress, &cardExists);
|
|
if (NS_SUCCEEDED(rv) && cardExists)
|
|
{
|
|
// mark this msg as non-junk, because we whitelisted it.
|
|
mDatabase->SetStringProperty(msgKey, "junkscore", "0");
|
|
mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin");
|
|
continue; // skip this msg since it's in the white list
|
|
}
|
|
}
|
|
}
|
|
|
|
keysToClassify.Add(newMessageKeys.GetAt(i));
|
|
|
|
}
|
|
|
|
if (keysToClassify.GetSize() > 0)
|
|
{
|
|
PRUint32 numMessagesToClassify = keysToClassify.GetSize();
|
|
char ** messageURIs = (char **) PR_MALLOC(sizeof(const char *) * numMessagesToClassify);
|
|
if (!messageURIs)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
for ( PRUint32 msgIndex=0 ; msgIndex < numMessagesToClassify ; ++msgIndex )
|
|
{
|
|
// generate a URI for the message
|
|
//
|
|
rv = GenerateMessageURI(keysToClassify.GetAt(msgIndex), &messageURIs[msgIndex]);
|
|
if (NS_FAILED(rv))
|
|
NS_WARNING("nsMsgDBFolder::CallFilterPlugins(): could not"
|
|
" generate URI for message");
|
|
}
|
|
// filterMsgs
|
|
//
|
|
*aFiltersRun = PR_TRUE;
|
|
rv = SpamFilterClassifyMessages((const char **) messageURIs, numMessagesToClassify, aMsgWindow, junkMailPlugin);
|
|
|
|
for ( PRUint32 freeIndex=0 ; freeIndex < numMessagesToClassify ; ++freeIndex )
|
|
PR_Free(messageURIs[freeIndex]);
|
|
PR_Free(messageURIs);
|
|
|
|
}
|
|
m_saveNewMsgs.RemoveAll();
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetLastMessageLoaded(nsMsgKey *aMsgKey)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aMsgKey);
|
|
*aMsgKey = mLastMessageLoaded;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetLastMessageLoaded(nsMsgKey aMsgKey)
|
|
{
|
|
mLastMessageLoaded = aMsgKey;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::PromptForCachePassword(nsIMsgIncomingServer *server, nsIMsgWindow *aWindow, PRBool &passwordCorrect)
|
|
{
|
|
PRBool userDidntCancel;
|
|
passwordCorrect = PR_FALSE;
|
|
nsCOMPtr <nsIStringBundle> bundle;
|
|
nsresult rv = GetBaseStringBundle(getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsXPIDLCString hostName;
|
|
nsXPIDLCString userName;
|
|
nsXPIDLString passwordTemplate;
|
|
nsXPIDLCString password;
|
|
nsXPIDLString passwordTitle;
|
|
nsXPIDLString passwordPromptString;
|
|
|
|
server->GetRealHostName(getter_Copies(hostName));
|
|
server->GetRealUsername(getter_Copies(userName));
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("passwordTitle").get(), getter_Copies(passwordTitle));
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("passwordPrompt").get(), getter_Copies(passwordTemplate));
|
|
|
|
NS_ConvertASCIItoUCS2 userNameStr(userName);
|
|
NS_ConvertASCIItoUCS2 hostNameStr(hostName);
|
|
|
|
const PRUnichar *stringParams[2] = { userNameStr.get(), hostNameStr.get() };
|
|
|
|
rv = bundle->FormatStringFromName(
|
|
NS_LITERAL_STRING("passwordPrompt").get(), stringParams, 2,
|
|
getter_Copies(passwordPromptString ));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
do
|
|
{
|
|
rv = server->GetPasswordWithUI(passwordPromptString,
|
|
passwordTitle,
|
|
aWindow,
|
|
&userDidntCancel,
|
|
getter_Copies(password));
|
|
if (rv != NS_MSG_PASSWORD_PROMPT_CANCELLED && !password.IsEmpty())
|
|
{
|
|
nsCOMPtr <nsIPasswordManagerInternal> passwordMgrInt = do_GetService(NS_PASSWORDMANAGER_CONTRACTID, &rv);
|
|
if(passwordMgrInt)
|
|
{
|
|
|
|
// Get the current server URI
|
|
nsXPIDLCString currServerUri;
|
|
rv = server->GetServerURI(getter_Copies(currServerUri));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
currServerUri.Insert('x', 0);
|
|
nsCAutoString hostFound;
|
|
nsAutoString userNameFound;
|
|
nsAutoString passwordFound;
|
|
|
|
const nsAFlatString& empty = EmptyString();
|
|
|
|
// Get password entry corresponding to the host URI we are passing in.
|
|
rv = passwordMgrInt->FindPasswordEntry(currServerUri, empty, empty,
|
|
hostFound, userNameFound,
|
|
passwordFound);
|
|
if (NS_FAILED(rv))
|
|
break;
|
|
// compare the user-entered password with the saved password with
|
|
// the munged uri.
|
|
passwordCorrect = password.Equals(NS_ConvertUCS2toUTF8(passwordFound).get());
|
|
if (!passwordCorrect)
|
|
server->SetPassword("");
|
|
else
|
|
{
|
|
nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
|
|
if (accountManager)
|
|
accountManager->SetUserNeedsToAuthenticate(PR_FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (NS_SUCCEEDED(rv) && rv != NS_MSG_PASSWORD_PROMPT_CANCELLED && userDidntCancel && !passwordCorrect);
|
|
return (!passwordCorrect) ? NS_ERROR_FAILURE : rv;
|
|
}
|
|
|
|
// this gets called after the last junk mail classification has run.
|
|
nsresult nsMsgDBFolder::PerformBiffNotifications(void)
|
|
{
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
PRInt32 numBiffMsgs = 0;
|
|
nsCOMPtr<nsIMsgFolder> root;
|
|
rv = GetRootFolder(getter_AddRefs(root));
|
|
root->GetNumNewMessages(PR_TRUE, &numBiffMsgs);
|
|
if (numBiffMsgs > 0)
|
|
{
|
|
server->SetPerformingBiff(true);
|
|
SetBiffState(nsIMsgFolder::nsMsgBiffState_NewMail);
|
|
server->SetPerformingBiff(false);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::initializeStrings()
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIStringBundleService> bundleService =
|
|
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
rv = bundleService->CreateBundle("chrome://messenger/locale/messenger.properties",
|
|
getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("inboxFolderName").get(),
|
|
&kLocalizedInboxName);
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("trashFolderName").get(),
|
|
&kLocalizedTrashName);
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("sentFolderName").get(),
|
|
&kLocalizedSentName);
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("draftsFolderName").get(),
|
|
&kLocalizedDraftsName);
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("templatesFolderName").get(),
|
|
&kLocalizedTemplatesName);
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("junkFolderName").get(),
|
|
&kLocalizedJunkName);
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("unsentFolderName").get(),
|
|
&kLocalizedUnsentName);
|
|
|
|
nsCOMPtr<nsIStringBundle> brandBundle;
|
|
rv = bundleService->CreateBundle("chrome://branding/locale/brand.properties", getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
|
|
&kLocalizedBrandShortName);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::createCollationKeyGenerator()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
nsCOMPtr<nsILocaleService> localeSvc = do_GetService(NS_LOCALESERVICE_CONTRACTID,&rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsILocale> locale;
|
|
rv = localeSvc->GetApplicationLocale(getter_AddRefs(locale));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr <nsICollationFactory> factory = do_CreateInstance(kCollationFactoryCID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = factory->CreateCollation(locale, &gCollationKeyGenerator);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::Init(const char* aURI)
|
|
{
|
|
// for now, just initialize everything during Init()
|
|
|
|
nsresult rv;
|
|
|
|
rv = nsRDFResource::Init(aURI);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = CreateBaseMessageURI(aURI);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::CreateBaseMessageURI(const char *aURI)
|
|
{
|
|
// Each folder needs to implement this.
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsISerializable methods:
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::Read(nsIObjectInputStream *aStream)
|
|
{
|
|
NS_NOTREACHED("nsMsgDBFolder::Read");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::Write(nsIObjectOutputStream *aStream)
|
|
{
|
|
NS_NOTREACHED("nsMsgDBFolder::Write");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
// nsICollection methods:
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::Count(PRUint32 *result)
|
|
{
|
|
return mSubFolders->Count(result);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetElementAt(PRUint32 i, nsISupports* *result)
|
|
{
|
|
return mSubFolders->GetElementAt(i, result);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::QueryElementAt(PRUint32 i, const nsIID & iid, void * *result)
|
|
{
|
|
return mSubFolders->QueryElementAt(i, iid, result);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetElementAt(PRUint32 i, nsISupports* value)
|
|
{
|
|
return mSubFolders->SetElementAt(i, value);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::AppendElement(nsISupports *aElement)
|
|
{
|
|
return mSubFolders->AppendElement(aElement);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::RemoveElement(nsISupports *aElement)
|
|
{
|
|
return mSubFolders->RemoveElement(aElement);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::Enumerate(nsIEnumerator* *result)
|
|
{
|
|
// nsMsgDBFolders only have subfolders, no message elements
|
|
return mSubFolders->Enumerate(result);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::Clear(void)
|
|
{
|
|
return mSubFolders->Clear();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetURI(char* *name)
|
|
{
|
|
return nsRDFResource::GetValue(name);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef PRBool
|
|
(*nsArrayFilter)(nsISupports* element, void* data);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetSubFolders(nsIEnumerator* *result)
|
|
{
|
|
return mSubFolders->Enumerate(result);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::FindSubFolder(const nsACString& aEscapedSubFolderName, nsIMsgFolder **aFolder)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// XXX use necko here
|
|
nsCAutoString uri;
|
|
uri.Append(mURI);
|
|
uri.Append('/');
|
|
uri.Append(aEscapedSubFolderName);
|
|
|
|
nsCOMPtr<nsIRDFResource> res;
|
|
rv = rdf->GetResource(uri, getter_AddRefs(res));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
if (!aFolder)
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
*aFolder = folder;
|
|
NS_ADDREF(*aFolder);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetHasSubFolders(PRBool *_retval)
|
|
{
|
|
PRUint32 cnt;
|
|
nsresult rv = mSubFolders->Count(&cnt);
|
|
if (NS_FAILED(rv)) return rv;
|
|
*_retval = (cnt > 0);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::AddFolderListener(nsIFolderListener * listener)
|
|
{
|
|
return mListeners.AppendElement(listener) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::RemoveFolderListener(nsIFolderListener * listener)
|
|
{
|
|
|
|
mListeners.RemoveElement(listener);
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetParent(nsIMsgFolder *aParent)
|
|
{
|
|
mParent = do_GetWeakReference(aParent);
|
|
|
|
if (aParent)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgFolder> parentMsgFolder = do_QueryInterface(aParent, &rv);
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
|
|
// servers do not have parents, so we must not be a server
|
|
mIsServer = PR_FALSE;
|
|
mIsServerIsValid = PR_TRUE;
|
|
|
|
// also set the server itself while we're here.
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = parentMsgFolder->GetServer(getter_AddRefs(server));
|
|
if (NS_SUCCEEDED(rv) && server)
|
|
mServer = do_GetWeakReference(server);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetParent(nsIMsgFolder **aParent)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aParent);
|
|
|
|
nsCOMPtr<nsIMsgFolder> parent = do_QueryReferent(mParent);
|
|
|
|
*aParent = parent;
|
|
NS_IF_ADDREF(*aParent);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetParentMsgFolder(nsIMsgFolder **aParentMsgFolder)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aParentMsgFolder);
|
|
|
|
nsCOMPtr<nsIMsgFolder> parent = do_QueryReferent(mParent);
|
|
|
|
NS_IF_ADDREF(*aParentMsgFolder = parent);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetMessages(nsIMsgWindow *aMsgWindow, nsISimpleEnumerator* *result)
|
|
{
|
|
// XXX should this return an empty enumeration?
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::UpdateFolder(nsIMsgWindow *)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetFolderURL(char **url)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(url);
|
|
*url = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetServer(nsIMsgIncomingServer ** aServer)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aServer);
|
|
|
|
nsresult rv;
|
|
|
|
// short circut the server if we have it.
|
|
nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(mServer, &rv);
|
|
if (NS_FAILED(rv) || !server)
|
|
{
|
|
// try again after parsing the URI
|
|
rv = parseURI(PR_TRUE);
|
|
server = do_QueryReferent(mServer);
|
|
}
|
|
|
|
*aServer = server;
|
|
NS_IF_ADDREF(*aServer);
|
|
|
|
return (server) ? NS_OK : NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
#ifdef MSG_FASTER_URI_PARSING
|
|
class nsMsgAutoBool {
|
|
public:
|
|
nsMsgAutoBool() : mValue(nsnull) {}
|
|
void autoReset(PRBool *aValue) { mValue = aValue; }
|
|
~nsMsgAutoBool() { if (mValue) *mValue = PR_FALSE; }
|
|
private:
|
|
PRBool *mValue;
|
|
};
|
|
#endif
|
|
|
|
nsresult
|
|
nsMsgDBFolder::parseURI(PRBool needServer)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIURL> url;
|
|
|
|
#ifdef MSG_FASTER_URI_PARSING
|
|
nsMsgAutoBool parsingUrlState;
|
|
if (mParsingURLInUse)
|
|
{
|
|
url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
|
|
}
|
|
else
|
|
{
|
|
url = mParsingURL;
|
|
mParsingURLInUse = PR_TRUE;
|
|
parsingUrlState.autoReset(&mParsingURLInUse);
|
|
}
|
|
|
|
#else
|
|
url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
#endif
|
|
|
|
rv = url->SetSpec(nsDependentCString(mURI));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
//
|
|
// pull some info out of the URI
|
|
//
|
|
|
|
// empty path tells us it's a server.
|
|
if (!mIsServerIsValid)
|
|
{
|
|
nsCAutoString path;
|
|
rv = url->GetPath(path);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
if (!strcmp(path.get(), "/"))
|
|
mIsServer = PR_TRUE;
|
|
else
|
|
mIsServer = PR_FALSE;
|
|
}
|
|
mIsServerIsValid = PR_TRUE;
|
|
}
|
|
|
|
// grab the name off the leaf of the server
|
|
if (mName.IsEmpty())
|
|
{
|
|
// mName:
|
|
// the name is the trailing directory in the path
|
|
nsCAutoString fileName;
|
|
url->GetFileName(fileName);
|
|
if (!fileName.IsEmpty())
|
|
{
|
|
// XXX conversion to unicode here? is fileName in UTF8?
|
|
// yes, let's say it is in utf8
|
|
NS_UnescapeURL((char *)fileName.get());
|
|
NS_ASSERTION(IsUTF8(fileName), "fileName is not in UTF-8");
|
|
CopyUTF8toUTF16(fileName, mName);
|
|
}
|
|
}
|
|
|
|
// grab the server by parsing the URI and looking it up
|
|
// in the account manager...
|
|
// But avoid this extra work by first asking the parent, if any
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(mServer, &rv);
|
|
if (NS_FAILED(rv) || !server)
|
|
{
|
|
// first try asking the parent instead of the URI
|
|
nsCOMPtr<nsIMsgFolder> parentMsgFolder;
|
|
rv = GetParentMsgFolder(getter_AddRefs(parentMsgFolder));
|
|
|
|
if (NS_SUCCEEDED(rv) && parentMsgFolder)
|
|
rv = parentMsgFolder->GetServer(getter_AddRefs(server));
|
|
|
|
// no parent. do the extra work of asking
|
|
if (!server && needServer)
|
|
{
|
|
nsCOMPtr<nsIMsgAccountManager> accountManager =
|
|
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
url->SetScheme(nsDependentCString(GetIncomingServerType()));
|
|
rv = accountManager->FindServerByURI(url, PR_FALSE,
|
|
getter_AddRefs(server));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
}
|
|
|
|
mServer = do_GetWeakReference(server);
|
|
|
|
} /* !mServer */
|
|
|
|
// now try to find the local path for this folder
|
|
if (server)
|
|
{
|
|
nsCAutoString newPath;
|
|
|
|
nsCAutoString urlPath;
|
|
url->GetFilePath(urlPath);
|
|
if (!urlPath.IsEmpty())
|
|
{
|
|
NS_UnescapeURL((char *) urlPath.get());
|
|
|
|
// transform the filepath from the URI, such as
|
|
// "/folder1/folder2/foldern"
|
|
// to
|
|
// "folder1.sbd/folder2.sbd/foldern"
|
|
// (remove leading / and add .sbd to first n-1 folders)
|
|
// to be appended onto the server's path
|
|
|
|
PRBool isNewsFolder = PR_FALSE;
|
|
nsCAutoString scheme;
|
|
if (NS_SUCCEEDED(url->GetScheme(scheme)))
|
|
{
|
|
isNewsFolder = scheme.EqualsLiteral("news") ||
|
|
scheme.EqualsLiteral("snews") ||
|
|
scheme.EqualsLiteral("nntp");
|
|
}
|
|
|
|
NS_MsgCreatePathStringFromFolderURI(urlPath.get(), newPath, isNewsFolder);
|
|
}
|
|
|
|
// now append munged path onto server path
|
|
nsCOMPtr<nsIFileSpec> serverPath;
|
|
rv = server->GetLocalPath(getter_AddRefs(serverPath));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (serverPath)
|
|
{
|
|
if (!newPath.IsEmpty())
|
|
{
|
|
rv = serverPath->AppendRelativeUnixPath(newPath.get());
|
|
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to append to the serverPath");
|
|
if (NS_FAILED(rv))
|
|
{
|
|
mPath = nsnull;
|
|
return rv;
|
|
}
|
|
}
|
|
mPath = serverPath;
|
|
}
|
|
|
|
// URI is completely parsed when we've attempted to get the server
|
|
mHaveParsedURI=PR_TRUE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetIsServer(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
// make sure we've parsed the URI
|
|
if (!mIsServerIsValid)
|
|
{
|
|
nsresult rv = parseURI();
|
|
if (NS_FAILED(rv) || !mIsServerIsValid)
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
*aResult = mIsServer;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetNoSelect(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetImapShared(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
return GetFlag(MSG_FOLDER_FLAG_PERSONAL_SHARED, aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetCanSubscribe(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
// by default, you can't subscribe.
|
|
// if otherwise, override it.
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetCanFileMessages(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
//varada - checking folder flag to see if it is the "Unsent Messages"
|
|
//and if so return FALSE
|
|
if (mFlags & (MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_VIRTUAL))
|
|
{
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
PRBool isServer = PR_FALSE;
|
|
nsresult rv = GetIsServer(&isServer);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// by default, you can't file messages into servers, only to folders
|
|
// if otherwise, override it.
|
|
*aResult = !isServer;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetCanDeleteMessages(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
*aResult = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetCanCreateSubfolders(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
//Checking folder flag to see if it is the "Unsent Messages"
|
|
//or a virtual folder, and if so return FALSE
|
|
if (mFlags & (MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_VIRTUAL))
|
|
{
|
|
*aResult = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
// by default, you can create subfolders on server and folders
|
|
// if otherwise, override it.
|
|
*aResult = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetCanRename(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
PRBool isServer = PR_FALSE;
|
|
nsresult rv = GetIsServer(&isServer);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// by default, you can't rename servers, only folders
|
|
// if otherwise, override it.
|
|
if (isServer)
|
|
{
|
|
*aResult = PR_FALSE;
|
|
}
|
|
//
|
|
// check if the folder is a special folder
|
|
// (Trash, Drafts, Unsent Messages, Inbox, Sent, Templates, Junk)
|
|
// if it is, don't allow the user to rename it
|
|
// (which includes dnd moving it with in the same server)
|
|
//
|
|
// this errors on the side of caution. we'll return false a lot
|
|
// more often if we use flags,
|
|
// instead of checking if the folder really is being used as a
|
|
// special folder by looking at the "copies and folders" prefs on the
|
|
// identities.
|
|
//
|
|
// one day...
|
|
else if (mFlags & MSG_FOLDER_FLAG_TRASH ||
|
|
mFlags & MSG_FOLDER_FLAG_DRAFTS ||
|
|
mFlags & MSG_FOLDER_FLAG_QUEUE ||
|
|
mFlags & MSG_FOLDER_FLAG_INBOX ||
|
|
mFlags & MSG_FOLDER_FLAG_SENTMAIL ||
|
|
mFlags & MSG_FOLDER_FLAG_TEMPLATES ||
|
|
mFlags & MSG_FOLDER_FLAG_JUNK)
|
|
{
|
|
*aResult = PR_FALSE;
|
|
}
|
|
else
|
|
{
|
|
*aResult = PR_TRUE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetCanCompact(PRBool *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
PRBool isServer = PR_FALSE;
|
|
nsresult rv = GetIsServer(&isServer);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
// servers cannot be compacted --> 4.x
|
|
// virtual search folders cannot be compacted
|
|
*aResult = !isServer && !(mFlags & MSG_FOLDER_FLAG_VIRTUAL);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetPrettyName(PRUnichar ** name)
|
|
{
|
|
return GetName(name);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetPrettyName(const PRUnichar *name)
|
|
{
|
|
nsresult rv;
|
|
nsAutoString unicodeName(name);
|
|
|
|
//Set pretty name only if special flag is set and if it the default folder name
|
|
if (mFlags & MSG_FOLDER_FLAG_INBOX && unicodeName.LowerCaseEqualsLiteral("inbox"))
|
|
rv = SetName(kLocalizedInboxName);
|
|
else if (mFlags & MSG_FOLDER_FLAG_SENTMAIL && unicodeName.LowerCaseEqualsLiteral("sent"))
|
|
rv = SetName(kLocalizedSentName);
|
|
//netscape webmail uses "Draft" instead of "Drafts"
|
|
else if (mFlags & MSG_FOLDER_FLAG_DRAFTS && (unicodeName.LowerCaseEqualsLiteral("drafts")
|
|
|| unicodeName.LowerCaseEqualsLiteral("draft")))
|
|
rv = SetName(kLocalizedDraftsName);
|
|
else if (mFlags & MSG_FOLDER_FLAG_TEMPLATES && unicodeName.LowerCaseEqualsLiteral("templates"))
|
|
rv = SetName(kLocalizedTemplatesName);
|
|
else if (mFlags & MSG_FOLDER_FLAG_TRASH && unicodeName.LowerCaseEqualsLiteral("trash"))
|
|
rv = SetName(kLocalizedTrashName);
|
|
else if (mFlags & MSG_FOLDER_FLAG_QUEUE && unicodeName.LowerCaseEqualsLiteral("unsent messages"))
|
|
rv = SetName(kLocalizedUnsentName);
|
|
else if (mFlags & MSG_FOLDER_FLAG_JUNK && unicodeName.LowerCaseEqualsLiteral("junk"))
|
|
rv = SetName(kLocalizedJunkName);
|
|
else
|
|
rv = SetName(name);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetName(PRUnichar **name)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(name);
|
|
|
|
nsresult rv;
|
|
if (!mHaveParsedURI && mName.IsEmpty())
|
|
{
|
|
rv = parseURI();
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
|
|
// if it's a server, just forward the call
|
|
if (mIsServer)
|
|
{
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = GetServer(getter_AddRefs(server));
|
|
if (NS_SUCCEEDED(rv) && server)
|
|
return server->GetPrettyName(name);
|
|
}
|
|
|
|
*name = ToNewUnicode(mName);
|
|
|
|
if (!(*name)) return NS_ERROR_OUT_OF_MEMORY;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetName(const PRUnichar * name)
|
|
{
|
|
// override the URI-generated name
|
|
if (!mName.Equals(name))
|
|
{
|
|
mName = name;
|
|
|
|
// old/new value doesn't matter here
|
|
NotifyUnicharPropertyChanged(kNameAtom, name, name);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//For default, just return name
|
|
NS_IMETHODIMP nsMsgDBFolder::GetAbbreviatedName(PRUnichar * *aAbbreviatedName)
|
|
{
|
|
return GetName(aAbbreviatedName);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetChildNamed(const PRUnichar *name, nsISupports ** aChild)
|
|
{
|
|
NS_ASSERTION(aChild, "NULL child");
|
|
nsresult rv;
|
|
// will return nsnull if we can't find it
|
|
*aChild = nsnull;
|
|
|
|
PRUint32 count;
|
|
rv = mSubFolders->Count(&count);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsXPIDLString folderName;
|
|
|
|
rv = folder->GetName(getter_Copies(folderName));
|
|
// case-insensitive compare is probably LCD across OS filesystems
|
|
if (NS_SUCCEEDED(rv) &&
|
|
folderName.Equals(name, nsCaseInsensitiveStringComparator()))
|
|
{
|
|
NS_ADDREF(*aChild = folder);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
// don't return NS_OK if we didn't find the folder
|
|
// see http://bugzilla.mozilla.org/show_bug.cgi?id=210089#c15
|
|
// and http://bugzilla.mozilla.org/show_bug.cgi?id=210089#c17
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetChildWithURI(const char *uri, PRBool deep, PRBool caseInsensitive, nsIMsgFolder ** child)
|
|
{
|
|
NS_ASSERTION(child, "NULL child");
|
|
nsresult rv;
|
|
// will return nsnull if we can't find it
|
|
*child = nsnull;
|
|
|
|
nsCOMPtr <nsIEnumerator> aEnumerator;
|
|
|
|
rv = GetSubFolders(getter_AddRefs(aEnumerator));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsISupports> aItem;
|
|
|
|
rv = aEnumerator->First();
|
|
if (NS_FAILED(rv))
|
|
return NS_OK; // it's OK, there are no sub-folders.
|
|
|
|
while(NS_SUCCEEDED(rv))
|
|
{
|
|
rv = aEnumerator->CurrentItem(getter_AddRefs(aItem));
|
|
if (NS_FAILED(rv)) break;
|
|
nsCOMPtr<nsIRDFResource> folderResource = do_QueryInterface(aItem);
|
|
nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(aItem);
|
|
if (folderResource && folder)
|
|
{
|
|
const char *folderURI;
|
|
rv = folderResource->GetValueConst(&folderURI);
|
|
if (NS_FAILED(rv)) return rv;
|
|
PRBool equal;
|
|
equal = folderURI &&
|
|
(caseInsensitive
|
|
? nsCRT::strcasecmp(folderURI, uri)
|
|
: nsCRT::strcmp(folderURI, uri)) == 0;
|
|
if (equal)
|
|
{
|
|
*child = folder;
|
|
NS_ADDREF(*child);
|
|
return NS_OK;
|
|
}
|
|
if (deep)
|
|
{
|
|
rv = folder->GetChildWithURI(uri, deep, caseInsensitive, child);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (*child)
|
|
return NS_OK;
|
|
}
|
|
}
|
|
rv = aEnumerator->Next();
|
|
if (NS_FAILED(rv))
|
|
{
|
|
rv = NS_OK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetPrettiestName(PRUnichar **name)
|
|
{
|
|
if (NS_SUCCEEDED(GetPrettyName(name)))
|
|
return NS_OK;
|
|
return GetName(name);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetShowDeletedMessages(PRBool *showDeletedMessages)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(showDeletedMessages);
|
|
|
|
*showDeletedMessages = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::Delete()
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::DeleteSubFolders(nsISupportsArray *folders,
|
|
nsIMsgWindow *msgWindow)
|
|
{
|
|
nsresult rv;
|
|
|
|
PRUint32 count;
|
|
rv = folders->Count(&count);
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(folders, i, &rv));
|
|
if (folder)
|
|
PropagateDelete(folder, PR_TRUE, msgWindow);
|
|
}
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::CreateStorageIfMissing(nsIUrlListener* /* urlListener */)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "needs to be overridden");
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::PropagateDelete(nsIMsgFolder *folder, PRBool deleteStorage, nsIMsgWindow *msgWindow)
|
|
{
|
|
nsresult status = NS_OK;
|
|
|
|
nsCOMPtr<nsIMsgFolder> child;
|
|
|
|
// first, find the folder we're looking to delete
|
|
PRUint32 cnt;
|
|
nsresult rv = mSubFolders->Count(&cnt);
|
|
if (NS_FAILED(rv)) return rv;
|
|
for (PRUint32 i = 0; i < cnt; i++)
|
|
{
|
|
nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
|
|
child = do_QueryInterface(supports, &status);
|
|
if (NS_SUCCEEDED(status))
|
|
{
|
|
if (folder == child.get())
|
|
{
|
|
//Remove self as parent
|
|
child->SetParent(nsnull);
|
|
|
|
// maybe delete disk storage for it, and its subfolders
|
|
status = child->RecursiveDelete(deleteStorage, msgWindow);
|
|
|
|
if (status == NS_OK)
|
|
{
|
|
//Remove from list of subfolders.
|
|
mSubFolders->RemoveElement(supports);
|
|
NotifyItemRemoved(supports);
|
|
break;
|
|
}
|
|
else
|
|
{ // setting parent back if we failed
|
|
child->SetParent(this);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = child->PropagateDelete (folder, deleteStorage, msgWindow);
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::RecursiveDelete(PRBool deleteStorage, nsIMsgWindow *msgWindow)
|
|
{
|
|
// If deleteStorage is PR_TRUE, recursively deletes disk storage for this folder
|
|
// and all its subfolders.
|
|
// Regardless of deleteStorage, always unlinks them from the children lists and
|
|
// frees memory for the subfolders but NOT for _this_
|
|
|
|
nsresult status = NS_OK;
|
|
nsCOMPtr <nsIFileSpec> dbPath;
|
|
|
|
// first remove the deleted folder from the folder cache;
|
|
nsresult result = GetFolderCacheKey(getter_AddRefs(dbPath));
|
|
|
|
nsCOMPtr<nsIMsgAccountManager> accountMgr =
|
|
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &result);
|
|
if(NS_SUCCEEDED(result))
|
|
{
|
|
nsCOMPtr <nsIMsgFolderCache> folderCache;
|
|
result = accountMgr->GetFolderCache(getter_AddRefs(folderCache));
|
|
if (NS_SUCCEEDED(result) && folderCache)
|
|
{
|
|
nsXPIDLCString persistentPath;
|
|
dbPath->GetPersistentDescriptorString(getter_Copies(persistentPath));
|
|
folderCache->RemoveElement(persistentPath.get());
|
|
}
|
|
}
|
|
|
|
PRUint32 cnt;
|
|
nsresult rv = mSubFolders->Count(&cnt);
|
|
if (NS_FAILED(rv)) return rv;
|
|
while (cnt > 0)
|
|
{
|
|
nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(0));
|
|
nsCOMPtr<nsIMsgFolder> child(do_QueryInterface(supports, &status));
|
|
|
|
if (NS_SUCCEEDED(status))
|
|
{
|
|
child->SetParent(nsnull);
|
|
status = child->RecursiveDelete(deleteStorage,msgWindow); // recur
|
|
if (NS_SUCCEEDED(status))
|
|
mSubFolders->RemoveElement(supports); // unlink it from this's child list
|
|
else
|
|
{ // setting parent back if we failed for some reason
|
|
child->SetParent(this);
|
|
}
|
|
}
|
|
cnt--;
|
|
}
|
|
|
|
// now delete the disk storage for _this_
|
|
if (deleteStorage && (status == NS_OK))
|
|
{
|
|
status = Delete();
|
|
nsCOMPtr <nsISupports> supports;
|
|
QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports));
|
|
nsCOMPtr <nsIMsgFolderNotificationService> notifier = do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID);
|
|
if (notifier)
|
|
notifier->NotifyItemDeleted(supports);
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::CreateSubfolder(const PRUnichar *folderName, nsIMsgWindow *msgWindow )
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::AddSubfolder(const nsAString& name,
|
|
nsIMsgFolder** child)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(child);
|
|
|
|
PRInt32 flags = 0;
|
|
nsresult rv;
|
|
nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCAutoString uri(mURI);
|
|
uri.Append('/');
|
|
|
|
// URI should use UTF-8
|
|
// (see RFC2396 Uniform Resource Identifiers (URI): Generic Syntax)
|
|
nsCAutoString escapedName;
|
|
rv = NS_MsgEscapeEncodeURLPath(name, escapedName);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// fix for #192780
|
|
// if this is the root folder
|
|
// make sure the the special folders
|
|
// have the right uri.
|
|
// on disk, host\INBOX should be a folder with the uri mailbox://user@host/Inbox"
|
|
// as mailbox://user@host/Inbox != mailbox://user@host/INBOX
|
|
nsCOMPtr<nsIMsgFolder> rootFolder;
|
|
rv = GetRootFolder(getter_AddRefs(rootFolder));
|
|
if (NS_SUCCEEDED(rv) && rootFolder && (rootFolder.get() == (nsIMsgFolder *)this))
|
|
{
|
|
if (nsCRT::strcasecmp(escapedName.get(), "INBOX") == 0)
|
|
uri += "Inbox";
|
|
else if (nsCRT::strcasecmp(escapedName.get(), "UNSENT%20MESSAGES") == 0)
|
|
uri += "Unsent%20Messages";
|
|
else if (nsCRT::strcasecmp(escapedName.get(), "DRAFTS") == 0)
|
|
uri += "Drafts";
|
|
else if (nsCRT::strcasecmp(escapedName.get(), "TRASH") == 0)
|
|
uri += "Trash";
|
|
else if (nsCRT::strcasecmp(escapedName.get(), "SENT") == 0)
|
|
uri += "Sent";
|
|
else if (nsCRT::strcasecmp(escapedName.get(), "TEMPLATES") == 0)
|
|
uri +="Templates";
|
|
else
|
|
uri += escapedName.get();
|
|
}
|
|
else
|
|
uri += escapedName.get();
|
|
|
|
nsCOMPtr <nsIMsgFolder> msgFolder;
|
|
rv = GetChildWithURI(uri.get(), PR_FALSE/*deep*/, PR_TRUE /*case Insensitive*/, getter_AddRefs(msgFolder));
|
|
if (NS_SUCCEEDED(rv) && msgFolder)
|
|
return NS_MSG_FOLDER_EXISTS;
|
|
|
|
nsCOMPtr<nsIRDFResource> res;
|
|
rv = rdf->GetResource(uri, getter_AddRefs(res));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsFileSpec path;
|
|
// we just need to do this for the parent folder, i.e., "this".
|
|
rv = CreateDirectoryForFolder(path);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
folder->GetFlags((PRUint32 *)&flags);
|
|
|
|
flags |= MSG_FOLDER_FLAG_MAIL;
|
|
|
|
folder->SetParent(this);
|
|
|
|
PRBool isServer;
|
|
rv = GetIsServer(&isServer);
|
|
|
|
//Only set these is these are top level children.
|
|
if(NS_SUCCEEDED(rv) && isServer)
|
|
{
|
|
if(name.LowerCaseEqualsLiteral("inbox"))
|
|
{
|
|
flags |= MSG_FOLDER_FLAG_INBOX;
|
|
SetBiffState(nsIMsgFolder::nsMsgBiffState_Unknown);
|
|
}
|
|
else if (name.LowerCaseEqualsLiteral("trash"))
|
|
flags |= MSG_FOLDER_FLAG_TRASH;
|
|
else if (name.LowerCaseEqualsLiteral("unsent messages") ||
|
|
name.LowerCaseEqualsLiteral("outbox"))
|
|
flags |= MSG_FOLDER_FLAG_QUEUE;
|
|
#if 0
|
|
// the logic for this has been moved into
|
|
// SetFlagsOnDefaultMailboxes()
|
|
else if(name.EqualsIgnoreCase(NS_LITERAL_STRING("Sent"), nsCaseInsensitiveStringComparator()))
|
|
folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
|
|
else if(name.EqualsIgnoreCase(NS_LITERAL_STRING("Drafts"), nsCaseInsensitiveStringComparator()))
|
|
folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
|
|
else if(name.EqualsIgnoreCase(NS_LITERAL_STRING("Templates"), nsCaseInsensitiveStringComparator()))
|
|
folder->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
|
|
#endif
|
|
}
|
|
|
|
folder->SetFlags(flags);
|
|
|
|
//at this point we must be ok and we don't want to return failure in case GetIsServer failed.
|
|
rv = NS_OK;
|
|
|
|
nsCOMPtr<nsISupports> supports = do_QueryInterface(folder);
|
|
if(folder)
|
|
mSubFolders->AppendElement(supports);
|
|
*child = folder;
|
|
NS_ADDREF(*child);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray, PRBool aCompactOfflineAlso, nsISupportsArray *aCompactOfflineArray)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "should be overridden by child class");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::CheckIfFolderExists(const PRUnichar *newFolderName, nsIMsgFolder *parentFolder, nsIMsgWindow *msgWindow)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(newFolderName);
|
|
NS_ENSURE_ARG_POINTER(parentFolder);
|
|
nsCOMPtr<nsIEnumerator> subfolders;
|
|
nsresult rv = parentFolder->GetSubFolders(getter_AddRefs(subfolders));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
rv = subfolders->First(); //will fail if no subfolders
|
|
while (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsISupports> supports;
|
|
subfolders->CurrentItem(getter_AddRefs(supports));
|
|
nsCOMPtr<nsIMsgFolder> msgFolder = do_QueryInterface(supports);
|
|
nsAutoString folderNameString;
|
|
PRUnichar *folderName;
|
|
if (msgFolder)
|
|
msgFolder->GetName(&folderName);
|
|
folderNameString.Adopt(folderName);
|
|
if (folderNameString.Equals(newFolderName, nsCaseInsensitiveStringComparator()))
|
|
{
|
|
if (msgWindow)
|
|
ThrowAlertMsg("folderExists", msgWindow);
|
|
return NS_MSG_FOLDER_EXISTS;
|
|
}
|
|
rv = subfolders->Next();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsMsgDBFolder::AddDirectorySeparator(nsFileSpec &path)
|
|
{
|
|
nsAutoString sep;
|
|
nsresult rv = nsGetMailFolderSeparator(sep);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// see if there's a dir with the same name ending with .sbd
|
|
// unfortunately we can't just say:
|
|
// path += sep;
|
|
// here because of the way nsFileSpec concatenates
|
|
|
|
nsCAutoString str(path.GetNativePathCString());
|
|
str.AppendWithConversion(sep);
|
|
path = str.get();
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* Finds the directory associated with this folder. That is if the path is
|
|
c:\Inbox, it will return c:\Inbox.sbd if it succeeds. If that path doesn't
|
|
currently exist then it will create it. Path is strictly an out parameter.
|
|
*/
|
|
nsresult nsMsgDBFolder::CreateDirectoryForFolder(nsFileSpec &path)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
nsCOMPtr<nsIFileSpec> pathSpec;
|
|
rv = GetPath(getter_AddRefs(pathSpec));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = pathSpec->GetFileSpec(&path);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if(!path.IsDirectory())
|
|
{
|
|
//If the current path isn't a directory, add directory separator
|
|
//and test it out.
|
|
rv = AddDirectorySeparator(path);
|
|
if(NS_FAILED(rv))
|
|
return rv;
|
|
|
|
//If that doesn't exist, then we have to create this directory
|
|
if(!path.IsDirectory())
|
|
{
|
|
//If for some reason there's a file with the directory separator
|
|
//then we are going to fail.
|
|
if(path.Exists())
|
|
{
|
|
return NS_MSG_COULD_NOT_CREATE_DIRECTORY;
|
|
}
|
|
//otherwise we need to create a new directory.
|
|
else
|
|
{
|
|
nsFileSpec tempPath(path.GetNativePathCString(), PR_TRUE); // create intermediate directories
|
|
path.CreateDirectory();
|
|
//Above doesn't return an error value so let's see if
|
|
//it was created.
|
|
if(!path.IsDirectory())
|
|
return NS_MSG_COULD_NOT_CREATE_DIRECTORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::Rename(const PRUnichar *aNewName, nsIMsgWindow *msgWindow)
|
|
{
|
|
nsCOMPtr<nsIFileSpec> oldPathSpec;
|
|
nsCOMPtr<nsIAtom> folderRenameAtom;
|
|
nsresult rv = GetPath(getter_AddRefs(oldPathSpec));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
nsCOMPtr<nsIMsgFolder> parentFolder;
|
|
rv = GetParentMsgFolder(getter_AddRefs(parentFolder));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
nsCOMPtr<nsISupports> parentSupport = do_QueryInterface(parentFolder);
|
|
|
|
nsFileSpec fileSpec;
|
|
oldPathSpec->GetFileSpec(&fileSpec);
|
|
nsLocalFolderSummarySpec oldSummarySpec(fileSpec);
|
|
nsFileSpec dirSpec;
|
|
|
|
PRUint32 cnt = 0;
|
|
if (mSubFolders)
|
|
mSubFolders->Count(&cnt);
|
|
|
|
if (cnt > 0)
|
|
rv = CreateDirectoryForFolder(dirSpec);
|
|
|
|
// convert from PRUnichar* to char* due to not having Rename(PRUnichar*)
|
|
// function in nsIFileSpec
|
|
|
|
nsAutoString safeName(aNewName);
|
|
NS_MsgHashIfNecessary(safeName);
|
|
nsCAutoString newDiskName;
|
|
if (NS_FAILED(NS_CopyUnicodeToNative(safeName, newDiskName)))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsXPIDLCString oldLeafName;
|
|
oldPathSpec->GetLeafName(getter_Copies(oldLeafName));
|
|
|
|
if (mName.Equals(aNewName, nsCaseInsensitiveStringComparator()))
|
|
{
|
|
if(msgWindow)
|
|
rv = ThrowAlertMsg("folderExists", msgWindow);
|
|
return NS_MSG_FOLDER_EXISTS;
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr <nsIFileSpec> parentPathSpec;
|
|
parentFolder->GetPath(getter_AddRefs(parentPathSpec));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsFileSpec parentPath;
|
|
parentPathSpec->GetFileSpec(&parentPath);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
if (!parentPath.IsDirectory())
|
|
AddDirectorySeparator(parentPath);
|
|
|
|
rv = CheckIfFolderExists(aNewName, parentFolder, msgWindow);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
ForceDBClosed();
|
|
|
|
nsCAutoString newNameDirStr(newDiskName); //save of dir name before appending .msf
|
|
|
|
if (! (mFlags & MSG_FOLDER_FLAG_VIRTUAL))
|
|
rv = oldPathSpec->Rename(newDiskName.get());
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
newDiskName += ".msf";
|
|
oldSummarySpec.Rename(newDiskName.get());
|
|
}
|
|
else
|
|
{
|
|
ThrowAlertMsg("folderRenameFailed", msgWindow);
|
|
return rv;
|
|
}
|
|
|
|
if (NS_SUCCEEDED(rv) && cnt > 0)
|
|
{
|
|
// rename "*.sbd" directory
|
|
newNameDirStr += ".sbd";
|
|
dirSpec.Rename(newNameDirStr.get());
|
|
}
|
|
|
|
nsCOMPtr<nsIMsgFolder> newFolder;
|
|
if (parentSupport)
|
|
{
|
|
rv = parentFolder->AddSubfolder(nsDependentString(aNewName), getter_AddRefs(newFolder));
|
|
if (newFolder)
|
|
{
|
|
newFolder->SetPrettyName(aNewName);
|
|
newFolder->SetFlags(mFlags);
|
|
PRBool changed = PR_FALSE;
|
|
MatchOrChangeFilterDestination(newFolder, PR_TRUE /*caseInsenstive*/, &changed);
|
|
if (changed)
|
|
AlertFilterChanged(msgWindow);
|
|
|
|
if (cnt > 0)
|
|
newFolder->RenameSubFolders(msgWindow, this);
|
|
|
|
if (parentFolder)
|
|
{
|
|
SetParent(nsnull);
|
|
parentFolder->PropagateDelete(this, PR_FALSE, msgWindow);
|
|
parentFolder->NotifyItemAdded(newFolder);
|
|
}
|
|
folderRenameAtom = do_GetAtom("RenameCompleted");
|
|
newFolder->NotifyFolderEvent(folderRenameAtom);
|
|
}
|
|
}
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ContainsChildNamed(const PRUnichar *name, PRBool* containsChild)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(containsChild);
|
|
|
|
nsCOMPtr<nsISupports> child;
|
|
GetChildNamed(name, getter_AddRefs(child));
|
|
*containsChild = child != nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::IsAncestorOf(nsIMsgFolder *child, PRBool *isAncestor)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(isAncestor);
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
PRUint32 count;
|
|
rv = mSubFolders->Count(&count);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
if (folder.get() == child )
|
|
{
|
|
*isAncestor = PR_TRUE;
|
|
}
|
|
else
|
|
folder->IsAncestorOf(child, isAncestor);
|
|
|
|
}
|
|
if (*isAncestor)
|
|
return NS_OK;
|
|
}
|
|
*isAncestor = PR_FALSE;
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GenerateUniqueSubfolderName(const PRUnichar *prefix,
|
|
nsIMsgFolder *otherFolder,
|
|
PRUnichar **name)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(name);
|
|
|
|
/* only try 256 times */
|
|
for (int count = 0; (count < 256); count++)
|
|
{
|
|
nsAutoString uniqueName;
|
|
uniqueName.Assign(prefix);
|
|
uniqueName.AppendInt(count);
|
|
PRBool containsChild;
|
|
PRBool otherContainsChild = PR_FALSE;
|
|
|
|
ContainsChildNamed(uniqueName.get(), &containsChild);
|
|
if (otherFolder)
|
|
{
|
|
((nsIMsgFolder*)otherFolder)->ContainsChildNamed(uniqueName.get(), &otherContainsChild);
|
|
}
|
|
|
|
if (!containsChild && !otherContainsChild)
|
|
{
|
|
*name = nsCRT::strdup(uniqueName.get());
|
|
return NS_OK;
|
|
}
|
|
}
|
|
*name = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::UpdateSummaryTotals(PRBool /* force */)
|
|
{
|
|
//We don't support this
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SummaryChanged()
|
|
{
|
|
UpdateSummaryTotals(PR_FALSE);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetNumUnread(PRBool deep, PRInt32 *numUnread)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(numUnread);
|
|
|
|
nsresult rv;
|
|
PRInt32 total = mNumUnreadMessages + mNumPendingUnreadMessages;
|
|
if (deep)
|
|
{
|
|
if (total < 0) // deep search never returns negative counts
|
|
total = 0;
|
|
PRUint32 count;
|
|
rv = mSubFolders->Count(&count);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRInt32 num;
|
|
PRUint32 folderFlags;
|
|
folder->GetFlags(&folderFlags);
|
|
if (!(folderFlags & MSG_FOLDER_FLAG_VIRTUAL))
|
|
{
|
|
folder->GetNumUnread(deep, &num);
|
|
total += num;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*numUnread = total;
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetTotalMessages(PRBool deep, PRInt32 *totalMessages)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(totalMessages);
|
|
|
|
nsresult rv;
|
|
PRInt32 total = mNumTotalMessages + mNumPendingTotalMessages;
|
|
if (deep)
|
|
{
|
|
if (total < 0) // deep search never returns negative counts
|
|
total = 0;
|
|
PRUint32 count;
|
|
rv = mSubFolders->Count(&count);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRInt32 num;
|
|
PRUint32 folderFlags;
|
|
folder->GetFlags(&folderFlags);
|
|
if (!(folderFlags & MSG_FOLDER_FLAG_VIRTUAL))
|
|
{
|
|
folder->GetTotalMessages (deep, &num);
|
|
total += num;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*totalMessages = total;
|
|
return NS_OK;
|
|
}
|
|
|
|
PRInt32 nsMsgDBFolder::GetNumPendingUnread()
|
|
{
|
|
return mNumPendingUnreadMessages;
|
|
}
|
|
|
|
PRInt32 nsMsgDBFolder::GetNumPendingTotalMessages()
|
|
{
|
|
return mNumPendingTotalMessages;
|
|
}
|
|
|
|
void nsMsgDBFolder::ChangeNumPendingUnread(PRInt32 delta)
|
|
{
|
|
if (delta)
|
|
{
|
|
PRInt32 oldUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages;
|
|
mNumPendingUnreadMessages += delta;
|
|
PRInt32 newUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages;
|
|
NS_ASSERTION(newUnreadMessages >= 0, "shouldn't have negative unread message count");
|
|
if (newUnreadMessages >= 0)
|
|
{
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if (NS_SUCCEEDED(rv) && folderInfo)
|
|
folderInfo->SetImapUnreadPendingMessages(mNumPendingUnreadMessages);
|
|
|
|
NotifyIntPropertyChanged(kTotalUnreadMessagesAtom, oldUnreadMessages, newUnreadMessages);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsMsgDBFolder::ChangeNumPendingTotalMessages(PRInt32 delta)
|
|
{
|
|
if (delta)
|
|
{
|
|
PRInt32 oldTotalMessages = mNumTotalMessages + mNumPendingTotalMessages;
|
|
mNumPendingTotalMessages += delta;
|
|
PRInt32 newTotalMessages = mNumTotalMessages + mNumPendingTotalMessages;
|
|
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
nsresult rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if (NS_SUCCEEDED(rv) && folderInfo)
|
|
folderInfo->SetImapTotalPendingMessages(mNumPendingTotalMessages);
|
|
NotifyIntPropertyChanged(kTotalMessagesAtom, oldTotalMessages, newTotalMessages);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetPrefFlag()
|
|
{
|
|
// *** Note: this method should only be called when we done with the folder
|
|
// discovery. GetResource() may return a node which is not in the folder
|
|
// tree hierarchy but in the rdf cache in case of the non-existing default
|
|
// Sent, Drafts, and Templates folders. The resouce will be eventually
|
|
// released when the rdf service shuts down. When we create the default
|
|
// folders later on in the imap server, the subsequent GetResource() of the
|
|
// same uri will get us the cached rdf resource which should have the folder
|
|
// flag set appropriately.
|
|
nsresult rv;
|
|
nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIMsgAccountManager> accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = GetServer(getter_AddRefs(server));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIMsgIdentity> identity;
|
|
rv = accountMgr->GetFirstIdentityForServer(server, getter_AddRefs(identity));
|
|
if (NS_SUCCEEDED(rv) && identity)
|
|
{
|
|
nsXPIDLCString folderUri;
|
|
nsCOMPtr<nsIRDFResource> res;
|
|
nsCOMPtr<nsIMsgFolder> folder;
|
|
identity->GetFccFolder(getter_Copies(folderUri));
|
|
if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
|
|
{
|
|
folder = do_QueryInterface(res, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
|
|
}
|
|
identity->GetDraftFolder(getter_Copies(folderUri));
|
|
if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
|
|
{
|
|
folder = do_QueryInterface(res, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
|
|
}
|
|
identity->GetStationeryFolder(getter_Copies(folderUri));
|
|
if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
|
|
{
|
|
folder = do_QueryInterface(res, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = folder->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
|
|
}
|
|
}
|
|
|
|
// XXX TODO
|
|
// JUNK RELATED
|
|
// should we be using the spam settings to set the JUNK flag?
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetFlag(PRUint32 flag)
|
|
{
|
|
ReadDBFolderInfo(PR_FALSE);
|
|
// OnFlagChange can be expensive, so don't call it if we don't need to
|
|
PRBool flagSet;
|
|
nsresult rv;
|
|
|
|
PRBool dbWasOpen = mDatabase != nsnull;
|
|
|
|
if (NS_FAILED(rv = GetFlag(flag, &flagSet)))
|
|
return rv;
|
|
|
|
if (!flagSet)
|
|
{
|
|
mFlags |= flag;
|
|
OnFlagChange(flag);
|
|
}
|
|
if (!dbWasOpen && mDatabase)
|
|
SetMsgDatabase(nsnull);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ClearFlag(PRUint32 flag)
|
|
{
|
|
// OnFlagChange can be expensive, so don't call it if we don't need to
|
|
PRBool flagSet;
|
|
nsresult rv;
|
|
|
|
if (NS_FAILED(rv = GetFlag(flag, &flagSet)))
|
|
return rv;
|
|
|
|
if (flagSet)
|
|
{
|
|
mFlags &= ~flag;
|
|
OnFlagChange (flag);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetFlag(PRUint32 flag, PRBool *_retval)
|
|
{
|
|
*_retval = ((mFlags & flag) != 0);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ToggleFlag(PRUint32 flag)
|
|
{
|
|
mFlags ^= flag;
|
|
OnFlagChange (flag);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::OnFlagChange(PRUint32 flag)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr<nsIMsgDatabase> db;
|
|
nsCOMPtr<nsIDBFolderInfo> folderInfo;
|
|
rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
|
if (NS_SUCCEEDED(rv) && folderInfo)
|
|
{
|
|
#ifdef DEBUG_bienvenu1
|
|
nsXPIDLString name;
|
|
rv = GetName(getter_Copies(name));
|
|
NS_ASSERTION(Compare(name, kLocalizedTrashName) || (mFlags & MSG_FOLDER_FLAG_TRASH), "lost trash flag");
|
|
#endif
|
|
folderInfo->SetFlags((PRInt32) mFlags);
|
|
if (db)
|
|
db->Commit(nsMsgDBCommitType::kLargeCommit);
|
|
|
|
if (flag & MSG_FOLDER_FLAG_OFFLINE)
|
|
{
|
|
PRBool newValue = mFlags & MSG_FOLDER_FLAG_OFFLINE;
|
|
rv = NotifyBoolPropertyChanged(kSynchronizeAtom, !newValue, newValue);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
else if (flag & MSG_FOLDER_FLAG_ELIDED)
|
|
{
|
|
PRBool newValue = mFlags & MSG_FOLDER_FLAG_ELIDED;
|
|
rv = NotifyBoolPropertyChanged(kOpenAtom, newValue, !newValue);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
}
|
|
folderInfo = nsnull;
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetFlags(PRUint32 aFlags)
|
|
{
|
|
if (mFlags != aFlags)
|
|
{
|
|
mFlags = aFlags;
|
|
OnFlagChange(mFlags);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetAllFoldersWithFlag(PRUint32 flag, nsISupportsArray **aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
nsresult rv = CallCreateInstance(NS_SUPPORTSARRAY_CONTRACTID, aResult);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return ListFoldersWithFlag(flag, *aResult);
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::ListFoldersWithFlag(PRUint32 flag, nsISupportsArray *array)
|
|
{
|
|
if ((flag & mFlags) == flag)
|
|
{
|
|
nsCOMPtr <nsISupports> supports;
|
|
QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports));
|
|
array->AppendElement(supports);
|
|
}
|
|
|
|
nsresult rv;
|
|
PRUint32 cnt;
|
|
|
|
// call GetSubFolders() to ensure that mSubFolders is initialized
|
|
nsCOMPtr <nsIEnumerator> enumerator;
|
|
rv = GetSubFolders(getter_AddRefs(enumerator));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = mSubFolders->Count(&cnt);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
for (PRUint32 i=0; i < cnt; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv) && folder)
|
|
{
|
|
nsIMsgFolder *msgFolder = folder.get();
|
|
nsMsgDBFolder *dbFolder = NS_STATIC_CAST(nsMsgDBFolder *, msgFolder);
|
|
dbFolder->ListFoldersWithFlag(flag,array);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetFoldersWithFlag(PRUint32 flags, PRUint32 resultsize, PRUint32 *numFolders, nsIMsgFolder **result)
|
|
{
|
|
PRUint32 num = 0;
|
|
if ((flags & mFlags) == flags)
|
|
{
|
|
if (result && (num < resultsize))
|
|
{
|
|
result[num] = this;
|
|
NS_IF_ADDREF(result[num]);
|
|
}
|
|
num++;
|
|
}
|
|
|
|
nsresult rv;
|
|
PRUint32 cnt;
|
|
|
|
// call GetSubFolders() to ensure that mSubFolders is initialized
|
|
nsCOMPtr <nsIEnumerator> enumerator;
|
|
rv = GetSubFolders(getter_AddRefs(enumerator));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = mSubFolders->Count(&cnt);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
for (PRUint32 i=0; i < cnt; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv) && folder)
|
|
{
|
|
// CAREFUL! if NULL is passed in for result then the caller
|
|
// still wants the full count! Otherwise, the result should be at most the
|
|
// number that the caller asked for.
|
|
PRUint32 numSubFolders;
|
|
|
|
if (!result)
|
|
{
|
|
folder->GetFoldersWithFlag(flags, 0, &numSubFolders, NULL);
|
|
num += numSubFolders;
|
|
}
|
|
else if (num < resultsize)
|
|
{
|
|
folder->GetFoldersWithFlag(flags, resultsize - num, &numSubFolders, result+num);
|
|
num += numSubFolders;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*numFolders = num;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetExpansionArray(nsISupportsArray *expansionArray)
|
|
{
|
|
// the application of flags in GetExpansionArray is subtly different
|
|
// than in GetFoldersWithFlag
|
|
|
|
nsresult rv;
|
|
PRUint32 cnt;
|
|
rv = mSubFolders->Count(&cnt);
|
|
if (NS_FAILED(rv)) return rv;
|
|
for (PRUint32 i = 0; i < cnt; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRUint32 cnt2;
|
|
rv = expansionArray->Count(&cnt2);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
expansionArray->InsertElementAt(folder, cnt2);
|
|
PRUint32 flags;
|
|
folder->GetFlags(&flags);
|
|
if (!(flags & MSG_FOLDER_FLAG_ELIDED))
|
|
folder->GetExpansionArray(expansionArray);
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetDeletable(PRBool *deletable)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(deletable);
|
|
|
|
*deletable = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetRequiresCleanup(PRBool *requiredCleanup)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(requiredCleanup);
|
|
|
|
*requiredCleanup = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ClearRequiresCleanup()
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetKnowsSearchNntpExtension(PRBool *knowsExtension)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(knowsExtension);
|
|
|
|
*knowsExtension = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetAllowsPosting(PRBool *allowsPosting)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(allowsPosting);
|
|
|
|
*allowsPosting = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetDisplayRecipients(PRBool *displayRecipients)
|
|
{
|
|
nsresult rv;
|
|
|
|
*displayRecipients = PR_FALSE;
|
|
|
|
if (mFlags & MSG_FOLDER_FLAG_SENTMAIL && !(mFlags & MSG_FOLDER_FLAG_INBOX))
|
|
*displayRecipients = PR_TRUE;
|
|
else if (mFlags & MSG_FOLDER_FLAG_QUEUE)
|
|
*displayRecipients = PR_TRUE;
|
|
else
|
|
{
|
|
// Only mail folders can be FCC folders
|
|
if (mFlags & MSG_FOLDER_FLAG_MAIL || mFlags & MSG_FOLDER_FLAG_IMAPBOX)
|
|
{
|
|
// There's one FCC folder for sent mail, and one for sent news
|
|
nsIMsgFolder *fccFolders[2];
|
|
int numFccFolders = 0;
|
|
for (int i = 0; i < numFccFolders; i++)
|
|
{
|
|
PRBool isAncestor;
|
|
if (NS_SUCCEEDED(rv = fccFolders[i]->IsAncestorOf(this, &isAncestor)))
|
|
{
|
|
if (isAncestor)
|
|
*displayRecipients = PR_TRUE;
|
|
}
|
|
NS_RELEASE(fccFolders[i]);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::AcquireSemaphore(nsISupports *semHolder)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (mSemaphoreHolder == NULL)
|
|
{
|
|
mSemaphoreHolder = semHolder; //Don't AddRef due to ownership issues.
|
|
}
|
|
else
|
|
rv = NS_MSG_FOLDER_BUSY;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ReleaseSemaphore(nsISupports *semHolder)
|
|
{
|
|
if (!mSemaphoreHolder || mSemaphoreHolder == semHolder)
|
|
{
|
|
mSemaphoreHolder = NULL;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::TestSemaphore(nsISupports *semHolder, PRBool *result)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(result);
|
|
|
|
*result = (mSemaphoreHolder == semHolder);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetLocked(PRBool *isLocked)
|
|
{
|
|
*isLocked = mSemaphoreHolder != NULL;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetRelativePathName(char **pathName)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(pathName);
|
|
|
|
*pathName = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetSizeOnDisk(PRUint32 *size)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(size);
|
|
|
|
*size = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetSizeOnDisk(PRUint32 aSizeOnDisk)
|
|
{
|
|
NotifyIntPropertyChanged(kFolderSizeAtom, mFolderSize, aSizeOnDisk);
|
|
mFolderSize = aSizeOnDisk;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetUsername(char **userName)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(userName);
|
|
nsresult rv;
|
|
nsCOMPtr <nsIMsgIncomingServer> server;
|
|
|
|
rv = GetServer(getter_AddRefs(server));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (server)
|
|
return server->GetUsername(userName);
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetHostname(char **hostName)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(hostName);
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = GetServer(getter_AddRefs(server));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (server)
|
|
return server->GetHostName(hostName);
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetNewMessages(nsIMsgWindow *, nsIUrlListener * /* aListener */)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetBiffState(PRUint32 *aBiffState)
|
|
{
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
if (NS_SUCCEEDED(rv) && server)
|
|
rv = server->GetBiffState(aBiffState);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetBiffState(PRUint32 aBiffState)
|
|
{
|
|
PRUint32 oldBiffState;
|
|
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
if (NS_SUCCEEDED(rv) && server)
|
|
rv = server->GetBiffState(&oldBiffState);
|
|
// Get the server and notify it and not inbox.
|
|
if (oldBiffState != aBiffState)
|
|
{
|
|
// we don't distinguish between unknown and noMail for servers
|
|
if (! (oldBiffState == nsMsgBiffState_Unknown && aBiffState == nsMsgBiffState_NoMail))
|
|
{
|
|
if (!mIsServer)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder;
|
|
rv = GetRootFolder(getter_AddRefs(folder));
|
|
if (NS_SUCCEEDED(rv) && folder)
|
|
return folder->SetBiffState(aBiffState);
|
|
}
|
|
|
|
if (server)
|
|
server->SetBiffState(aBiffState);
|
|
NotifyIntPropertyChanged(kBiffStateAtom, oldBiffState, aBiffState);
|
|
}
|
|
}
|
|
else if (aBiffState == nsMsgBiffState_NoMail)
|
|
{
|
|
// even if the old biff state equals the new biff state, it is still possible that we've never
|
|
// cleared the number of new messages for this particular folder. This happens when the new mail state
|
|
// got cleared by viewing a new message in folder that is different from this one. Biff state is stored per server
|
|
// the num. of new messages is per folder.
|
|
SetNumNewMessages(0);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetNumNewMessages(PRBool deep, PRInt32 *aNumNewMessages)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aNumNewMessages);
|
|
|
|
PRInt32 numNewMessages = (!deep || ! (mFlags & MSG_FOLDER_FLAG_VIRTUAL))
|
|
? mNumNewBiffMessages : 0;
|
|
if (deep)
|
|
{
|
|
PRUint32 count;
|
|
nsresult rv = NS_OK;
|
|
rv = mSubFolders->Count(&count);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
for (PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgFolder> folder(do_QueryElementAt(mSubFolders, i, &rv));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRInt32 num;
|
|
folder->GetNumNewMessages(deep, &num);
|
|
if (num > 0) // it's legal for counts to be negative if we don't know
|
|
numNewMessages += num;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*aNumNewMessages = numNewMessages;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetNumNewMessages(PRInt32 aNumNewMessages)
|
|
{
|
|
if (aNumNewMessages != mNumNewBiffMessages)
|
|
{
|
|
PRInt32 oldNumMessages = mNumNewBiffMessages;
|
|
mNumNewBiffMessages = aNumNewMessages;
|
|
|
|
nsCAutoString oldNumMessagesStr;
|
|
oldNumMessagesStr.AppendInt(oldNumMessages);
|
|
nsCAutoString newNumMessagesStr;
|
|
newNumMessagesStr.AppendInt(aNumNewMessages);
|
|
NotifyPropertyChanged(kNumNewBiffMessagesAtom, oldNumMessagesStr.get(), newNumMessagesStr.get());
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetNewMessagesNotificationDescription(PRUnichar * *aDescription)
|
|
{
|
|
nsresult rv;
|
|
nsAutoString description;
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = GetServer(getter_AddRefs(server));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
if (!(mFlags & MSG_FOLDER_FLAG_INBOX))
|
|
{
|
|
nsXPIDLString folderName;
|
|
rv = GetPrettyName(getter_Copies(folderName));
|
|
if (NS_SUCCEEDED(rv) && folderName)
|
|
description.Assign(folderName);
|
|
}
|
|
|
|
// append the server name
|
|
nsXPIDLString serverName;
|
|
rv = server->GetPrettyName(getter_Copies(serverName));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
// put this test here because we don't want to just put "folder name on"
|
|
// in case the above failed
|
|
if (!(mFlags & MSG_FOLDER_FLAG_INBOX))
|
|
description.AppendLiteral(" on ");
|
|
description.Append(serverName);
|
|
}
|
|
}
|
|
*aDescription = ToNewUnicode(description);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetRootFolder(nsIMsgFolder * *aRootFolder)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aRootFolder);
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
rv = GetServer(getter_AddRefs(server));
|
|
if (NS_FAILED(rv)) return rv;
|
|
NS_ASSERTION(server, "server is null");
|
|
// if this happens, bail.
|
|
if (!server) return NS_ERROR_NULL_POINTER;
|
|
|
|
rv = server->GetRootMsgFolder(aRootFolder);
|
|
if (!aRootFolder)
|
|
return NS_ERROR_NULL_POINTER;
|
|
else
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetPath(nsIFileSpec * *aPath)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aPath);
|
|
nsresult rv=NS_OK;
|
|
|
|
if (!mPath)
|
|
rv = parseURI(PR_TRUE);
|
|
|
|
*aPath = mPath;
|
|
NS_IF_ADDREF(*aPath);
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetPath(nsIFileSpec *aPath)
|
|
{
|
|
// XXX - make a local copy!
|
|
mPath = aPath;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetFilePath(nsILocalFile *aFile)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "don't call this until we've converted mPath to an nsIFile");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetFilePath(nsILocalFile * *aFile)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aFile);
|
|
|
|
nsCOMPtr <nsIFileSpec> fileSpec;
|
|
nsresult rv = GetPath(getter_AddRefs(fileSpec));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsFileSpec spec;
|
|
rv = fileSpec->GetFileSpec(&spec);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
return NS_FileSpecToIFile(&spec, aFile);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::MarkMessagesRead(nsISupportsArray *messages, PRBool markRead)
|
|
{
|
|
PRUint32 count;
|
|
nsresult rv;
|
|
|
|
rv = messages->Count(&count);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(messages, i, &rv);
|
|
|
|
if (message)
|
|
rv = message->MarkRead(markRead);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::MarkMessagesFlagged(nsISupportsArray *messages, PRBool markFlagged)
|
|
{
|
|
PRUint32 count;
|
|
nsresult rv;
|
|
|
|
rv = messages->Count(&count);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(messages, i, &rv);
|
|
|
|
if (message)
|
|
rv = message->MarkFlagged(markFlagged);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetLabelForMessages(nsISupportsArray *aMessages, nsMsgLabelValue aLabel)
|
|
{
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
{
|
|
PRUint32 count;
|
|
NS_ENSURE_ARG(aMessages);
|
|
nsresult rv = aMessages->Count(&count);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsMsgKey msgKey;
|
|
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
(void) message->GetMessageKey(&msgKey);
|
|
rv = mDatabase->SetLabel(msgKey, aLabel);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetJunkScoreForMessages(nsISupportsArray *aMessages, const char *junkScore)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
{
|
|
PRUint32 count;
|
|
NS_ENSURE_ARG(aMessages);
|
|
nsresult rv = aMessages->Count(&count);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsMsgKey msgKey;
|
|
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
(void) message->GetMessageKey(&msgKey);
|
|
|
|
mDatabase->SetStringProperty(msgKey, "junkscore", junkScore);
|
|
mDatabase->SetStringProperty(msgKey, "junkscoreorigin", /* ### should this be plugin? */"plugin");
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::ApplyRetentionSettings()
|
|
{
|
|
return ApplyRetentionSettings(PR_TRUE);
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::ApplyRetentionSettings(PRBool deleteViaFolder)
|
|
{
|
|
if (mFlags & MSG_FOLDER_FLAG_VIRTUAL) // ignore virtual folders.
|
|
return NS_OK;
|
|
nsresult rv;
|
|
PRBool weOpenedDB = PR_FALSE;
|
|
if (!mDatabase)
|
|
{
|
|
rv = GetDatabase(nsnull);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
weOpenedDB = PR_TRUE;
|
|
}
|
|
if (mDatabase)
|
|
{
|
|
nsCOMPtr<nsIMsgRetentionSettings> retentionSettings;
|
|
rv = GetRetentionSettings(getter_AddRefs(retentionSettings));
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = mDatabase->ApplyRetentionSettings(retentionSettings, deleteViaFolder);
|
|
// we don't want applying retention settings to keep the db open, because
|
|
// if we try to purge a bunch of folders, that will leave the dbs all open.
|
|
// So if we opened the db, close it.
|
|
if (weOpenedDB)
|
|
CloseDBIfFolderNotOpen();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::DeleteMessages(nsISupportsArray *messages,
|
|
nsIMsgWindow *msgWindow,
|
|
PRBool deleteStorage,
|
|
PRBool isMove,
|
|
nsIMsgCopyServiceListener *listener,
|
|
PRBool allowUndo)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::CopyMessages(nsIMsgFolder* srcFolder,
|
|
nsISupportsArray *messages,
|
|
PRBool isMove,
|
|
nsIMsgWindow *window,
|
|
nsIMsgCopyServiceListener* listener,
|
|
PRBool isFolder,
|
|
PRBool allowUndo)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::CopyFolder(nsIMsgFolder* srcFolder,
|
|
PRBool isMoveFolder,
|
|
nsIMsgWindow *window,
|
|
nsIMsgCopyServiceListener* listener)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "should be overridden by child class");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::CopyFileMessage(nsIFileSpec* fileSpec,
|
|
nsIMsgDBHdr* messageToReplace,
|
|
PRBool isDraftOrTemplate,
|
|
PRUint32 aNewMsgFlags,
|
|
nsIMsgWindow *window,
|
|
nsIMsgCopyServiceListener* listener)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::CopyDataToOutputStreamForAppend(nsIInputStream *aInStream,
|
|
PRInt32 aLength, nsIOutputStream *aOutputStream)
|
|
{
|
|
if (!aInStream)
|
|
return NS_OK;
|
|
|
|
PRUint32 uiWritten;
|
|
return aOutputStream->WriteFrom(aInStream, aLength, &uiWritten);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::CopyDataDone()
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::MatchName(nsString *name, PRBool *matches)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(matches);
|
|
|
|
*matches = mName.Equals(*name, nsCaseInsensitiveStringComparator());
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::NotifyPropertyChanged(nsIAtom *property,
|
|
const char *oldValue, const char* newValue)
|
|
{
|
|
for (PRInt32 i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlisteners aren't refcounted.
|
|
nsIFolderListener* listener =(nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemPropertyChanged(this, property, oldValue, newValue);
|
|
}
|
|
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemPropertyChanged(this, property, oldValue, newValue);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::NotifyUnicharPropertyChanged(nsIAtom *property,
|
|
const PRUnichar* oldValue,
|
|
const PRUnichar *newValue)
|
|
{
|
|
nsresult rv;
|
|
|
|
for (PRInt32 i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
// folderlisteners aren't refcounted in the array
|
|
nsIFolderListener* listener=(nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemUnicharPropertyChanged(this, property, oldValue, newValue);
|
|
}
|
|
|
|
// Notify listeners who listen to every folder
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = folderListenerManager->OnItemUnicharPropertyChanged(this,
|
|
property,
|
|
oldValue,
|
|
newValue);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::NotifyIntPropertyChanged(nsIAtom *property, PRInt32 oldValue, PRInt32 newValue)
|
|
{
|
|
//Don't send off count notifications if they are turned off.
|
|
if (!mNotifyCountChanges && ((property == kTotalMessagesAtom) ||( property == kTotalUnreadMessagesAtom)))
|
|
return NS_OK;
|
|
|
|
for (PRInt32 i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlisteners aren't refcounted.
|
|
nsIFolderListener* listener =(nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemIntPropertyChanged(this, property, oldValue, newValue);
|
|
}
|
|
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemIntPropertyChanged(this, property, oldValue, newValue);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::NotifyBoolPropertyChanged(nsIAtom* property,
|
|
PRBool oldValue, PRBool newValue)
|
|
{
|
|
for (PRInt32 i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlisteners aren't refcounted.
|
|
nsIFolderListener* listener =(nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemBoolPropertyChanged(this, property, oldValue, newValue);
|
|
}
|
|
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemBoolPropertyChanged(this, property, oldValue, newValue);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::NotifyPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property,
|
|
PRUint32 oldValue, PRUint32 newValue)
|
|
{
|
|
PRInt32 i;
|
|
for(i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlistener's aren't refcounted.
|
|
nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemPropertyFlagChanged(item, property, oldValue, newValue);
|
|
}
|
|
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemPropertyFlagChanged(item, property, oldValue, newValue);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::NotifyItemAdded(nsISupports *aItem)
|
|
{
|
|
static PRBool notify = PR_TRUE;
|
|
|
|
if (!notify)
|
|
return NS_OK;
|
|
|
|
PRInt32 i;
|
|
for(i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlistener's aren't refcounted.
|
|
nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemAdded(this, aItem);
|
|
}
|
|
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemAdded(this, aItem);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::NotifyItemRemoved(nsISupports *item)
|
|
{
|
|
|
|
PRInt32 i;
|
|
for(i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlistener's aren't refcounted.
|
|
nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemRemoved(this, item);
|
|
}
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemRemoved(this, item);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
nsresult nsMsgDBFolder::NotifyFolderEvent(nsIAtom* aEvent)
|
|
{
|
|
PRInt32 i;
|
|
for(i = 0; i < mListeners.Count(); i++)
|
|
{
|
|
//Folderlistener's aren't refcounted.
|
|
nsIFolderListener *listener = (nsIFolderListener*)mListeners.ElementAt(i);
|
|
listener->OnItemEvent(this, aEvent);
|
|
}
|
|
//Notify listeners who listen to every folder
|
|
nsresult rv;
|
|
nsCOMPtr<nsIFolderListener> folderListenerManager =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
folderListenerManager->OnItemEvent(this, aEvent);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsGetMailFolderSeparator(nsString& result)
|
|
{
|
|
result.AssignLiteral(".sbd");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetFilterList(nsIMsgWindow *aMsgWindow, nsIMsgFilterList **aResult)
|
|
{
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
NS_ENSURE_TRUE(server, NS_ERROR_FAILURE);
|
|
|
|
return server->GetFilterList(aMsgWindow, aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::SetFilterList(nsIMsgFilterList *aFilterList)
|
|
{
|
|
nsCOMPtr<nsIMsgIncomingServer> server;
|
|
nsresult rv = GetServer(getter_AddRefs(server));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
NS_ENSURE_TRUE(server, NS_ERROR_FAILURE);
|
|
|
|
return server->SetFilterList(aFilterList);
|
|
}
|
|
|
|
/* void enableNotifications (in long notificationType, in boolean enable); */
|
|
NS_IMETHODIMP nsMsgDBFolder::EnableNotifications(PRInt32 notificationType, PRBool enable, PRBool dbBatching)
|
|
{
|
|
if (notificationType == nsIMsgFolder::allMessageCountNotifications)
|
|
{
|
|
mNotifyCountChanges = enable;
|
|
|
|
// start and stop db batching here. This is under the theory
|
|
// that any time we want to enable and disable notifications,
|
|
// we're probably doing something that should be batched.
|
|
nsCOMPtr <nsIMsgDatabase> database;
|
|
|
|
if (dbBatching) //only if we do dbBatching we need to get db
|
|
GetMsgDatabase(nsnull, getter_AddRefs(database));
|
|
|
|
if (enable)
|
|
{
|
|
if (database)
|
|
database->EndBatch();
|
|
UpdateSummaryTotals(PR_TRUE);
|
|
}
|
|
else if (database)
|
|
return database->StartBatch();
|
|
|
|
return NS_OK;
|
|
}
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetMessageHeader(nsMsgKey msgKey, nsIMsgDBHdr **aMsgHdr)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (aMsgHdr)
|
|
{
|
|
nsCOMPtr <nsIMsgDatabase> database;
|
|
|
|
rv = GetMsgDatabase(nsnull, getter_AddRefs(database));
|
|
if (NS_SUCCEEDED(rv) && database) // did we get a db back?
|
|
rv = database->GetMsgHdrForKey(msgKey, aMsgHdr);
|
|
}
|
|
else
|
|
rv = NS_ERROR_NULL_POINTER;
|
|
|
|
return rv;
|
|
}
|
|
|
|
// this gets the deep sub-folders too, e.g., the children of the children
|
|
NS_IMETHODIMP nsMsgDBFolder::ListDescendents(nsISupportsArray *descendents)
|
|
{
|
|
NS_ENSURE_ARG(descendents);
|
|
PRUint32 cnt;
|
|
nsresult rv = mSubFolders->Count(&cnt);
|
|
if (NS_FAILED(rv)) return rv;
|
|
for (PRUint32 index = 0; index < cnt; index++)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsISupports> supports(do_QueryElementAt(mSubFolders, index));
|
|
nsCOMPtr<nsIMsgFolder> child(do_QueryInterface(supports, &rv));
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
if (!descendents->AppendElement(supports))
|
|
rv = NS_ERROR_OUT_OF_MEMORY;
|
|
else
|
|
rv = child->ListDescendents(descendents); // recurse
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetBaseMessageURI(char **baseMessageURI)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(baseMessageURI);
|
|
|
|
if (!mBaseMessageURI)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
*baseMessageURI = nsCRT::strdup(mBaseMessageURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetUriForMsg(nsIMsgDBHdr *msgHdr, char **aURI)
|
|
{
|
|
NS_ENSURE_ARG(msgHdr);
|
|
NS_ENSURE_ARG(aURI);
|
|
nsMsgKey msgKey;
|
|
msgHdr->GetMessageKey(&msgKey);
|
|
nsCAutoString uri;
|
|
uri.Assign(mBaseMessageURI);
|
|
|
|
// append a "#" followed by the message key.
|
|
uri.Append('#');
|
|
uri.AppendInt(msgKey);
|
|
|
|
*aURI = ToNewCString(uri);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GenerateMessageURI(nsMsgKey msgKey, char **aURI)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aURI);
|
|
nsXPIDLCString baseURI;
|
|
|
|
nsresult rv = GetBaseMessageURI(getter_Copies(baseURI));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
nsCAutoString uri;
|
|
uri.Assign(baseURI);
|
|
|
|
// append a "#" followed by the message key.
|
|
uri.Append('#');
|
|
uri.AppendInt(msgKey);
|
|
|
|
*aURI = ToNewCString(uri);
|
|
if (! *aURI)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::GetBaseStringBundle(nsIStringBundle **aBundle)
|
|
{
|
|
nsresult rv=NS_OK;
|
|
NS_ENSURE_ARG_POINTER(aBundle);
|
|
nsCOMPtr<nsIStringBundleService> bundleService =
|
|
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
if (bundleService && NS_SUCCEEDED(rv))
|
|
bundleService->CreateBundle("chrome://messenger/locale/messenger.properties",
|
|
getter_AddRefs(bundle));
|
|
*aBundle = bundle;
|
|
NS_IF_ADDREF(*aBundle);
|
|
return rv;
|
|
}
|
|
|
|
nsresult //Do not use this routine if you have to call it very often because it creates a new bundle each time
|
|
nsMsgDBFolder::GetStringFromBundle(const char *msgName, PRUnichar **aResult)
|
|
{
|
|
nsresult rv=NS_OK;
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
nsCOMPtr <nsIStringBundle> bundle;
|
|
rv = GetBaseStringBundle(getter_AddRefs(bundle));
|
|
if (NS_SUCCEEDED(rv) && bundle)
|
|
rv=bundle->GetStringFromName(NS_ConvertASCIItoUCS2(msgName).get(), aResult);
|
|
return rv;
|
|
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::ThrowConfirmationPrompt(nsIMsgWindow *msgWindow, const PRUnichar *confirmString, PRBool *confirmed)
|
|
{
|
|
nsresult rv=NS_OK;
|
|
if (msgWindow)
|
|
{
|
|
nsCOMPtr <nsIDocShell> docShell;
|
|
msgWindow->GetRootDocShell(getter_AddRefs(docShell));
|
|
if (docShell)
|
|
{
|
|
nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
|
|
if (dialog && confirmString)
|
|
dialog->Confirm(nsnull, confirmString, confirmed);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgDBFolder::GetStringWithFolderNameFromBundle(const char *msgName, PRUnichar **aResult)
|
|
{
|
|
nsCOMPtr <nsIStringBundle> bundle;
|
|
nsresult rv = GetBaseStringBundle(getter_AddRefs(bundle));
|
|
if (NS_SUCCEEDED(rv) && bundle)
|
|
{
|
|
nsXPIDLString folderName;
|
|
GetName(getter_Copies(folderName));
|
|
const PRUnichar *formatStrings[] =
|
|
{
|
|
folderName,
|
|
kLocalizedBrandShortName
|
|
};
|
|
rv = bundle->FormatStringFromName(NS_ConvertASCIItoUCS2(msgName).get(),
|
|
formatStrings, 2, aResult);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ConfirmFolderDeletionForFilter(nsIMsgWindow *msgWindow, PRBool *confirmed)
|
|
{
|
|
nsXPIDLString confirmString;
|
|
nsresult rv = GetStringWithFolderNameFromBundle("confirmFolderDeletionForFilter", getter_Copies(confirmString));
|
|
if (NS_SUCCEEDED(rv) && confirmString)
|
|
rv = ThrowConfirmationPrompt(msgWindow, confirmString.get(), confirmed);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::ThrowAlertMsg(const char*msgName, nsIMsgWindow *msgWindow)
|
|
{
|
|
nsXPIDLString alertString;
|
|
nsresult rv = GetStringWithFolderNameFromBundle(msgName, getter_Copies(alertString));
|
|
if (NS_SUCCEEDED(rv) && alertString && msgWindow)
|
|
{
|
|
nsCOMPtr <nsIDocShell> docShell;
|
|
msgWindow->GetRootDocShell(getter_AddRefs(docShell));
|
|
if (docShell)
|
|
{
|
|
nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
|
|
if (dialog && alertString)
|
|
dialog->Alert(nsnull, alertString);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::AlertFilterChanged(nsIMsgWindow *msgWindow)
|
|
{ //this is a different alert i.e alert w/ checkbox.
|
|
nsresult rv = NS_OK;
|
|
PRBool checkBox=PR_FALSE;
|
|
GetWarnFilterChanged(&checkBox);
|
|
if (msgWindow && !checkBox)
|
|
{
|
|
nsCOMPtr <nsIDocShell> docShell;
|
|
msgWindow->GetRootDocShell(getter_AddRefs(docShell));
|
|
nsXPIDLString alertString;
|
|
rv = GetStringFromBundle("alertFilterChanged", getter_Copies(alertString));
|
|
nsXPIDLString alertCheckbox;
|
|
rv = GetStringFromBundle("alertFilterCheckbox", getter_Copies(alertCheckbox));
|
|
if (alertString && alertCheckbox && docShell)
|
|
{
|
|
nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
|
|
if (dialog)
|
|
{
|
|
dialog->AlertCheck(nsnull, alertString, alertCheckbox, &checkBox);
|
|
SetWarnFilterChanged(checkBox);
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::GetWarnFilterChanged(PRBool *aVal)
|
|
{
|
|
NS_ENSURE_ARG(aVal);
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && prefBranch)
|
|
{
|
|
rv = prefBranch->GetBoolPref(PREF_MAIL_WARN_FILTER_CHANGED, aVal);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
*aVal = PR_FALSE;
|
|
rv = NS_OK;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::SetWarnFilterChanged(PRBool aVal)
|
|
{
|
|
nsresult rv=NS_OK;
|
|
nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && prefBranch)
|
|
rv = prefBranch->SetBoolPref(PREF_MAIL_WARN_FILTER_CHANGED, aVal);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::NotifyCompactCompleted()
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "should be overridden by child class");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::CloseDBIfFolderNotOpen()
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgMailSession> session =
|
|
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && session) // don't use NS_ENSURE_SUCCESS here - we need to release semaphore below
|
|
{
|
|
PRBool folderOpen;
|
|
session->IsFolderOpenInWindow(this, &folderOpen);
|
|
if (!folderOpen && ! (mFlags & (MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_INBOX)))
|
|
SetMsgDatabase(nsnull);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetSortOrder(PRInt32 order)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "not implemented");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetSortOrder(PRInt32 *order)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(order);
|
|
|
|
PRUint32 flags;
|
|
nsresult rv = GetFlags(&flags);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
if (flags & MSG_FOLDER_FLAG_INBOX)
|
|
*order = 0;
|
|
else if (flags & MSG_FOLDER_FLAG_QUEUE)
|
|
*order = 1;
|
|
else if (flags & MSG_FOLDER_FLAG_DRAFTS)
|
|
*order = 2;
|
|
else if (flags & MSG_FOLDER_FLAG_TEMPLATES)
|
|
*order = 3;
|
|
else if (flags & MSG_FOLDER_FLAG_SENTMAIL)
|
|
*order = 4;
|
|
else if (flags & MSG_FOLDER_FLAG_JUNK)
|
|
*order = 5;
|
|
else if (flags & MSG_FOLDER_FLAG_TRASH)
|
|
*order = 6;
|
|
else if (flags & MSG_FOLDER_FLAG_VIRTUAL)
|
|
*order = 7;
|
|
else
|
|
*order = 8;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetSortKey(PRUint8 **aKey, PRUint32 *aLength)
|
|
{
|
|
NS_ENSURE_ARG(aKey);
|
|
PRInt32 order;
|
|
nsresult rv = GetSortOrder(&order);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
nsAutoString orderString;
|
|
orderString.AppendInt(order);
|
|
|
|
nsXPIDLString folderName;
|
|
rv = GetName(getter_Copies(folderName));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
orderString.Append(folderName);
|
|
return CreateCollationKey(orderString, aKey, aLength);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetPersistElided(PRBool *aPersistElided)
|
|
{
|
|
// by default, we should always persist the open / closed state of folders & servers
|
|
*aPersistElided = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgDBFolder::CreateCollationKey(const nsString &aSource, PRUint8 **aKey, PRUint32 *aLength)
|
|
{
|
|
NS_ASSERTION(gCollationKeyGenerator, "gCollationKeyGenerator is null");
|
|
if (!gCollationKeyGenerator)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
return gCollationKeyGenerator->AllocateRawSortKey(nsICollation::kCollationCaseInSensitive, aSource, aKey, aLength);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::CompareSortKeys(nsIMsgFolder *aFolder, PRInt32 *sortOrder)
|
|
{
|
|
PRUint8 *sortKey1=nsnull;
|
|
PRUint8 *sortKey2=nsnull;
|
|
PRUint32 sortKey1Length;
|
|
PRUint32 sortKey2Length;
|
|
nsresult rv = GetSortKey(&sortKey1, &sortKey1Length);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
aFolder->GetSortKey(&sortKey2, &sortKey2Length);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
rv = gCollationKeyGenerator->CompareRawSortKey(sortKey1, sortKey1Length, sortKey2, sortKey2Length, sortOrder);
|
|
PR_Free(sortKey1);
|
|
PR_Free(sortKey2);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetInVFEditSearchScope (PRBool *aInVFEditSearchScope)
|
|
{
|
|
*aInVFEditSearchScope = mInVFEditSearchScope;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::SetInVFEditSearchScope (PRBool aInVFEditSearchScope, PRBool aSetOnSubFolders)
|
|
{
|
|
PRBool oldInVFEditSearchScope = mInVFEditSearchScope;
|
|
mInVFEditSearchScope = aInVFEditSearchScope;
|
|
NotifyBoolPropertyChanged(kInVFEditSearchScopeAtom, oldInVFEditSearchScope, mInVFEditSearchScope);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys,
|
|
PRBool aLocalOnly, nsIUrlListener *aUrlListener,
|
|
PRBool *aAsyncResults)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aKeysToFetch);
|
|
NS_ENSURE_ARG_POINTER(aAsyncResults);
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::GetMsgTextFromStream(nsIMsgDBHdr *msgHdr, nsIInputStream *stream,
|
|
PRInt32 bytesToRead,
|
|
PRInt32 aMaxOutputLen,
|
|
PRBool aCompressQuotes,
|
|
nsACString &aMsgText)
|
|
{
|
|
/*
|
|
1. non mime message - the message body starts after the blank line following the headers.
|
|
2. mime message, multipart/alternative - we could simply scan for the boundary line,
|
|
advance past its headers, and treat the next few lines as the text.
|
|
3. mime message, text/plain - body follows headers
|
|
4. multipart/mixed - scan past boundary, treat next part as body.
|
|
*/
|
|
|
|
// If we've got a header charset use it, otherwise look for one in the mime parts.
|
|
PRUint32 len;
|
|
msgHdr->GetMessageSize(&len);
|
|
nsLineBuffer<char> *lineBuffer;
|
|
|
|
nsresult rv = NS_InitLineBuffer(&lineBuffer);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsXPIDLCString strCharset;
|
|
msgHdr->GetCharset(getter_Copies(strCharset));
|
|
nsAutoString charset (NS_ConvertUTF8toUTF16(strCharset.get()));
|
|
|
|
nsCString msgText;
|
|
nsCAutoString encoding;
|
|
nsCAutoString boundary;
|
|
nsCAutoString curLine;
|
|
|
|
// might want to use a state var instead of bools.
|
|
PRBool inMsgBody = PR_FALSE, msgBodyIsHtml = PR_FALSE, lookingForBoundary = PR_FALSE;
|
|
PRBool lookingForCharset = PR_FALSE;
|
|
PRBool haveBoundary = PR_FALSE;
|
|
PRBool isBase64 = PR_FALSE;
|
|
PRBool reachedEndBody = bytesToRead >= len;
|
|
PRBool more = PR_TRUE;
|
|
while (len > 0 && more)
|
|
{
|
|
// might be on same line as content-type, so look before
|
|
// we read the next line.
|
|
if (lookingForBoundary)
|
|
{
|
|
// Mail.app doesn't wrap the boundary id in quotes so we need
|
|
// to be sure to handle an unquoted boundary.
|
|
PRInt32 boundaryIndex = curLine.Find("boundary=", PR_TRUE /* ignore case*/);
|
|
if (boundaryIndex != kNotFound)
|
|
{
|
|
boundaryIndex += 9;
|
|
if (curLine[boundaryIndex] == '\"')
|
|
boundaryIndex++;
|
|
|
|
PRInt32 endBoundaryIndex = curLine.RFindChar('"');
|
|
if (endBoundaryIndex == kNotFound)
|
|
endBoundaryIndex = curLine.Length(); // no trailing quote? assume the boundary runs to the end of the line
|
|
|
|
// prepend "--" to boundary, and then boundary delimiter, minus the trailing "
|
|
boundary.Assign("--");
|
|
boundary.Append(Substring(curLine, boundaryIndex, endBoundaryIndex - boundaryIndex));
|
|
haveBoundary = PR_TRUE;
|
|
lookingForBoundary = PR_FALSE;
|
|
}
|
|
}
|
|
rv = NS_ReadLine(stream, lineBuffer, curLine, &more);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
len -= MSG_LINEBREAK_LEN;
|
|
len -= curLine.Length();
|
|
if (inMsgBody)
|
|
{
|
|
if (!boundary.IsEmpty() && boundary.Equals(curLine))
|
|
{
|
|
reachedEndBody = PR_TRUE;
|
|
break;
|
|
}
|
|
msgText.Append(curLine);
|
|
if (!isBase64) // don't append a LF for base64 encoded text
|
|
msgText.Append(nsCRT::LF); // put a LF back, we'll strip this out later
|
|
|
|
if (msgText.Length() > bytesToRead)
|
|
break;
|
|
continue;
|
|
}
|
|
if (haveBoundary)
|
|
{
|
|
// this line is the boundary; continue and fall into code that looks
|
|
// for msg body after headers
|
|
if (curLine.Equals(boundary))
|
|
haveBoundary = PR_FALSE;
|
|
continue;
|
|
}
|
|
if (curLine.IsEmpty())
|
|
{
|
|
inMsgBody = PR_TRUE;
|
|
continue;
|
|
}
|
|
if (StringBeginsWith(curLine, NS_LITERAL_CSTRING("Content-Type:"),
|
|
nsCaseInsensitiveCStringComparator()) ||
|
|
lookingForCharset)
|
|
{
|
|
// look for a charset in the Content-Type header line, we'll take the first one we find.
|
|
nsCOMPtr<nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv) && charset.IsEmpty())
|
|
mimehdrpar->GetParameter(curLine, "charset", EmptyCString(), false, nsnull, charset);
|
|
// if the Content-Type header is multiline, look for a charset in other lines
|
|
if (charset.IsEmpty())
|
|
lookingForCharset = PR_TRUE;
|
|
else if (lookingForCharset || (curLine[0] != ' ' && curLine[0] != '\t'))
|
|
lookingForCharset = PR_FALSE;
|
|
if (FindInReadable(NS_LITERAL_CSTRING("text/html"), curLine,
|
|
nsCaseInsensitiveCStringComparator()))
|
|
msgBodyIsHtml = PR_TRUE;
|
|
else if (FindInReadable(NS_LITERAL_CSTRING("multipart/"), curLine,
|
|
nsCaseInsensitiveCStringComparator()))
|
|
lookingForBoundary = PR_TRUE;
|
|
}
|
|
else if (StringBeginsWith(curLine, NS_LITERAL_CSTRING("Content-Transfer-Encoding:"),
|
|
nsCaseInsensitiveCStringComparator()))
|
|
{
|
|
curLine.Right(encoding, curLine.Length() - 27);
|
|
if (encoding.EqualsLiteral("base64"))
|
|
isBase64 = PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the snippet is encoded, decode it
|
|
if (!encoding.IsEmpty())
|
|
decodeMsgSnippet(encoding, !reachedEndBody, msgText);
|
|
|
|
// In order to turn our snippet into unicode, we need to convert it from the charset we
|
|
// detected earlier.
|
|
nsString unicodeMsgBodyStr;
|
|
ConvertToUnicode(NS_ConvertUTF16toUTF8(charset).get(), msgText, unicodeMsgBodyStr);
|
|
|
|
// now we've got a msg body. If it's html, convert it to plain text.
|
|
// Then, set the previewProperty on the msg hdr to the plain text.
|
|
if (msgBodyIsHtml)
|
|
convertMsgSnippetToPlainText(unicodeMsgBodyStr);
|
|
|
|
// step 3, optionally remove quoted text from the snippet
|
|
nsString compressedQuotesMsgStr;
|
|
if (aCompressQuotes)
|
|
compressQuotesInMsgSnippet(unicodeMsgBodyStr, compressedQuotesMsgStr);
|
|
|
|
// now convert back to utf-8 which is more convenient for storage
|
|
CopyUTF16toUTF8(aCompressQuotes ? compressedQuotesMsgStr : unicodeMsgBodyStr, aMsgText);
|
|
|
|
// finally, truncate the string based on aMaxOutputLen
|
|
if (aMsgText.Length() > aMaxOutputLen)
|
|
aMsgText.Truncate(aMaxOutputLen);
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* decodeMsgSnippet - helper function which applies the appropriate transfer decoding
|
|
* to the message snippet based on aEncodingType. Currently handles
|
|
* base64 and quoted-printable. If aEncodingType refers to an encoding we don't
|
|
* handle, the message data is passed back unmodified.
|
|
* @param aEncodingType the encoding type (base64, quoted-printable)
|
|
* @param aIsComplete the snippet is actually the entire message so the decoder
|
|
* doesn't have to worry about partial data
|
|
* @param aMsgSnippet in/out argument. The encoded msg snippet and then the decoded snippet
|
|
*/
|
|
void nsMsgDBFolder::decodeMsgSnippet(const nsACString& aEncodingType, PRBool aIsComplete, nsCString& aMsgSnippet)
|
|
{
|
|
if (!aEncodingType.IsEmpty())
|
|
{
|
|
if (aEncodingType.EqualsLiteral("base64"))
|
|
{
|
|
PRInt32 base64Len = aMsgSnippet.Length();
|
|
if (aIsComplete)
|
|
base64Len -= base64Len % 4;
|
|
char *decodedBody = PL_Base64Decode(aMsgSnippet.get(), base64Len, nsnull);
|
|
if (decodedBody)
|
|
aMsgSnippet.Adopt(decodedBody);
|
|
|
|
// base64 encoded message haven't had line endings converted to LFs yet.
|
|
aMsgSnippet.ReplaceChar(nsCRT::CR, nsCRT::LF);
|
|
|
|
}
|
|
else if (aEncodingType.EqualsLiteral("quoted-printable"))
|
|
{
|
|
// giant hack - decode in place, and truncate string.
|
|
MsgStripQuotedPrintable((unsigned char *) aMsgSnippet.get());
|
|
aMsgSnippet.Truncate(strlen(aMsgSnippet.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* stripQuotesFromMsgSnippet - Reduces quoted reply text including the citation (Scott wrote:) from
|
|
* the message snippet to " ... ". Assumes the snippet has been decoded and converted to
|
|
* plain text.
|
|
* @param aMsgSnippet in/out argument. The string to strip quotes from.
|
|
*/
|
|
void nsMsgDBFolder::compressQuotesInMsgSnippet(const nsString& aMsgSnippet, nsAString& aCompressedQuotes)
|
|
{
|
|
PRUint32 msgBodyStrLen = aMsgSnippet.Length();
|
|
PRBool lastLineWasAQuote = PR_FALSE;
|
|
PRUint32 offset = 0;
|
|
PRUint32 lineFeedPos = 0;
|
|
while (offset < msgBodyStrLen)
|
|
{
|
|
lineFeedPos = aMsgSnippet.FindChar(nsCRT::LF, offset);
|
|
if (lineFeedPos != kNotFound)
|
|
{
|
|
const nsAString& currentLine = Substring(aMsgSnippet, offset, lineFeedPos - offset);
|
|
// this catches quoted text ("> "), nested quotes of any level (">> ", ">>> ", ...)
|
|
// it also catches empty line quoted text (">"). It might be over agressive and require
|
|
// tweaking later.
|
|
// Try to strip the citation. If the current line ends with a ':' and the next line
|
|
// looks like a quoted reply (starts with a ">") skip the current line
|
|
if (StringBeginsWith(currentLine, NS_LITERAL_STRING(">")) ||
|
|
(lineFeedPos + 1 < msgBodyStrLen && lineFeedPos
|
|
&& aMsgSnippet[lineFeedPos - 1] == PRUnichar(':')
|
|
&& aMsgSnippet[lineFeedPos + 1] == PRUnichar('>')))
|
|
{
|
|
lastLineWasAQuote = PR_TRUE;
|
|
}
|
|
else if (!currentLine.IsEmpty())
|
|
{
|
|
if (lastLineWasAQuote)
|
|
{
|
|
aCompressedQuotes += NS_LITERAL_STRING(" ... ");
|
|
lastLineWasAQuote = PR_FALSE;
|
|
}
|
|
|
|
aCompressedQuotes += currentLine;
|
|
aCompressedQuotes += PRUnichar(' '); // don't forget to substitute a space for the line feed
|
|
}
|
|
|
|
offset = lineFeedPos + 1;
|
|
}
|
|
else
|
|
{
|
|
aCompressedQuotes.Append(Substring(aMsgSnippet, offset, msgBodyStrLen - offset));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* converts an html mail snippet to plain text
|
|
* @param aMessageText - in place conversion
|
|
*/
|
|
nsresult nsMsgDBFolder::convertMsgSnippetToPlainText(nsAString& aMessageText)
|
|
{
|
|
nsString bodyText;
|
|
nsresult rv = NS_OK;
|
|
|
|
// Create a parser
|
|
nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Create the appropriate output sink
|
|
nsCOMPtr<nsIContentSink> sink = do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID,&rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIHTMLToTextSink> textSink(do_QueryInterface(sink));
|
|
NS_ENSURE_TRUE(textSink, NS_ERROR_FAILURE);
|
|
PRUint32 flags = nsIDocumentEncoder::OutputLFLineBreak
|
|
| nsIDocumentEncoder::OutputNoScriptContent
|
|
| nsIDocumentEncoder::OutputNoFramesContent
|
|
| nsIDocumentEncoder::OutputBodyOnly;
|
|
textSink->Initialize(&bodyText, flags, 80);
|
|
|
|
parser->SetContentSink(sink);
|
|
nsCOMPtr<nsIDTD> dtd = do_CreateInstance(kNavDTDCID,&rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
parser->RegisterDTD(dtd);
|
|
rv = parser->Parse(aMessageText, 0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE);
|
|
aMessageText.Assign(bodyText);
|
|
return rv;
|
|
}
|
|
|
|
nsresult nsMsgDBFolder::GetMsgPreviewTextFromStream(nsIMsgDBHdr *msgHdr, nsIInputStream *stream)
|
|
{
|
|
nsCString msgBody;
|
|
nsresult rv = GetMsgTextFromStream(msgHdr, stream, 2048, 255, PR_TRUE, msgBody);
|
|
// replaces all tabs and line returns with a space, then trims off leading and trailing white space
|
|
msgBody.CompressWhitespace(PR_TRUE, PR_TRUE);
|
|
msgHdr->SetStringProperty("preview", msgBody.get());
|
|
return rv;
|
|
}
|
|
|
|
void nsMsgDBFolder::SetMRUTime()
|
|
{
|
|
PRUint32 seconds;
|
|
PRTime2Seconds(PR_Now(), &seconds);
|
|
nsCAutoString nowStr;
|
|
nowStr.AppendInt(seconds);
|
|
SetStringProperty(MRU_TIME_PROPERTY, nowStr.get());
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
{
|
|
PRUint32 count;
|
|
NS_ENSURE_ARG(aMessages);
|
|
nsresult rv = aMessages->Count(&count);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsXPIDLCString keywords;
|
|
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
message->GetStringProperty("keywords", getter_Copies(keywords));
|
|
nsCStringArray keywordArray;
|
|
keywordArray.ParseString(aKeywords, " ");
|
|
for (PRInt32 j = 0; j < keywordArray.Count(); j++)
|
|
{
|
|
nsACString::const_iterator start, end;
|
|
if (!MsgFindKeyword(*(keywordArray[j]), keywords, start, end))
|
|
{
|
|
if (!keywords.IsEmpty())
|
|
keywords.Append(' ');
|
|
keywords.Append(keywordArray[j]->get());
|
|
}
|
|
}
|
|
// go through the msg, not the db, to set the string property, because
|
|
// in the case of filters running on incoming pop3 mail with quarantining
|
|
// turned on, the message key is wrong.
|
|
message->SetStringProperty("keywords", keywords);
|
|
PRUint32 msgFlags;
|
|
message->GetFlags(&msgFlags);
|
|
mDatabase->NotifyHdrChangeAll(message, msgFlags, msgFlags, nsnull);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgDBFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
GetDatabase(nsnull);
|
|
if (mDatabase)
|
|
{
|
|
PRUint32 count;
|
|
NS_ENSURE_ARG(aMessages);
|
|
nsresult rv = aMessages->Count(&count);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
nsXPIDLCString keywords;
|
|
// If the tag is also a label, we should remove the label too...
|
|
|
|
for(PRUint32 i = 0; i < count; i++)
|
|
{
|
|
nsMsgKey msgKey;
|
|
nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aMessages, i, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
(void) message->GetMessageKey(&msgKey);
|
|
rv = message->GetStringProperty("keywords", getter_Copies(keywords));
|
|
nsCStringArray keywordArray;
|
|
keywordArray.ParseString(aKeywords, " ");
|
|
for (PRInt32 j = 0; j < keywordArray.Count(); j++)
|
|
{
|
|
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);
|
|
*aIdentity = nsnull;
|
|
return NS_OK;
|
|
}
|