/* -*- 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 * * 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 #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 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 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 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 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 folderInfo; nsCOMPtr 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 folderInfo; nsCOMPtr 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 folderInfo; nsCOMPtr 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 folderInfo; nsCOMPtr 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 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 folderCache; #ifdef DEBUG_bienvenu1 PRBool exists; NS_ASSERTION(NS_SUCCEEDED(fileSpec->Exists(&exists)) && exists, "whoops, file doesn't exist, mac will break"); #endif nsCOMPtr 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 dbPath; result = GetFolderCacheKey(getter_AddRefs(dbPath), PR_TRUE /* createDBIfMissing */); if (dbPath) { nsCOMPtr 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 folderInfo; nsCOMPtr 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 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 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 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 fileChannel = do_CreateInstance(NS_LOCALFILECHANNEL_CONTRACTID); if (fileChannel) { nsCOMPtr 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 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 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 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 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 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 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 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 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 accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && accountManager) { nsCOMPtr 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 aEnumerator; nsresult rv; if (folderCache) { nsCOMPtr cacheElement; nsCOMPtr 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 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 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 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 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 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 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 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 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 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 accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr 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 server = do_QueryElementAt(allServers, serverIndex); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr folderArray; nsCOMPtr offlineFolderArray; NS_NewISupportsArray(getter_AddRefs(folderArray)); NS_NewISupportsArray(getter_AddRefs(offlineFolderArray)); PRInt32 totalExpungedBytes=0; PRInt32 offlineExpungedBytes =0; PRInt32 localExpungedBytes = 0; do { nsCOMPtr rootFolder; rv = server->GetRootFolder(getter_AddRefs(rootFolder)); if(NS_SUCCEEDED(rv) && rootFolder) { rv = server->GetOfflineSupportLevel(&offlineSupportLevel); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr 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 supports = getter_AddRefs(allDescendents->ElementAt(i)); nsCOMPtr 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 supports = getter_AddRefs(allDescendents->ElementAt(i)); nsCOMPtr 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 pref = do_GetService(NS_PREFSERVICE_CONTRACTID); nsCOMPtr branch; pref->GetBranch("", getter_AddRefs(branch)); PRBool askBeforePurge; branch->GetBoolPref(PREF_MAIL_PURGE_ASK, &askBeforePurge); if (askBeforePurge) { nsCOMPtr 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 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 aboutToCompactAtom = do_GetAtom("AboutToCompact"); NotifyFolderEvent(aboutToCompactAtom); if ( localExpungedBytes > 0) { nsCOMPtr 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 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 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 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 filterList; nsCOMPtr accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr 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 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 dbFolderInfo; nsCOMPtr 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 dbFolderInfo; nsCOMPtr 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 dbPath; nsresult rv = GetFolderCacheKey(getter_AddRefs(dbPath)); if (dbPath) { nsCOMPtr 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 folderInfo; nsCOMPtr 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 dbPath; GetFolderCacheKey(getter_AddRefs(dbPath)); if (dbPath) { nsCOMPtr cacheElement; GetFolderCacheElemFromFileSpec(dbPath, getter_AddRefs(cacheElement)); if (cacheElement) //try to set in the cache cacheElement->SetStringProperty(propertyName, propertyValue); } nsCOMPtr folderInfo; nsCOMPtr 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 server; nsCOMPtr spamSettings; nsCOMPtr whiteListDirectory; nsCOMPtr 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 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 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 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 rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 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 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 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 server; nsresult rv = GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); PRInt32 numBiffMsgs = 0; nsCOMPtr 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 bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 localeSvc = do_GetService(NS_LOCALESERVICE_CONTRACTID,&rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr locale; rv = localeSvc->GetApplicationLocale(getter_AddRefs(locale)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 res; rv = rdf->GetResource(uri, getter_AddRefs(res)); if (NS_FAILED(rv)) return rv; nsCOMPtr 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 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 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 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 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 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 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 server = do_QueryReferent(mServer, &rv); if (NS_FAILED(rv) || !server) { // first try asking the parent instead of the URI nsCOMPtr 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 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 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 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 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 aEnumerator; rv = GetSubFolders(getter_AddRefs(aEnumerator)); if (NS_FAILED(rv)) return rv; nsCOMPtr 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 folderResource = do_QueryInterface(aItem); nsCOMPtr 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 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 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 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 dbPath; // first remove the deleted folder from the folder cache; nsresult result = GetFolderCacheKey(getter_AddRefs(dbPath)); nsCOMPtr accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &result); if(NS_SUCCEEDED(result)) { nsCOMPtr 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 supports = getter_AddRefs(mSubFolders->ElementAt(0)); nsCOMPtr 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 supports; QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports)); nsCOMPtr 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 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 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 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 res; rv = rdf->GetResource(uri, getter_AddRefs(res)); if (NS_FAILED(rv)) return rv; nsCOMPtr 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 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 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 supports; subfolders->CurrentItem(getter_AddRefs(supports)); nsCOMPtr 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 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 oldPathSpec; nsCOMPtr folderRenameAtom; nsresult rv = GetPath(getter_AddRefs(oldPathSpec)); if (NS_FAILED(rv)) return rv; nsCOMPtr parentFolder; rv = GetParentMsgFolder(getter_AddRefs(parentFolder)); if (NS_FAILED(rv)) return rv; nsCOMPtr 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 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 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 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 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 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 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 db; nsCOMPtr 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 db; nsCOMPtr 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 rdf(do_GetService(kRDFServiceCID, &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr accountMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr server; rv = GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr identity; rv = accountMgr->GetFirstIdentityForServer(server, getter_AddRefs(identity)); if (NS_SUCCEEDED(rv) && identity) { nsXPIDLCString folderUri; nsCOMPtr res; nsCOMPtr 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 db; nsCOMPtr 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 supports(do_QueryElementAt(mSubFolders, index)); nsCOMPtr 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 bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); nsCOMPtr 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 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 docShell; msgWindow->GetRootDocShell(getter_AddRefs(docShell)); if (docShell) { nsCOMPtr dialog(do_GetInterface(docShell)); if (dialog && confirmString) dialog->Confirm(nsnull, confirmString, confirmed); } } return rv; } NS_IMETHODIMP nsMsgDBFolder::GetStringWithFolderNameFromBundle(const char *msgName, PRUnichar **aResult) { nsCOMPtr 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 docShell; msgWindow->GetRootDocShell(getter_AddRefs(docShell)); if (docShell) { nsCOMPtr 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 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 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 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 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 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 *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 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 parser = do_CreateInstance(kParserCID, &rv); NS_ENSURE_SUCCESS(rv, rv); // Create the appropriate output sink nsCOMPtr sink = do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID,&rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 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 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; }