/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** 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) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * Seth Spitzer * Lorenzo Colitti * * 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 "nsMsgImapCID.h" #include "nsImapMailFolder.h" #include "nsIEnumerator.h" #include "nsILocalFile.h" #include "nsIFolderListener.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsIRDFService.h" #include "nsIRDFDataSource.h" #include "nsRDFCID.h" #include "nsFileStream.h" #include "nsMsgDBCID.h" #include "nsMsgFolderFlags.h" #include "nsLocalFolderSummarySpec.h" #include "nsImapFlagAndUidState.h" #include "nsIEventQueueService.h" #include "nsIImapUrl.h" #include "nsImapUtils.h" #include "nsMsgUtils.h" #include "nsIMsgMailSession.h" #include "nsMsgBaseCID.h" #include "nsMsgLocalCID.h" #include "nsImapUndoTxn.h" #include "nsIIMAPHostSessionList.h" #include "nsIMsgCopyService.h" #include "nsICopyMsgStreamListener.h" #include "nsImapStringBundle.h" #include "nsIMsgFolderCacheElement.h" #include "nsTextFormatter.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsMsgI18N.h" #include "nsICacheSession.h" #include "nsEscape.h" #include "nsIDOMWindowInternal.h" #include "nsIMsgFilter.h" #include "nsImapMoveCoalescer.h" #include "nsIPrompt.h" #include "nsIPromptService.h" #include "nsIDocShell.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsSpecialSystemDirectory.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsIImapFlagAndUidState.h" #include "nsIImapHeaderXferInfo.h" #include "nsIMessenger.h" #include "nsIMsgSearchAdapter.h" #include "nsIImapMockChannel.h" #include "nsIProgressEventSink.h" #include "nsIMsgWindow.h" #include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later... #include "nsIMsgOfflineImapOperation.h" #include "nsImapOfflineSync.h" #include "nsIMsgAccountManager.h" #include "nsQuickSort.h" #include "nsIImapMockChannel.h" #include "nsIWebNavigation.h" #include "nsNetUtil.h" #include "nsIMAPNamespace.h" #include "nsHashtable.h" #include "nsIMsgFolderCompactor.h" #include "nsMsgMessageFlags.h" #include "nsIMimeHeaders.h" #include "nsIMsgMdnGenerator.h" #include "nsISpamSettings.h" #include "nsInt64.h" #include #include "nsIMsgMailNewsUrl.h" #include "nsEmbedCID.h" #include "nsIMsgComposeService.h" #include "nsMsgCompCID.h" #include "nsICacheEntryDescriptor.h" #include "nsDirectoryServiceDefs.h" #include "nsIMsgIdentity.h" #include "nsIMsgFolderNotificationService.h" static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); static NS_DEFINE_CID(kCMailDB, NS_MAILDB_CID); static NS_DEFINE_CID(kCImapDB, NS_IMAPDB_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kParseMailMsgStateCID, NS_PARSEMAILMSGSTATE_CID); static NS_DEFINE_CID(kCImapHostSessionList, NS_IIMAPHOSTSESSIONLIST_CID); nsIAtom* nsImapMailFolder::mImapHdrDownloadedAtom=nsnull; #define FOUR_K 4096 #define MAILNEWS_CUSTOM_HEADERS "mailnews.customHeaders" /* Copies the contents of srcDir into destDir. destDir will be created if it doesn't exist. */ static nsresult RecursiveCopy(nsIFile* srcDir, nsIFile* destDir) { nsresult rv; PRBool isDir; rv = srcDir->IsDirectory(&isDir); if (NS_FAILED(rv)) return rv; if (!isDir) return NS_ERROR_INVALID_ARG; PRBool exists; rv = destDir->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) rv = destDir->Create(nsIFile::DIRECTORY_TYPE, 0775); if (NS_FAILED(rv)) return rv; PRBool hasMore = PR_FALSE; nsCOMPtr dirIterator; rv = srcDir->GetDirectoryEntries(getter_AddRefs(dirIterator)); if (NS_FAILED(rv)) return rv; rv = dirIterator->HasMoreElements(&hasMore); if (NS_FAILED(rv)) return rv; nsCOMPtr dirEntry; while (hasMore) { rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(dirEntry)); if (NS_SUCCEEDED(rv)) { rv = dirEntry->IsDirectory(&isDir); if (NS_SUCCEEDED(rv)) { if (isDir) { nsCOMPtr destClone; rv = destDir->Clone(getter_AddRefs(destClone)); if (NS_SUCCEEDED(rv)) { nsCOMPtr newChild(do_QueryInterface(destClone)); nsAutoString leafName; dirEntry->GetLeafName(leafName); newChild->AppendRelativePath(leafName); rv = newChild->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) rv = newChild->Create(nsIFile::DIRECTORY_TYPE, 0775); rv = RecursiveCopy(dirEntry, newChild); } } else rv = dirEntry->CopyTo(destDir, EmptyString()); } } rv = dirIterator->HasMoreElements(&hasMore); if (NS_FAILED(rv)) return rv; } return rv; } nsImapMailFolder::nsImapMailFolder() : m_initialized(PR_FALSE),m_haveDiscoveredAllFolders(PR_FALSE), m_haveReadNameFromDB(PR_FALSE), m_curMsgUid(0), m_nextMessageByteLength(0), m_urlRunning(PR_FALSE), m_verifiedAsOnlineFolder(PR_FALSE), m_explicitlyVerify(PR_FALSE), m_folderIsNamespace(PR_FALSE), m_folderNeedsSubscribing(PR_FALSE), m_folderNeedsAdded(PR_FALSE), m_folderNeedsACLListed(PR_TRUE), m_performingBiff(PR_FALSE), m_folderQuotaCommandIssued(PR_FALSE), m_folderQuotaDataIsValid(PR_FALSE), m_updatingFolder(PR_FALSE), m_downloadMessageForOfflineUse(PR_FALSE), m_downloadingFolderForOfflineUse(PR_FALSE), m_folderQuotaUsedKB(0), m_folderQuotaMaxKB(0) { MOZ_COUNT_CTOR(nsImapMailFolder); // double count these for now. if (mImapHdrDownloadedAtom == nsnull) mImapHdrDownloadedAtom = NS_NewAtom("ImapHdrDownloaded"); m_appendMsgMonitor = nsnull; // since we're not using this (yet?) make it null. // if we do start using it, it should be created lazily nsresult rv; // Get current thread envent queue nsCOMPtr pEventQService = do_GetService(kEventQueueServiceCID, &rv); if (NS_SUCCEEDED(rv) && pEventQService) pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(m_eventQueue)); m_moveCoalescer = nsnull; m_boxFlags = 0; m_uidValidity = kUidUnknown; m_numStatusRecentMessages = 0; m_numStatusUnseenMessages = 0; m_hierarchyDelimiter = kOnlineHierarchySeparatorUnknown; m_pathName = nsnull; m_folderACL = nsnull; m_aclFlags = 0; m_supportedUserFlags = 0; m_namespace = nsnull; m_numFilterClassifyRequests = 0; } nsImapMailFolder::~nsImapMailFolder() { MOZ_COUNT_DTOR(nsImapMailFolder); if (m_appendMsgMonitor) PR_DestroyMonitor(m_appendMsgMonitor); // I think our destructor gets called before the base class... if (mInstanceCount == 1) NS_IF_RELEASE(mImapHdrDownloadedAtom); NS_IF_RELEASE(m_moveCoalescer); delete m_pathName; delete m_folderACL; } NS_IMPL_ADDREF_INHERITED(nsImapMailFolder, nsMsgDBFolder) NS_IMPL_RELEASE_INHERITED(nsImapMailFolder, nsMsgDBFolder) NS_IMPL_QUERY_HEAD(nsImapMailFolder) NS_IMPL_QUERY_BODY(nsIMsgImapMailFolder) NS_IMPL_QUERY_BODY(nsICopyMessageListener) NS_IMPL_QUERY_BODY(nsIImapMailFolderSink) NS_IMPL_QUERY_BODY(nsIImapMessageSink) NS_IMPL_QUERY_BODY(nsIUrlListener) NS_IMPL_QUERY_BODY(nsIMsgFilterHitNotify) NS_IMPL_QUERY_BODY(nsIJunkMailClassificationListener) NS_IMPL_QUERY_TAIL_INHERITING(nsMsgDBFolder) NS_IMETHODIMP nsImapMailFolder::Enumerate(nsIEnumerator* *result) { #if 0 nsresult rv = NS_OK; nsIEnumerator* folders; nsIEnumerator* messages; rv = GetSubFolders(&folders); if (NS_FAILED(rv)) return rv; rv = GetMessages(nsnull, &messages); if (NS_FAILED(rv)) return rv; return NS_NewConjoiningEnumerator(folders, messages, (nsIBidirectionalEnumerator**)result); #endif NS_ASSERTION(PR_FALSE, "obsolete, right?"); return NS_ERROR_FAILURE; } nsresult nsImapMailFolder::AddDirectorySeparator(nsFileSpec &path) { nsresult rv = NS_OK; if (mURI.Equals(kImapRootURI)) { // don't concat the full separator with .sbd } else { nsAutoString sep; 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()); LossyAppendUTF16toASCII(sep, str); path = str.get(); } return rv; } static PRBool nsShouldIgnoreFile(nsString& name) { PRInt32 len = name.Length(); if (len > 4 && name.RFind(".msf", PR_TRUE) == len -4) { name.SetLength(len-4); // truncate the string return PR_FALSE; } return PR_TRUE; } // this is only called for virtual folders, currently. NS_IMETHODIMP nsImapMailFolder::AddSubfolder(const nsAString& aName, nsIMsgFolder** aChild) { NS_ENSURE_ARG_POINTER(aChild); 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('/'); // If AddSubFolder starts getting called for folders other than virtual folders, // we'll have to do convert those names to modified utf-7. For now, the account manager code // that loads the virtual folders for each account, expects utf8 not modified utf-7. nsCAutoString escapedName; rv = NS_MsgEscapeEncodeURLPath(aName, escapedName); NS_ENSURE_SUCCESS(rv, rv); 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; nsMsgDBFolder *dbFolder = NS_STATIC_CAST(nsMsgDBFolder *, NS_STATIC_CAST(nsIMsgFolder *, folder.get())); rv = dbFolder->CreateDirectoryForFolder(path); NS_ENSURE_SUCCESS(rv, rv); folder->GetFlags((PRUint32 *)&flags); flags |= MSG_FOLDER_FLAG_MAIL; folder->SetParent(this); folder->SetFlags(flags); nsCOMPtr supports = do_QueryInterface(folder); if(folder) mSubFolders->AppendElement(supports); NS_ADDREF(*aChild = folder); nsCOMPtr imapChild = do_QueryInterface(*aChild); if (imapChild) { NS_LossyConvertUTF16toASCII folderCName(aName); imapChild->SetOnlineName(folderCName.get()); imapChild->SetHierarchyDelimiter(m_hierarchyDelimiter); } return rv; } NS_IMETHODIMP nsImapMailFolder::AddSubfolderWithPath(nsAString& name, nsIFileSpec *dbPath, nsIMsgFolder **child) { if(!child) return NS_ERROR_NULL_POINTER; nsresult rv = NS_OK; nsCOMPtr rdf(do_GetService(kRDFServiceCID, &rv)); if(NS_FAILED(rv)) return rv; PRInt32 flags = 0; nsCAutoString uri = mURI + NS_LITERAL_CSTRING("/"); AppendUTF16toUTF8(name, uri); //will make sure mSubFolders does not have duplicates because of bogus msf files. nsCOMPtr msgFolder; rv = GetChildWithURI(uri.get(), PR_FALSE/*deep*/, PR_FALSE /*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; folder->SetPath(dbPath); nsCOMPtr imapFolder = do_QueryInterface(folder); folder->GetFlags((PRUint32 *)&flags); folder->SetParent(this); flags |= MSG_FOLDER_FLAG_MAIL; PRBool isServer; rv = GetIsServer(&isServer); PRInt32 pFlags; GetFlags ((PRUint32 *) &pFlags); PRBool isParentInbox = pFlags & MSG_FOLDER_FLAG_INBOX; //Only set these if these are top level children or parent is inbox if(NS_SUCCEEDED(rv)) { if(isServer && name.LowerCaseEqualsLiteral("inbox")) flags |= MSG_FOLDER_FLAG_INBOX; else if(isServer || isParentInbox) { nsAutoString trashName; GetTrashFolderName(trashName); if (name.Equals(trashName)) flags |= MSG_FOLDER_FLAG_TRASH; } #if 0 else if(name.LowerCaseEqualsLiteral("sent")) folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL); else if(name.LowerCaseEqualsLiteral("drafts")) folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS); else if (name.LowerCaseEqualsLiteral("templates")) 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); NS_ASSERTION(supports, "couldn't get isupports from imap folder"); if (supports) mSubFolders->AppendElement(supports); *child = folder; NS_IF_ADDREF(*child); return rv; } nsresult nsImapMailFolder::CreateSubFolders(nsFileSpec &path) { nsresult rv = NS_OK; nsAutoString currentFolderNameStr; // online name nsAutoString currentFolderDBNameStr; // possibly munged name nsCOMPtr child; nsCOMPtr server; nsCOMPtr imapServer; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) imapServer = do_QueryInterface(server); PRBool isServer; rv = GetIsServer(&isServer); nsCAutoString folderName; for (nsDirectoryIterator dir(path, PR_FALSE); dir.Exists(); dir++) { nsFileSpec currentFolderPath = dir.Spec(); folderName.Adopt(currentFolderPath.GetLeafName()); currentFolderNameStr.AssignWithConversion(folderName); if (isServer && imapServer) { PRBool isPFC; imapServer->GetIsPFC(folderName.get(), &isPFC); if (isPFC) { nsCOMPtr pfcFolder; imapServer->GetPFC(PR_TRUE, getter_AddRefs(pfcFolder)); continue; } // should check if this is the PFC } if (nsShouldIgnoreFile(currentFolderNameStr)) continue; // OK, here we need to get the online name from the folder cache if we can. // If we can, use that to create the sub-folder nsCOMPtr cacheElement; nsCOMPtr curFolder; nsCOMPtr dbFile; NS_NewFileSpecWithSpec(currentFolderPath, getter_AddRefs(dbFile)); // don't strip off the .msf in currentFolderPath. currentFolderPath.SetLeafName(folderName.get()); rv = NS_NewFileSpecWithSpec(currentFolderPath, getter_AddRefs(curFolder)); currentFolderDBNameStr = currentFolderNameStr; nsAutoString utf7LeafName = currentFolderNameStr; if (NS_SUCCEEDED(rv) && curFolder) { rv = GetFolderCacheElemFromFileSpec(dbFile, getter_AddRefs(cacheElement)); if (NS_SUCCEEDED(rv) && cacheElement) { nsXPIDLString unicodeName; nsXPIDLCString onlineFullUtf7Name; PRUint32 folderFlags; rv = cacheElement->GetInt32Property("flags", (PRInt32 *) &folderFlags); if (NS_SUCCEEDED(rv) && folderFlags & MSG_FOLDER_FLAG_VIRTUAL) //ignore virtual folders continue; PRInt32 hierarchyDelimiter; rv = cacheElement->GetInt32Property("hierDelim", &hierarchyDelimiter); if (NS_SUCCEEDED(rv) && hierarchyDelimiter == kOnlineHierarchySeparatorUnknown) { currentFolderPath.Delete(PR_FALSE); continue; // blow away .msf files for folders with unknown delimiter. } rv = cacheElement->GetStringProperty("onlineName", getter_Copies(onlineFullUtf7Name)); if (NS_SUCCEEDED(rv) && onlineFullUtf7Name.get() && strlen(onlineFullUtf7Name.get())) { // Call ConvertFolderName() and HideFolderName() to do special folder name // mapping and hiding, if configured to do so. For example, need to hide AOL's // 'RECYCLE_OUT' & convert a few AOL folder names. Regular imap accounts // will do no-op in the calls if (imapServer) { PRBool hideFolder; rv = imapServer->HideFolderName(onlineFullUtf7Name.get(), &hideFolder); if (hideFolder) continue; // skip this folder else { rv = imapServer->ConvertFolderName(onlineFullUtf7Name.get(), getter_Copies(unicodeName)); if (NS_FAILED(rv)) { rv = CopyMUTF7toUTF16(onlineFullUtf7Name, unicodeName); if (NS_FAILED(rv)) // XXX Does this make sense? CopyASCIItoUTF16(onlineFullUtf7Name, unicodeName); } } } currentFolderNameStr.Assign(unicodeName); PRUnichar delimiter = 0; GetHierarchyDelimiter(&delimiter); PRInt32 leafPos = currentFolderNameStr.RFindChar(delimiter); if (leafPos > 0) currentFolderNameStr.Cut(0, leafPos + 1); // take the utf7 full online name, and determine the utf7 leaf name CopyASCIItoUTF16(onlineFullUtf7Name, utf7LeafName); leafPos = utf7LeafName.RFindChar(delimiter); if (leafPos > 0) utf7LeafName.Cut(0, leafPos + 1); } } } // make the imap folder remember the file spec it was created with. nsCAutoString leafName; leafName.AssignWithConversion(currentFolderDBNameStr); nsCOMPtr msfFileSpec; rv = NS_NewFileSpecWithSpec(currentFolderPath, getter_AddRefs(msfFileSpec)); if (NS_SUCCEEDED(rv) && msfFileSpec) { // leaf name is the db name w/o .msf (nsShouldIgnoreFile strips it off) // so this trims the .msf off the file spec. msfFileSpec->SetLeafName(leafName.get()); } // use the utf7 name as the uri for the folder. AddSubfolderWithPath(utf7LeafName, msfFileSpec, getter_AddRefs(child)); if (child) { // use the unicode name as the "pretty" name. Set it so it won't be // automatically computed from the URI, which is in utf7 form. if (!currentFolderNameStr.IsEmpty()) child->SetPrettyName(currentFolderNameStr.get()); child->SetMsgDatabase(nsnull); } } return rv; } NS_IMETHODIMP nsImapMailFolder::GetSubFolders(nsIEnumerator* *result) { PRBool isServer; nsresult rv = GetIsServer(&isServer); if (!m_initialized) { nsCOMPtr pathSpec; rv = GetPath(getter_AddRefs(pathSpec)); if (NS_FAILED(rv)) return rv; nsFileSpec path; rv = pathSpec->GetFileSpec(&path); if (NS_FAILED(rv)) return rv; // host directory does not need .sbd tacked on if (NS_SUCCEEDED(rv) && !isServer) rv = AddDirectorySeparator(path); if(NS_FAILED(rv)) return rv; m_initialized = PR_TRUE; // need to set this here to avoid infinite recursion from CreateSubfolders. // we have to treat the root folder specially, because it's name // doesn't end with .sbd PRInt32 newFlags = MSG_FOLDER_FLAG_MAIL; if (path.IsDirectory()) { newFlags |= (MSG_FOLDER_FLAG_DIRECTORY | MSG_FOLDER_FLAG_ELIDED); if (!mIsServer) SetFlag(newFlags); rv = CreateSubFolders(path); } if (isServer) { PRUint32 numFolders = 0; nsCOMPtr inboxFolder; rv = GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(inboxFolder)); if (NS_FAILED(rv) || numFolders == 0 || !inboxFolder) { // create an inbox if we don't have one. CreateClientSubfolderInfo("INBOX", kOnlineHierarchySeparatorUnknown,0, PR_TRUE); } } UpdateSummaryTotals(PR_FALSE); if (NS_FAILED(rv)) return rv; } rv = mSubFolders->Enumerate(result); return rv; } //Makes sure the database is open and exists. If the database is valid then //returns NS_OK. Otherwise returns a failure error value. nsresult nsImapMailFolder::GetDatabase(nsIMsgWindow *aMsgWindow) { nsresult folderOpen = NS_OK; if (!mDatabase) { nsCOMPtr pathSpec; nsresult rv = GetPath(getter_AddRefs(pathSpec)); if (NS_FAILED(rv)) return rv; nsCOMPtr msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); folderOpen = msgDBService->OpenFolderDB(this, PR_TRUE, PR_FALSE, getter_AddRefs(mDatabase)); if (NS_FAILED(folderOpen) && folderOpen != NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) folderOpen = msgDBService->OpenFolderDB(this, PR_TRUE, PR_TRUE, getter_AddRefs(mDatabase)); if (NS_FAILED(folderOpen) && folderOpen != NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) return folderOpen; if(folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) folderOpen = NS_OK; if(mDatabase) { UpdateNewMessages(); if(mAddListener) mDatabase->AddListener(this); UpdateSummaryTotals(PR_TRUE); } } return folderOpen; } NS_IMETHODIMP nsImapMailFolder::UpdateFolder(nsIMsgWindow *msgWindow) { nsresult rv = NS_ERROR_NULL_POINTER; PRBool selectFolder = PR_FALSE; if (mFlags & MSG_FOLDER_FLAG_INBOX && !m_filterList) rv = GetFilterList(msgWindow, getter_AddRefs(m_filterList)); if (m_filterList) { nsCOMPtr server; rv = GetServer(getter_AddRefs(server)); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get server"); PRBool canFileMessagesOnServer = PR_TRUE; if (server) { rv = server->GetCanFileMessagesOnServer(&canFileMessagesOnServer); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to determine if we could file messages on this server"); } // the mdn filter is for filing return receipts into the sent folder // some servers (like AOL mail servers) // can't file to the sent folder, so we don't add the filter for those servers if (canFileMessagesOnServer) { rv = server->ConfigureTemporaryFilters(m_filterList); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add temp filters"); } } selectFolder = PR_TRUE; PRBool isServer; rv = GetIsServer(&isServer); if (NS_SUCCEEDED(rv) && isServer) { if (!m_haveDiscoveredAllFolders) { PRBool hasSubFolders = PR_FALSE; GetHasSubFolders(&hasSubFolders); if (!hasSubFolders) { rv = CreateClientSubfolderInfo("Inbox", kOnlineHierarchySeparatorUnknown,0, PR_FALSE); if (NS_FAILED(rv)) return rv; } m_haveDiscoveredAllFolders = PR_TRUE; } selectFolder = PR_FALSE; } rv = GetDatabase(msgWindow); if (NS_FAILED(rv)) { ThrowAlertMsg("errorGettingDB", msgWindow); return rv; } PRBool canOpenThisFolder = PR_TRUE; GetCanIOpenThisFolder(&canOpenThisFolder); PRBool hasOfflineEvents = PR_FALSE; GetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS, &hasOfflineEvents); if (!WeAreOffline()) { if (hasOfflineEvents) { nsImapOfflineSync *goOnline = new nsImapOfflineSync(msgWindow, this, this); if (goOnline) return goOnline->ProcessNextOperation(); } } else // we're offline - check if we're password protecting the offline store { nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); PRBool userNeedsToAuthenticate = PR_FALSE; // if we're PasswordProtectLocalCache, then we need to find out if the server is authenticated. (void) accountManager->GetUserNeedsToAuthenticate(&userNeedsToAuthenticate); if (userNeedsToAuthenticate) { nsCOMPtr server; rv = GetServer(getter_AddRefs(server)); if (NS_SUCCEEDED(rv)) { PRBool passwordMatches = PR_FALSE; rv = PromptForCachePassword(server, msgWindow, passwordMatches); if (!passwordMatches) return NS_ERROR_FAILURE; } } } if (!canOpenThisFolder) selectFolder = PR_FALSE; // don't run select if we can't select the folder... if (NS_SUCCEEDED(rv) && !m_urlRunning && selectFolder) { nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr url; rv = imapService->SelectFolder(m_eventQueue, this, m_urlListener, msgWindow, getter_AddRefs(url)); if (NS_SUCCEEDED(rv)) { m_urlRunning = PR_TRUE; m_updatingFolder = PR_TRUE; } if (url) { nsCOMPtr mailnewsUrl = do_QueryInterface(url); if (mailnewsUrl) { mailnewsUrl->RegisterListener(this); m_urlListener = nsnull; } } switch (rv) { case NS_MSG_ERROR_OFFLINE: if (msgWindow) AutoCompact(msgWindow); // note fall through to next case. case NS_BINDING_ABORTED: rv = NS_OK; NotifyFolderEvent(mFolderLoadedAtom); break; default: break; } } else if (NS_SUCCEEDED(rv)) // tell the front end that the folder is loaded if we're not going to { // actually run a url. if (!m_updatingFolder) // if we're already running an update url, we'll let that one send the folder loaded NotifyFolderEvent(mFolderLoadedAtom); NS_ENSURE_SUCCESS(rv,rv); } return rv; } NS_IMETHODIMP nsImapMailFolder::GetMessages(nsIMsgWindow *aMsgWindow, nsISimpleEnumerator* *result) { if (result) *result = nsnull; if (!mDatabase) GetDatabase(nsnull); if (mDatabase) return mDatabase->EnumerateMessages(result); return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP nsImapMailFolder::CreateSubfolder(const PRUnichar* folderName, nsIMsgWindow *msgWindow ) { nsresult rv = NS_ERROR_NULL_POINTER; if (!folderName) return rv; nsAutoString trashName; GetTrashFolderName(trashName); if ( nsDependentString(folderName).Equals(trashName) ) // Trash , a special folder { ThrowAlertMsg("folderExists", msgWindow); return NS_MSG_FOLDER_EXISTS; } else if (mIsServer && nsDependentString(folderName).LowerCaseEqualsLiteral("inbox") ) // Inbox, a special folder { ThrowAlertMsg("folderExists", msgWindow); return NS_MSG_FOLDER_EXISTS; } nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); rv = imapService->CreateFolder(m_eventQueue, this, folderName, this, nsnull); return rv; } NS_IMETHODIMP nsImapMailFolder::CreateClientSubfolderInfo(const char *folderName, PRUnichar hierarchyDelimiter, PRInt32 flags, PRBool suppressNotification) { nsresult rv = NS_OK; //Get a directory based on our current path. nsFileSpec path; rv = CreateDirectoryForFolder(path); if(NS_FAILED(rv)) return rv; NS_ConvertASCIItoUTF16 leafName(folderName); nsAutoString folderNameStr; nsAutoString parentName = leafName; PRInt32 folderStart = leafName.FindChar('/'); if (folderStart > 0) { nsCOMPtr rdf(do_GetService(kRDFServiceCID, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr res; nsCOMPtr parentFolder; nsCAutoString uri (mURI); parentName.Right(leafName, leafName.Length() - folderStart - 1); parentName.Truncate(folderStart); // the parentName might be too long or have some illegal chars // so we make it safe. // XXX Here it's assumed that IMAP folder names are stored locally // in modified UTF-7 (ASCII-only) as is stored remotely. If we ever change // this, we have to work with nsString instead of nsCString // (ref. bug 264071) nsCAutoString safeParentName; safeParentName.AssignWithConversion(parentName); NS_MsgHashIfNecessary(safeParentName); path += safeParentName.get(); // path is an out parameter to CreateDirectoryForFolder - I'm not // sure why we're munging it above. rv = CreateDirectoryForFolder(path); if (NS_FAILED(rv)) return rv; uri.Append('/'); uri.AppendWithConversion(parentName); rv = rdf->GetResource(uri, getter_AddRefs(res)); if (NS_FAILED(rv)) return rv; parentFolder = do_QueryInterface(res, &rv); if (NS_FAILED(rv)) return rv; nsCAutoString leafnameC; leafnameC.AssignWithConversion(leafName); return parentFolder->CreateClientSubfolderInfo(leafnameC.get(), hierarchyDelimiter,flags, suppressNotification); } // if we get here, it's really a leaf, and "this" is the parent. folderNameStr = leafName; // path += folderNameStr; // Create an empty database for this mail folder, set its name from the user nsCOMPtr mailDBFactory; nsCOMPtr child; nsCOMPtr msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv); if (msgDBService) { nsCOMPtr unusedDB; nsCOMPtr dbFileSpec; // warning, path will be changed rv = CreateFileSpecForDB(folderName, path, getter_AddRefs(dbFileSpec)); NS_ENSURE_SUCCESS(rv,rv); //Now let's create the actual new folder rv = AddSubfolderWithPath(folderNameStr, dbFileSpec, getter_AddRefs(child)); NS_ENSURE_SUCCESS(rv, rv); rv = msgDBService->OpenMailDBFromFileSpec(dbFileSpec, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(unusedDB)); if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) rv = NS_OK; if (NS_SUCCEEDED(rv) && unusedDB) { //need to set the folder name nsCOMPtr folderInfo; rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo)); // if(NS_SUCCEEDED(rv)) // { // ### DMB used to be leafNameFromUser? // folderInfo->SetMailboxName(&folderNameStr); // } nsCOMPtr imapFolder = do_QueryInterface(child); if (imapFolder) { nsCAutoString onlineName(m_onlineFolderName); if (!onlineName.IsEmpty()) onlineName.Append(char(hierarchyDelimiter)); onlineName.AppendWithConversion(folderNameStr); imapFolder->SetVerifiedAsOnlineFolder(PR_TRUE); imapFolder->SetOnlineName(onlineName.get()); imapFolder->SetHierarchyDelimiter(hierarchyDelimiter); imapFolder->SetBoxFlags(flags); child->SetFlag(MSG_FOLDER_FLAG_ELIDED); nsXPIDLString unicodeName; rv = CopyMUTF7toUTF16(nsDependentCString(folderName), unicodeName); if (NS_SUCCEEDED(rv)) child->SetPrettyName(unicodeName); // store the online name as the mailbox name in the db folder info // I don't think anyone uses the mailbox name, so we'll use it // to restore the online name when blowing away an imap db. if (folderInfo) { nsAutoString unicodeOnlineName; unicodeOnlineName.AssignWithConversion(onlineName.get()); folderInfo->SetMailboxName(unicodeOnlineName); } } unusedDB->SetSummaryValid(PR_TRUE); unusedDB->Commit(nsMsgDBCommitType::kLargeCommit); unusedDB->Close(PR_TRUE); // don't want to hold onto this newly created db. child->SetMsgDatabase(nsnull); } } if (!suppressNotification) { nsCOMPtr folderCreateAtom; if(NS_SUCCEEDED(rv) && child) { NotifyItemAdded(child); folderCreateAtom = do_GetAtom("FolderCreateCompleted"); child->NotifyFolderEvent(folderCreateAtom); } else { folderCreateAtom = do_GetAtom("FolderCreateFailed"); NotifyFolderEvent(folderCreateAtom); } } return rv; } NS_IMETHODIMP nsImapMailFolder::List() { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); rv = imapService->ListFolder(m_eventQueue, this, this, nsnull); return rv; } NS_IMETHODIMP nsImapMailFolder::RemoveSubFolder (nsIMsgFolder *which) { nsCOMPtr folders; nsresult rv = NS_NewISupportsArray(getter_AddRefs(folders)); if (NS_FAILED(rv)) return rv; nsCOMPtr folderSupport = do_QueryInterface(which, &rv); if (NS_FAILED(rv)) return rv; folders->AppendElement(folderSupport); rv = nsMsgDBFolder::DeleteSubFolders(folders, nsnull); which->Delete(); return rv; } NS_IMETHODIMP nsImapMailFolder::CreateStorageIfMissing(nsIUrlListener* urlListener) { nsresult status = NS_OK; nsCOMPtr msgParent; GetParentMsgFolder(getter_AddRefs(msgParent)); // parent is probably not set because *this* was probably created by rdf // and not by folder discovery. So, we have to compute the parent. if (!msgParent) { nsCAutoString folderName(mURI); PRInt32 leafPos = folderName.RFindChar('/'); nsCAutoString parentName(folderName); if (leafPos > 0) { // If there is a hierarchy, there is a parent. // Don't strip off slash if it's the first character parentName.Truncate(leafPos); // get the corresponding RDF resource // RDF will create the folder resource if it doesn't already exist nsCOMPtr rdf(do_GetService(kRDFServiceCID, &status)); if (NS_FAILED(status)) return status; nsCOMPtr resource; status = rdf->GetResource(parentName, getter_AddRefs(resource)); if (NS_FAILED(status)) return status; msgParent = do_QueryInterface(resource, &status); } } if (msgParent) { nsXPIDLString folderName; GetName(getter_Copies(folderName)); nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && imapService) { nsCOMPtr uri; imapService->EnsureFolderExists(m_eventQueue, msgParent, folderName.get(), urlListener, getter_AddRefs(uri)); } } return status; } NS_IMETHODIMP nsImapMailFolder::GetVerifiedAsOnlineFolder(PRBool *aVerifiedAsOnlineFolder) { if (!aVerifiedAsOnlineFolder) return NS_ERROR_NULL_POINTER; *aVerifiedAsOnlineFolder = m_verifiedAsOnlineFolder; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetVerifiedAsOnlineFolder(PRBool aVerifiedAsOnlineFolder) { m_verifiedAsOnlineFolder = aVerifiedAsOnlineFolder; // mark ancestors as verified as well if (aVerifiedAsOnlineFolder) { nsCOMPtr parent; do { GetParent(getter_AddRefs(parent)); if (parent) { nsCOMPtr imapParent = do_QueryInterface(parent); if (imapParent) { PRBool verifiedOnline; imapParent->GetVerifiedAsOnlineFolder(&verifiedOnline); if (verifiedOnline) break; imapParent->SetVerifiedAsOnlineFolder(PR_TRUE); } } } while (parent); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetOnlineDelimiter(char** onlineDelimiter) { if (onlineDelimiter) { nsresult rv; PRUnichar delimiter = 0; rv = GetHierarchyDelimiter(&delimiter); nsAutoString aString(delimiter); *onlineDelimiter = ToNewCString(aString); return rv; } return NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsImapMailFolder::SetHierarchyDelimiter(PRUnichar aHierarchyDelimiter) { m_hierarchyDelimiter = aHierarchyDelimiter; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetHierarchyDelimiter(PRUnichar *aHierarchyDelimiter) { NS_ENSURE_ARG_POINTER(aHierarchyDelimiter); if (mIsServer) { // if it's the root folder, we don't know the delimiter. So look at the // first child. PRUint32 count = 0; (void) mSubFolders->Count(&count); if (count > 0) { nsCOMPtr childFolder = do_QueryElementAt(mSubFolders, 0); if (childFolder) return childFolder->GetHierarchyDelimiter(aHierarchyDelimiter); } } ReadDBFolderInfo(PR_FALSE); // update cache first. *aHierarchyDelimiter = m_hierarchyDelimiter; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetBoxFlags(PRInt32 aBoxFlags) { ReadDBFolderInfo(PR_FALSE); m_boxFlags = aBoxFlags; PRUint32 newFlags = mFlags; newFlags |= MSG_FOLDER_FLAG_IMAPBOX; if (m_boxFlags & kNoinferiors) newFlags |= MSG_FOLDER_FLAG_IMAP_NOINFERIORS; else newFlags &= ~MSG_FOLDER_FLAG_IMAP_NOINFERIORS; if (m_boxFlags & kNoselect) newFlags |= MSG_FOLDER_FLAG_IMAP_NOSELECT; else newFlags &= ~MSG_FOLDER_FLAG_IMAP_NOSELECT; if (m_boxFlags & kPublicMailbox) newFlags |= MSG_FOLDER_FLAG_IMAP_PUBLIC; else newFlags &= ~MSG_FOLDER_FLAG_IMAP_PUBLIC; if (m_boxFlags & kOtherUsersMailbox) newFlags |= MSG_FOLDER_FLAG_IMAP_OTHER_USER; else newFlags &= ~MSG_FOLDER_FLAG_IMAP_OTHER_USER; if (m_boxFlags & kPersonalMailbox) newFlags |= MSG_FOLDER_FLAG_IMAP_PERSONAL; else newFlags &= ~MSG_FOLDER_FLAG_IMAP_PERSONAL; SetFlags(newFlags); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetBoxFlags(PRInt32 *aBoxFlags) { if (!aBoxFlags) return NS_ERROR_NULL_POINTER; *aBoxFlags = m_boxFlags; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetExplicitlyVerify(PRBool *aExplicitlyVerify) { if (!aExplicitlyVerify) return NS_ERROR_NULL_POINTER; *aExplicitlyVerify = m_explicitlyVerify; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetExplicitlyVerify(PRBool aExplicitlyVerify) { m_explicitlyVerify = aExplicitlyVerify; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetNoSelect(PRBool *aResult) { NS_ENSURE_ARG_POINTER(aResult); return GetFlag(MSG_FOLDER_FLAG_IMAP_NOSELECT, aResult); } NS_IMETHODIMP nsImapMailFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow) { nsresult rv; rv = GetDatabase(nsnull); // now's a good time to apply the retention settings. If we do delete any // messages, the expunge is going to have to wait until the delete to // finish before it can run, but the multiple-connection protection code // should handle that. if (mDatabase) ApplyRetentionSettings(); // compact offline store, if folder configured for offline use. // for now, check aMsgWindow because not having aMsgWindow means // we're doing a compact at shut-down. TEMPORARY HACK if (aMsgWindow && mFlags & MSG_FOLDER_FLAG_OFFLINE) CompactOfflineStore(aMsgWindow); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->Expunge(m_eventQueue, this, aListener, nsnull); } NS_IMETHODIMP nsImapMailFolder::CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, nsISupportsArray *aFolderArray, PRBool aCompactOfflineAlso, nsISupportsArray *aOfflineFolderArray) { NS_ASSERTION(!aOfflineFolderArray, "compacting automatically compacts offline stores"); nsresult rv = NS_OK; nsCOMPtr folderArray; if (!aFolderArray) { nsCOMPtr rootFolder; nsCOMPtr allDescendents; rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { NS_NewISupportsArray(getter_AddRefs(allDescendents)); rootFolder->ListDescendents(allDescendents); PRUint32 cnt =0; rv = allDescendents->Count(&cnt); NS_ENSURE_SUCCESS(rv,rv); NS_NewISupportsArray(getter_AddRefs(folderArray)); for (PRUint32 i=0; i< cnt;i++) { nsCOMPtr supports = getter_AddRefs(allDescendents->ElementAt(i)); nsCOMPtr folder = do_QueryInterface(supports, &rv); NS_ENSURE_SUCCESS(rv,rv); rv = folderArray->AppendElement(supports); } rv = folderArray->Count(&cnt); NS_ENSURE_SUCCESS(rv,rv); if (cnt == 0 ) return NotifyCompactCompleted(); } } nsCOMPtr folderCompactor = do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && folderCompactor) if (aFolderArray) rv = folderCompactor->CompactAll(aFolderArray, aMsgWindow, aCompactOfflineAlso, aOfflineFolderArray); else if (folderArray) rv = folderCompactor->CompactAll(folderArray, aMsgWindow, aCompactOfflineAlso, aOfflineFolderArray); return rv; } NS_IMETHODIMP nsImapMailFolder::UpdateStatus(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr uri; rv = imapService->UpdateFolderStatus(m_eventQueue, this, aListener, getter_AddRefs(uri)); if (uri) { nsCOMPtr mailNewsUrl = do_QueryInterface(uri); // if no msg window, we won't put up error messages (this is almost certainly a biff-inspired status) if (!aMsgWindow) mailNewsUrl->SetSuppressErrorMsgs(PR_TRUE); } return rv; } NS_IMETHODIMP nsImapMailFolder::EmptyTrash(nsIMsgWindow *aMsgWindow, nsIUrlListener *aListener) { nsCOMPtr trashFolder; nsresult rv = GetTrashFolder(getter_AddRefs(trashFolder)); if (NS_SUCCEEDED(rv)) { nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (accountManager) { // if we are emptying trash on exit and we are an aol server then don't perform // this operation because it's causing a hang that we haven't been able to figure out yet // this is an rtm fix and we'll look for the right solution post rtm. PRBool empytingOnExit = PR_FALSE; accountManager->GetEmptyTrashInProgress(&empytingOnExit); if (empytingOnExit) { nsCOMPtr imapServer; rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_SUCCEEDED(rv) && imapServer) { PRBool isAOLServer = PR_FALSE; imapServer->GetIsAOLServer(&isAOLServer); if (isAOLServer) return NS_ERROR_FAILURE; // we will not be performing an empty trash.... } // if we fetched an imap server } // if emptying trash on exit which is done through the account manager. } nsCOMPtr trashDB; if (WeAreOffline()) { nsCOMPtr trashDB; rv = trashFolder->GetMsgDatabase(nsnull, getter_AddRefs(trashDB)); if (NS_SUCCEEDED(rv) && trashDB) { nsMsgKey fakeKey; trashDB->GetNextFakeOfflineMsgKey(&fakeKey); nsCOMPtr op; rv = trashDB->GetOfflineOpForKey(fakeKey, PR_TRUE, getter_AddRefs(op)); trashFolder->SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS); op->SetOperation(nsIMsgOfflineImapOperation::kDeleteAllMsgs); } return rv; } nsCOMPtr transferInfo; rv = trashFolder->GetDBTransferInfo(getter_AddRefs(transferInfo)); rv = trashFolder->Delete(); // delete summary spec trashFolder->SetDBTransferInfo(transferInfo); trashFolder->SetSizeOnDisk(0); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { PRBool hasSubfolders = PR_FALSE; rv = trashFolder->GetHasSubFolders(&hasSubfolders); if (hasSubfolders) { nsCOMPtr aEnumerator; nsCOMPtr aSupport; nsCOMPtr aFolder; nsCOMPtr aSupportsArray; rv = NS_NewISupportsArray(getter_AddRefs(aSupportsArray)); if (NS_FAILED(rv)) return rv; rv = trashFolder->GetSubFolders(getter_AddRefs(aEnumerator)); PRBool confirmDeletion; nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) prefBranch->GetBoolPref("mail.imap.confirm_emptyTrashFolderDeletion", &confirmDeletion); nsXPIDLString confirmationStr; nsCOMPtr bundle; nsCOMPtr parentWindow; nsCOMPtr promptService; if (confirmDeletion) { IMAPGetStringByID(IMAP_EMPTY_TRASH_CONFIRM, getter_Copies(confirmationStr)); promptService = do_GetService(NS_PROMPTSERVICE_CONTRACTID); nsCOMPtr docShell; if (aMsgWindow) { (void) aMsgWindow->GetRootDocShell(getter_AddRefs(docShell)); parentWindow = do_QueryInterface(docShell); } rv = IMAPGetStringBundle(getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, rv); rv = aEnumerator->First(); while(NS_SUCCEEDED(rv)) { PRInt32 dlgResult = -1; rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport)); if (confirmDeletion) { nsXPIDLString statusString, confirmText; nsCOMPtr folder = do_QueryInterface(aSupport); nsXPIDLString folderName; folder->GetName(getter_Copies(folderName)); const PRUnichar *formatStrings[1] = { folderName.get() }; rv = bundle->FormatStringFromID(IMAP_EMPTY_TRASH_CONFIRM, formatStrings, 1, getter_Copies(confirmText)); // Got the text, now show dialog. rv = promptService->ConfirmEx(parentWindow, nsnull, confirmText, (nsIPromptService::BUTTON_TITLE_OK * nsIPromptService::BUTTON_POS_0) + (nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_1), nsnull, nsnull, nsnull, nsnull, nsnull, &dlgResult); } if ( NS_SUCCEEDED( rv ) ) { if (dlgResult == 1) return NS_BINDING_ABORTED; rv = aEnumerator->Next(); } } } } if (aListener) { rv = imapService->DeleteAllMessages(m_eventQueue, trashFolder, aListener, nsnull); } else { nsCOMPtr urlListener = do_QueryInterface(trashFolder); rv = imapService->DeleteAllMessages(m_eventQueue, trashFolder, urlListener, nsnull); } // return an error if this failed. We want the empty trash on exit code // to know if this fails so that it doesn't block waiting for empty trash to finish. if (NS_FAILED(rv)) return rv; if (hasSubfolders) { nsCOMPtr aEnumerator; nsCOMPtr aSupport; nsCOMPtr aFolder; nsCOMPtr aSupportsArray; rv = NS_NewISupportsArray(getter_AddRefs(aSupportsArray)); if (NS_FAILED(rv)) return rv; rv = trashFolder->GetSubFolders(getter_AddRefs(aEnumerator)); rv = aEnumerator->First(); while(NS_SUCCEEDED(rv)) { rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport)); aSupportsArray->AppendElement(aSupport); rv = aEnumerator->Next(); } PRUint32 cnt = 0; aSupportsArray->Count(&cnt); for (PRInt32 i = cnt-1; i >= 0; i--) { aFolder = do_QueryElementAt(aSupportsArray, i); aSupportsArray->RemoveElementAt(i); if (aFolder) trashFolder->PropagateDelete(aFolder, PR_TRUE, aMsgWindow); } } } return NS_OK; } return rv; } NS_IMETHODIMP nsImapMailFolder::Delete () { nsresult rv = NS_ERROR_FAILURE; if (mDatabase) { mDatabase->ForceClosed(); mDatabase = nsnull; } nsCOMPtr pathSpec; rv = GetPath(getter_AddRefs(pathSpec)); if (NS_SUCCEEDED(rv)) { nsFileSpec fileSpec; rv = pathSpec->GetFileSpec(&fileSpec); if (NS_SUCCEEDED(rv)) { nsLocalFolderSummarySpec summarySpec(fileSpec); if (summarySpec.Exists()) summarySpec.Delete(PR_FALSE); } } if (mPath) { nsFileSpec fileSpec; if (NS_SUCCEEDED(mPath->GetFileSpec(&fileSpec)) && fileSpec.Exists()) fileSpec.Delete(PR_FALSE); } // should notify nsIMsgFolderListeners about the folder getting deleted... return rv; } NS_IMETHODIMP nsImapMailFolder::Rename (const PRUnichar *newName, nsIMsgWindow *msgWindow ) { if (mFlags & MSG_FOLDER_FLAG_VIRTUAL) return nsMsgDBFolder::Rename(newName, msgWindow); nsresult rv = NS_ERROR_FAILURE; nsAutoString newNameStr(newName); if (newNameStr.FindChar(m_hierarchyDelimiter,0) != -1) { nsCOMPtr docShell; if (msgWindow) msgWindow->GetRootDocShell(getter_AddRefs(docShell)); if (docShell) { nsCOMPtr bundle; rv = IMAPGetStringBundle(getter_AddRefs(bundle)); if (NS_SUCCEEDED(rv) && bundle) { const PRUnichar *formatStrings[] = { (const PRUnichar*) m_hierarchyDelimiter }; nsXPIDLString alertString; rv = bundle->FormatStringFromID(IMAP_SPECIAL_CHAR, formatStrings, 1, getter_Copies(alertString)); nsCOMPtr dialog(do_GetInterface(docShell)); if (dialog && alertString) dialog->Alert(nsnull, alertString); } } return NS_ERROR_FAILURE; } nsCOMPtr incomingImapServer; GetImapIncomingServer(getter_AddRefs(incomingImapServer)); if (incomingImapServer) RecursiveCloseActiveConnections(incomingImapServer); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->RenameLeaf(m_eventQueue, this, newName, this, msgWindow, nsnull); } NS_IMETHODIMP nsImapMailFolder::RecursiveCloseActiveConnections(nsIImapIncomingServer *incomingImapServer) { NS_ENSURE_ARG(incomingImapServer); PRUint32 cnt = 0, i; nsresult rv; if (mSubFolders) { nsCOMPtr folder; mSubFolders->Count(&cnt); if (cnt > 0) { for (i = 0; i < cnt; i++) { folder = do_QueryElementAt(mSubFolders, i); if (folder) folder->RecursiveCloseActiveConnections(incomingImapServer); nsCOMPtr msgFolder = do_QueryInterface(folder, &rv); if (NS_SUCCEEDED(rv) && msgFolder) incomingImapServer->CloseConnectionForFolder(msgFolder); } } } return NS_OK; } // this is called *after* we've done the rename on the server. NS_IMETHODIMP nsImapMailFolder::PrepareToRename() { PRUint32 cnt = 0, i; if (mSubFolders) { nsCOMPtr folder; mSubFolders->Count(&cnt); if (cnt > 0) { for (i = 0; i < cnt; i++) { folder = do_QueryElementAt(mSubFolders, i); if (folder) folder->PrepareToRename(); } } } SetOnlineName(""); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::RenameLocal(const char *newName, nsIMsgFolder *parent) { // XXX Here it's assumed that IMAP folder names are stored locally // in modified UTF-7 (ASCII-only) as is stored remotely. If we ever change // this, we have to work with nsString instead of nsCString // (ref. bug 264071) nsCAutoString leafname(newName); nsCAutoString parentName; // newName always in the canonical form "greatparent/parentname/leafname" PRInt32 leafpos = leafname.RFindChar('/'); if (leafpos >0) leafname.Cut(0, leafpos+1); m_msgParser = nsnull; PrepareToRename(); ForceDBClosed(); nsresult rv = NS_OK; nsCOMPtr oldPathSpec; rv = GetPath(getter_AddRefs(oldPathSpec)); if (NS_FAILED(rv)) return rv; nsCOMPtr parentPathSpec; rv = parent->GetPath(getter_AddRefs(parentPathSpec)); NS_ENSURE_SUCCESS(rv,rv); nsFileSpec parentPath; rv = parentPathSpec->GetFileSpec(&parentPath); NS_ENSURE_SUCCESS(rv,rv); if (!parentPath.IsDirectory()) AddDirectorySeparator(parentPath); PRUint32 cnt = 0; nsFileSpec dirSpec; if (mSubFolders) mSubFolders->Count(&cnt); if (cnt > 0) rv = CreateDirectoryForFolder(dirSpec); nsFileSpec fileSpec; oldPathSpec->GetFileSpec(&fileSpec); nsLocalFolderSummarySpec oldSummarySpec(fileSpec); nsCAutoString newNameStr; oldSummarySpec.Delete(PR_FALSE); if (cnt > 0) { newNameStr = leafname; NS_MsgHashIfNecessary(newNameStr); newNameStr += ".sbd"; char *leafName = dirSpec.GetLeafName(); if (nsCRT::strcmp(leafName, newNameStr.get()) != 0 ) { dirSpec.Rename(newNameStr.get()); // in case of rename operation leaf names will differ nsCRT::free(leafName); return rv; } nsCRT::free(leafName); parentPath += newNameStr.get(); //only for move we need to progress further in case the parent differs if (!parentPath.IsDirectory()) parentPath.CreateDirectory(); else NS_ASSERTION(0,"Directory already exists."); nsCOMPtr srcDir = (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr destDir = (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv,rv); srcDir->InitWithNativePath(nsDependentCString(dirSpec.GetNativePathCString())); destDir->InitWithNativePath(nsDependentCString(parentPath.GetNativePathCString())); rv = RecursiveCopy(srcDir, destDir); NS_ENSURE_SUCCESS(rv,rv); dirSpec.Delete(PR_TRUE); // moving folders } return rv; } NS_IMETHODIMP nsImapMailFolder::GetPrettyName(PRUnichar ** prettyName) { return GetName(prettyName); } NS_IMETHODIMP nsImapMailFolder::UpdateSummaryTotals(PRBool force) { if (!mNotifyCountChanges || mIsServer) return NS_OK; // could we move this into nsMsgDBFolder, or do we need to deal // with the pending imap counts? nsresult rv = NS_OK; PRInt32 oldUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages; PRInt32 oldTotalMessages = mNumTotalMessages + mNumPendingTotalMessages; //We need to read this info from the database ReadDBFolderInfo(force); PRInt32 newUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages; PRInt32 newTotalMessages = mNumTotalMessages + mNumPendingTotalMessages; //Need to notify listeners that total count changed. if(oldTotalMessages != newTotalMessages) { NotifyIntPropertyChanged(kTotalMessagesAtom, oldTotalMessages, newTotalMessages); } if(oldUnreadMessages != newUnreadMessages) { NotifyIntPropertyChanged(kTotalUnreadMessagesAtom, oldUnreadMessages, newUnreadMessages); } FlushToFolderCache(); return rv; } NS_IMETHODIMP nsImapMailFolder::GetDeletable (PRBool *deletable) { nsresult rv = NS_ERROR_FAILURE; return rv; } NS_IMETHODIMP nsImapMailFolder::GetRequiresCleanup(PRBool *requiresCleanup) { nsresult rv = NS_ERROR_FAILURE; return rv; } NS_IMETHODIMP nsImapMailFolder::GetSizeOnDisk(PRUint32 * size) { NS_ENSURE_ARG_POINTER(size); *size = mFolderSize; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetCanCreateSubfolders(PRBool *aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = !(mFlags & (MSG_FOLDER_FLAG_IMAP_NOINFERIORS | MSG_FOLDER_FLAG_VIRTUAL)); PRBool isServer = PR_FALSE; GetIsServer(&isServer); if (!isServer) { nsCOMPtr imapServer; nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer)); PRBool dualUseFolders = PR_TRUE; if (NS_SUCCEEDED(rv) && imapServer) imapServer->GetDualUseFolders(&dualUseFolders); if (!dualUseFolders && *aResult) *aResult = (mFlags & MSG_FOLDER_FLAG_IMAP_NOSELECT); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetCanSubscribe(PRBool *aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = PR_FALSE; PRBool isImapServer = PR_FALSE; nsresult rv = GetIsServer(&isImapServer); if (NS_FAILED(rv)) return rv; // you can only subscribe to imap servers, not imap folders *aResult = isImapServer; return NS_OK; } nsresult nsImapMailFolder::GetServerKey(char **serverKey) { // look for matching imap folders, then pop folders nsCOMPtr server; nsresult rv = GetServer(getter_AddRefs(server)); if (NS_SUCCEEDED(rv) && server) return server->GetKey(serverKey); return rv; } nsresult nsImapMailFolder::GetImapIncomingServer(nsIImapIncomingServer **aImapIncomingServer) { NS_ENSURE_ARG(aImapIncomingServer); *aImapIncomingServer = nsnull; nsCOMPtr server; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) { nsCOMPtr incomingServer = do_QueryInterface(server); *aImapIncomingServer = incomingServer; NS_IF_ADDREF(*aImapIncomingServer); return NS_OK; } return NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsImapMailFolder::AddMessageDispositionState(nsIMsgDBHdr *aMessage, nsMsgDispositionState aDispositionFlag) { nsMsgDBFolder::AddMessageDispositionState(aMessage, aDispositionFlag); // set the mark message answered flag on the server for this message... if (aMessage) { nsMsgKeyArray messageIDs; nsMsgKey msgKey; aMessage->GetMessageKey(&msgKey); messageIDs.Add(msgKey); if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Replied) StoreImapFlags(kImapMsgAnsweredFlag, PR_TRUE, messageIDs.GetArray(), messageIDs.GetSize(), nsnull); else if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Forwarded) StoreImapFlags(kImapMsgForwardedFlag, PR_TRUE, messageIDs.GetArray(), messageIDs.GetSize(), nsnull); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::MarkMessagesRead(nsISupportsArray *messages, PRBool markRead) { // tell the folder to do it, which will mark them read in the db. nsresult rv = nsMsgDBFolder::MarkMessagesRead(messages, markRead); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keysToMarkRead; rv = BuildIdsAndKeyArray(messages, messageIds, keysToMarkRead); if (NS_FAILED(rv)) return rv; StoreImapFlags(kImapMsgSeenFlag, markRead, keysToMarkRead.GetArray(), keysToMarkRead.GetSize(), nsnull); rv = GetDatabase(nsnull); if (NS_SUCCEEDED(rv)) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } NS_IMETHODIMP nsImapMailFolder::SetLabelForMessages(nsISupportsArray *aMessages, nsMsgLabelValue aLabel) { NS_ENSURE_ARG(aMessages); nsresult rv = nsMsgDBFolder::SetLabelForMessages(aMessages, aLabel); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keysToLabel; nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keysToLabel); NS_ENSURE_SUCCESS(rv, rv); StoreImapFlags((aLabel << 9), PR_TRUE, keysToLabel.GetArray(), keysToLabel.GetSize(), nsnull); rv = GetDatabase(nsnull); if (NS_SUCCEEDED(rv)) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } NS_IMETHODIMP nsImapMailFolder::MarkAllMessagesRead(void) { nsresult rv = GetDatabase(nsnull); if(NS_SUCCEEDED(rv)) { nsMsgKeyArray thoseMarked; EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/); rv = mDatabase->MarkAllRead(&thoseMarked); EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/); if (NS_SUCCEEDED(rv)) { rv = StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, thoseMarked.GetArray(), thoseMarked.GetSize(), nsnull); mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } } return rv; } NS_IMETHODIMP nsImapMailFolder::MarkThreadRead(nsIMsgThread *thread) { nsresult rv = GetDatabase(nsnull); if(NS_SUCCEEDED(rv)) { nsMsgKeyArray thoseMarked; rv = mDatabase->MarkThreadRead(thread, nsnull, &thoseMarked); if (NS_SUCCEEDED(rv)) { rv = StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, thoseMarked.GetArray(), thoseMarked.GetSize(), nsnull); mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } } return rv; } NS_IMETHODIMP nsImapMailFolder::ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element) { nsresult rv = nsMsgDBFolder::ReadFromFolderCacheElem(element); PRInt32 hierarchyDelimiter = kOnlineHierarchySeparatorUnknown; nsXPIDLCString onlineName; element->GetInt32Property("boxFlags", &m_boxFlags); if (NS_SUCCEEDED(element->GetInt32Property("hierDelim", &hierarchyDelimiter)) && hierarchyDelimiter != kOnlineHierarchySeparatorUnknown) m_hierarchyDelimiter = (PRUnichar) hierarchyDelimiter; rv = element->GetStringProperty("onlineName", getter_Copies(onlineName)); if (NS_SUCCEEDED(rv) && (const char *) onlineName && strlen((const char *) onlineName)) m_onlineFolderName.Assign(onlineName); m_aclFlags = -1; // init to invalid value. element->GetInt32Property("aclFlags", (PRInt32 *) &m_aclFlags); #ifdef DEBUG_bienvenu if (!nsCRT::strcasecmp((const char *) onlineName, "Sent")) printf("loading folder cache elem for %s flags = %lx", (const char *) onlineName, mFlags); else if (!nsCRT::strcasecmp((const char *) onlineName, "INBOX")) printf("loading folder cache elem for %s flags = %lx", (const char *) onlineName, mFlags); #endif return rv; } NS_IMETHODIMP nsImapMailFolder::WriteToFolderCacheElem(nsIMsgFolderCacheElement *element) { nsresult rv = nsMsgDBFolder::WriteToFolderCacheElem(element); element->SetInt32Property("boxFlags", m_boxFlags); element->SetInt32Property("hierDelim", (PRInt32) m_hierarchyDelimiter); element->SetStringProperty("onlineName", m_onlineFolderName.get()); element->SetInt32Property("aclFlags", (PRInt32) m_aclFlags); return rv; } NS_IMETHODIMP nsImapMailFolder::MarkMessagesFlagged(nsISupportsArray *messages, PRBool markFlagged) { nsresult rv; // tell the folder to do it, which will mark them read in the db. rv = nsMsgDBFolder::MarkMessagesFlagged(messages, markFlagged); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keysToMarkFlagged; rv = BuildIdsAndKeyArray(messages, messageIds, keysToMarkFlagged); if (NS_FAILED(rv)) return rv; rv = StoreImapFlags(kImapMsgFlaggedFlag, markFlagged, keysToMarkFlagged.GetArray(), keysToMarkFlagged.GetSize(), nsnull); mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } NS_IMETHODIMP nsImapMailFolder::SetOnlineName(const char * aOnlineFolderName) { nsresult rv; nsCOMPtr db; nsCOMPtr folderInfo; rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db)); // do this after GetDBFolderInfoAndDB, because it crunches m_onlineFolderName (not sure why) m_onlineFolderName = aOnlineFolderName; if(NS_SUCCEEDED(rv) && folderInfo) { nsAutoString onlineName; onlineName.AssignWithConversion(aOnlineFolderName); rv = folderInfo->SetProperty("onlineName", onlineName); rv = folderInfo->SetMailboxName(onlineName); // so, when are we going to commit this? Definitely not every time! // We could check if the online name has changed. db->Commit(nsMsgDBCommitType::kLargeCommit); } folderInfo = nsnull; return rv; } NS_IMETHODIMP nsImapMailFolder::GetOnlineName(char ** aOnlineFolderName) { if (!aOnlineFolderName) return NS_ERROR_NULL_POINTER; ReadDBFolderInfo(PR_FALSE); // update cache first. *aOnlineFolderName = ToNewCString(m_onlineFolderName); return (*aOnlineFolderName) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; // ### do we want to read from folder cache first, or has that been done? } NS_IMETHODIMP nsImapMailFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **db) { nsresult openErr=NS_ERROR_UNEXPECTED; if(!db || !folderInfo) return NS_ERROR_NULL_POINTER; //ducarroz: should we use NS_ERROR_INVALID_ARG? nsresult rv; openErr = GetDatabase(nsnull); *db = mDatabase; NS_IF_ADDREF(*db); if (NS_SUCCEEDED(openErr)&& *db) { openErr = (*db)->GetDBFolderInfo(folderInfo); if (NS_SUCCEEDED(openErr) && folderInfo) { nsXPIDLCString onlineName; if (NS_SUCCEEDED((*folderInfo)->GetCharPtrProperty("onlineName", getter_Copies(onlineName)))) { if (!onlineName.IsEmpty()) m_onlineFolderName.Assign(onlineName); else { nsAutoString autoOnlineName; // autoOnlineName.AssignWithConversion(name); (*folderInfo)->GetMailboxName(autoOnlineName); if (autoOnlineName.IsEmpty()) { nsXPIDLCString uri; rv = GetURI(getter_Copies(uri)); if (NS_FAILED(rv)) return rv; nsXPIDLCString hostname; rv = GetHostname(getter_Copies(hostname)); if (NS_FAILED(rv)) return rv; nsXPIDLCString name; rv = nsImapURI2FullName(kImapRootURI, hostname, uri, getter_Copies(name)); nsCAutoString onlineCName(name); if (m_hierarchyDelimiter != '/') onlineCName.ReplaceChar('/', char(m_hierarchyDelimiter)); m_onlineFolderName.Assign(onlineCName); autoOnlineName.AssignWithConversion(onlineCName.get()); } rv = (*folderInfo)->SetProperty("onlineName", autoOnlineName); } } } } return openErr; } nsresult nsImapMailFolder::BuildIdsAndKeyArray(nsISupportsArray* messages, nsCString& msgIds, nsMsgKeyArray& keyArray) { nsresult rv = NS_ERROR_NULL_POINTER; PRUint32 count = 0; PRUint32 i; if (!messages) return rv; rv = messages->Count(&count); if (NS_FAILED(rv)) return rv; // build up message keys. for (i = 0; i < count; i++) { nsMsgKey key; nsCOMPtr msgDBHdr = do_QueryElementAt(messages, i, &rv); if (msgDBHdr) rv = msgDBHdr->GetMessageKey(&key); if (NS_SUCCEEDED(rv)) keyArray.Add(key); } return AllocateUidStringFromKeys(keyArray.GetArray(), keyArray.GetSize(), msgIds); } static int PR_CALLBACK CompareKey (const void *v1, const void *v2, void *) { // QuickSort callback to compare array values nsMsgKey i1 = *(nsMsgKey *)v1; nsMsgKey i2 = *(nsMsgKey *)v2; return i1 - i2; } /* static */nsresult nsImapMailFolder::AllocateUidStringFromKeys(nsMsgKey *keys, PRUint32 numKeys, nsCString &msgIds) { if (!numKeys) return NS_ERROR_INVALID_ARG; nsresult rv = NS_OK; PRUint32 startSequence; startSequence = keys[0]; PRUint32 curSequenceEnd = startSequence; PRUint32 total = numKeys; // sort keys and then generate ranges instead of singletons! NS_QuickSort(keys, numKeys, sizeof(nsMsgKey), CompareKey, nsnull); for (PRUint32 keyIndex = 0; keyIndex < total; keyIndex++) { PRUint32 curKey = keys[keyIndex]; PRUint32 nextKey = (keyIndex + 1 < total) ? keys[keyIndex + 1] : 0xFFFFFFFF; PRBool lastKey = (nextKey == 0xFFFFFFFF); if (lastKey) curSequenceEnd = curKey; if (nextKey == (PRUint32) curSequenceEnd + 1 && !lastKey) { curSequenceEnd = nextKey; continue; } else if (curSequenceEnd > startSequence) { msgIds.AppendInt(startSequence); msgIds += ':'; msgIds.AppendInt(curSequenceEnd); if (!lastKey) msgIds += ','; startSequence = nextKey; curSequenceEnd = startSequence; } else { startSequence = nextKey; curSequenceEnd = startSequence; msgIds.AppendInt(keys[keyIndex]); if (!lastKey) msgIds += ','; } } return rv; } nsresult nsImapMailFolder::MarkMessagesImapDeleted(nsMsgKeyArray *keyArray, PRBool deleted, nsIMsgDatabase *db) { for (PRUint32 kindex = 0; kindex < keyArray->GetSize(); kindex++) { nsMsgKey key = keyArray->ElementAt(kindex); db->MarkImapDeleted(key, deleted, nsnull); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::DeleteMessages(nsISupportsArray *messages, nsIMsgWindow *msgWindow, PRBool deleteStorage, PRBool isMove, nsIMsgCopyServiceListener* listener, PRBool allowUndo) { // *** jt - assuming delete is move to the trash folder for now nsCOMPtr aEnumerator; nsCOMPtr res; nsCAutoString uri; PRBool deleteImmediatelyNoTrash = PR_FALSE; nsCAutoString messageIds; nsMsgKeyArray srcKeyArray; PRBool deleteMsgs = PR_TRUE; //used for toggling delete status - default is true nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash; imapMessageFlagsType messageFlags = kImapMsgDeletedFlag; nsCOMPtr imapServer; nsresult rv = GetFlag(MSG_FOLDER_FLAG_TRASH, &deleteImmediatelyNoTrash); rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_SUCCEEDED(rv) && imapServer) { imapServer->GetDeleteModel(&deleteModel); if (deleteModel != nsMsgImapDeleteModels::MoveToTrash || deleteStorage) deleteImmediatelyNoTrash = PR_TRUE; // if we're deleting a message, we should pseudo-interrupt the msg //load of the current message. PRBool interrupted = PR_FALSE; imapServer->PseudoInterruptMsgLoad(this, msgWindow, &interrupted); } rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray); if (NS_FAILED(rv)) return rv; nsCOMPtr rootFolder; nsCOMPtr trashFolder; if (!deleteImmediatelyNoTrash) { rv = GetRootFolder(getter_AddRefs(rootFolder)); if (NS_SUCCEEDED(rv) && rootFolder) { PRUint32 numFolders = 0; rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_TRASH, 1, &numFolders, getter_AddRefs(trashFolder)); NS_ASSERTION(NS_SUCCEEDED(rv) && trashFolder != 0, "couldn't find trash"); // if we can't find the trash, we'll just have to do an imap delete and pretend this is the trash if (NS_FAILED(rv) || !trashFolder) deleteImmediatelyNoTrash = PR_TRUE; } } if ((NS_SUCCEEDED(rv) && deleteImmediatelyNoTrash) || deleteModel == nsMsgImapDeleteModels::IMAPDelete ) { if (allowUndo) { //need to take care of these two delete models nsImapMoveCopyMsgTxn* undoMsgTxn = new nsImapMoveCopyMsgTxn; if (!undoMsgTxn || NS_FAILED(undoMsgTxn->Init(this, &srcKeyArray, messageIds.get(), nsnull, PR_TRUE, isMove, m_eventQueue, nsnull))) { delete undoMsgTxn; return NS_ERROR_OUT_OF_MEMORY; } undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg); // we're adding this undo action before the delete is successful. This is evil, // but 4.5 did it as well. nsCOMPtr txnMgr; if (msgWindow) msgWindow->GetTransactionManager(getter_AddRefs(txnMgr)); if (txnMgr) txnMgr->DoTransaction(undoMsgTxn); } if (deleteModel == nsMsgImapDeleteModels::IMAPDelete && !deleteStorage) { PRUint32 cnt, flags; rv = messages->Count(&cnt); NS_ENSURE_SUCCESS(rv, rv); deleteMsgs = PR_FALSE; for (PRUint32 i=0; i msgHdr = do_QueryElementAt(messages, i); if (msgHdr) { msgHdr->GetFlags(&flags); if (!(flags & MSG_FLAG_IMAP_DELETED)) { deleteMsgs = PR_TRUE; break; } } } } // if copy service listener is also a url listener, pass that // url listener into StoreImapFlags. nsCOMPtr urlListener = do_QueryInterface(listener); if (deleteMsgs) messageFlags |= kImapMsgSeenFlag; rv = StoreImapFlags(messageFlags, deleteMsgs, srcKeyArray.GetArray(), srcKeyArray.GetSize(), urlListener); if (NS_SUCCEEDED(rv)) { if (mDatabase) { if (deleteModel == nsMsgImapDeleteModels::IMAPDelete) { MarkMessagesImapDeleted(&srcKeyArray, deleteMsgs, mDatabase); } else { EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/); //"remove it immediately" model mDatabase->DeleteMessages(&srcKeyArray,nsnull); EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/); NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom); } } } return rv; } else // have to move the messages to the trash { if(trashFolder) { nsCOMPtr srcFolder; nsCOMPtrsrcSupport; PRUint32 count = 0; rv = messages->Count(&count); rv = QueryInterface(NS_GET_IID(nsIMsgFolder), getter_AddRefs(srcFolder)); nsCOMPtr copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = copyService->CopyMessages(srcFolder, messages, trashFolder, PR_TRUE, listener, msgWindow, allowUndo); } } return rv; } // check if folder is the trash, or a descendent of the trash // so we can tell if the folders we're deleting from it should // be *really* deleted. PRBool nsImapMailFolder::TrashOrDescendentOfTrash(nsIMsgFolder* folder) { nsCOMPtr parent; nsCOMPtr curFolder; nsresult rv; PRUint32 flags = 0; if (!folder) return PR_FALSE; curFolder = do_QueryInterface(folder, &rv); if (NS_FAILED(rv)) return PR_FALSE; do { rv = curFolder->GetFlags(&flags); if (NS_FAILED(rv)) return PR_FALSE; if (flags & MSG_FOLDER_FLAG_TRASH) return PR_TRUE; rv = curFolder->GetParentMsgFolder(getter_AddRefs(parent)); if (NS_FAILED(rv)) return PR_FALSE; curFolder = do_QueryInterface(parent, &rv); } while (NS_SUCCEEDED(rv) && curFolder); return PR_FALSE; } NS_IMETHODIMP nsImapMailFolder::DeleteSubFolders(nsISupportsArray* folders, nsIMsgWindow *msgWindow) { nsCOMPtr curFolder; nsCOMPtr urlListener; nsCOMPtr trashFolder; PRInt32 i; PRUint32 folderCount = 0; nsresult rv; // "this" is the folder we're deleting from PRBool deleteNoTrash = TrashOrDescendentOfTrash(this) || !DeleteIsMoveToTrash(); PRBool confirmed = PR_FALSE; PRBool confirmDeletion = PR_TRUE; (void) folders->Count(&folderCount); for (i = folderCount - 1; i >= 0; i--) { curFolder = do_QueryElementAt(folders, i, &rv); if (NS_SUCCEEDED(rv)) { PRUint32 folderFlags; curFolder->GetFlags(&folderFlags); if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL) { RemoveSubFolder(curFolder); folders->RemoveElementAt(i); // since the folder pane only allows single selection, we can do this deleteNoTrash = confirmed = PR_TRUE; confirmDeletion = PR_FALSE; } } } nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { if (!deleteNoTrash) { rv = GetTrashFolder(getter_AddRefs(trashFolder)); //If we can't find the trash folder and we are supposed to move it to the trash //return failure. if(NS_FAILED(rv) || !trashFolder) return NS_ERROR_FAILURE; PRBool canHaveSubFoldersOfTrash = PR_TRUE; trashFolder->GetCanCreateSubfolders(&canHaveSubFoldersOfTrash); if (canHaveSubFoldersOfTrash) // UW server doesn't set NOINFERIORS - check dual use pref { nsCOMPtr imapServer; rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_SUCCEEDED(rv) && imapServer) { PRBool serverSupportsDualUseFolders; imapServer->GetDualUseFolders(&serverSupportsDualUseFolders); if (!serverSupportsDualUseFolders) canHaveSubFoldersOfTrash = PR_FALSE; } } if (!canHaveSubFoldersOfTrash) deleteNoTrash = PR_TRUE; nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) prefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash", &confirmDeletion); } if (!confirmed && (confirmDeletion || deleteNoTrash)) //let us alert the user if we are deleting folder immediately { nsXPIDLString confirmationStr; IMAPGetStringByID(((!deleteNoTrash) ? IMAP_MOVE_FOLDER_TO_TRASH : IMAP_DELETE_NO_TRASH), getter_Copies(confirmationStr)); if (!msgWindow) return NS_ERROR_NULL_POINTER; nsCOMPtr docShell; msgWindow->GetRootDocShell(getter_AddRefs(docShell)); nsCOMPtr dialog; if (docShell) dialog = do_GetInterface(docShell); if (dialog && confirmationStr) dialog->Confirm(nsnull, confirmationStr, &confirmed); } else confirmed = PR_TRUE; if (confirmed) { for (i = 0; i < folderCount; i++) { curFolder = do_QueryElementAt(folders, i, &rv); if (NS_SUCCEEDED(rv)) { urlListener = do_QueryInterface(curFolder); if (deleteNoTrash) rv = imapService->DeleteFolder(m_eventQueue, curFolder, urlListener, nsnull); else { PRBool confirm = PR_FALSE; PRBool match = PR_FALSE; rv = curFolder->MatchOrChangeFilterDestination(nsnull, PR_FALSE, &match); if (match) { curFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirm); if (!confirm) return NS_OK; } rv = imapService->MoveFolder(m_eventQueue, curFolder, trashFolder, urlListener, msgWindow, nsnull); } } } } } if (confirmed && deleteNoTrash) //delete subfolders only if you are deleting things from trash return nsMsgDBFolder::DeleteSubFolders(folders, msgWindow); return rv; } // Called by Biff, or when user presses GetMsg button. NS_IMETHODIMP nsImapMailFolder::GetNewMessages(nsIMsgWindow *aWindow, nsIUrlListener *aListener) { nsCOMPtr rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if(NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr imapServer; GetImapIncomingServer(getter_AddRefs(imapServer)); PRBool performingBiff = PR_FALSE; if (imapServer) { nsCOMPtr incomingServer = do_QueryInterface(imapServer, &rv); if (incomingServer) incomingServer->GetPerformingBiff(&performingBiff); } // Check preferences to see if we should check all folders for new // messages, or just the inbox and marked ones PRBool checkAllFolders = PR_FALSE; nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && prefBranch) // This pref might not exist, which is OK. We'll only check inbox and marked ones rv = prefBranch->GetBoolPref("mail.check_all_imap_folders_for_new", &checkAllFolders); m_urlListener = aListener; // Get new messages for inbox PRUint32 numFolders; nsCOMPtr inbox; rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(inbox)); if (inbox) { nsCOMPtr imapFolder = do_QueryInterface(inbox, &rv); if (imapFolder) imapFolder->SetPerformingBiff(performingBiff); inbox->SetGettingNewMessages(PR_TRUE); rv = inbox->UpdateFolder(aWindow); } // Get new messages for other folders if marked, or all of them if the pref is set if (imapServer) rv = imapServer->GetNewMessagesForNonInboxFolders(rootFolder, aWindow, checkAllFolders, performingBiff); } return rv; } NS_IMETHODIMP nsImapMailFolder::Shutdown(PRBool shutdownChildren) { m_filterList = nsnull; m_initialized = PR_FALSE; // m_pathName is used to decide if folder pathname needs to be reconstructed in GetPath(). delete m_pathName; m_pathName = nsnull; NS_IF_RELEASE(m_moveCoalescer); return nsMsgDBFolder::Shutdown(shutdownChildren); } nsresult nsImapMailFolder::GetBodysToDownload(nsMsgKeyArray *keysOfMessagesToDownload) { NS_ENSURE_ARG(keysOfMessagesToDownload); nsresult rv = NS_ERROR_NULL_POINTER; // if mDatabase not set if (mDatabase) { nsCOMPtr enumerator; rv = mDatabase->EnumerateMessages(getter_AddRefs(enumerator)); if (NS_SUCCEEDED(rv) && enumerator) { PRBool hasMore; while (NS_SUCCEEDED(rv = enumerator->HasMoreElements(&hasMore)) && (hasMore == PR_TRUE)) { nsCOMPtr pHeader; rv = enumerator->GetNext(getter_AddRefs(pHeader)); NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken"); if (pHeader && NS_SUCCEEDED(rv)) { PRBool shouldStoreMsgOffline = PR_FALSE; nsMsgKey msgKey; pHeader->GetMessageKey(&msgKey); // MsgFitsDownloadCriteria ignores MSG_FOLDER_FLAG_OFFLINE, which we want if (m_downloadingFolderForOfflineUse) MsgFitsDownloadCriteria(msgKey, &shouldStoreMsgOffline); else ShouldStoreMsgOffline(msgKey, &shouldStoreMsgOffline); if (shouldStoreMsgOffline) keysOfMessagesToDownload->Add(msgKey); } } } } return rv; } NS_IMETHODIMP nsImapMailFolder::OnNewIdleMessages() { PRBool checkAllFolders = PR_FALSE; nsCOMPtr prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID); if (prefBranch) // This pref might not exist, which is OK. (void) prefBranch->GetBoolPref("mail.check_all_imap_folders_for_new", &checkAllFolders); // only trigger biff if we're checking all new folders for new messages, or this particular folder, // but excluding trash,junk, sent, and no select folders, by default. if ((checkAllFolders && !(mFlags & (MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_JUNK | MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_IMAP_NOSELECT))) || (mFlags & (MSG_FOLDER_FLAG_CHECK_NEW|MSG_FOLDER_FLAG_INBOX))) SetPerformingBiff(PR_TRUE); return UpdateFolder(nsnull); } NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxInfo( nsIImapProtocol* aProtocol, nsIMailboxSpec* aSpec) { nsresult rv = NS_ERROR_FAILURE; ChangeNumPendingTotalMessages(-GetNumPendingTotalMessages()); ChangeNumPendingUnread(-GetNumPendingUnread()); m_numStatusRecentMessages = 0; // clear this since we selected the folder. m_numStatusUnseenMessages = 0; // clear this since we selected the folder. if (!mDatabase) GetDatabase(nsnull); PRBool folderSelected; rv = aSpec->GetFolderSelected(&folderSelected); if (NS_SUCCEEDED(rv) && folderSelected) { nsMsgKeyArray existingKeys; nsMsgKeyArray keysToDelete; nsMsgKeyArray keysToFetch; PRUint32 numNewUnread; nsCOMPtr dbFolderInfo; PRInt32 imapUIDValidity = 0; rv = NS_ERROR_UNEXPECTED; if (mDatabase) rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (NS_SUCCEEDED(rv) && dbFolderInfo) dbFolderInfo->GetImapUidValidity(&imapUIDValidity); if (mDatabase) { mDatabase->ListAllKeys(existingKeys); PRInt32 keyCount = existingKeys.GetSize(); mDatabase->ListAllOfflineDeletes(&existingKeys); if (keyCount < existingKeys.GetSize()) existingKeys.QuickSort(); } PRInt32 folderValidity; aSpec->GetFolder_UIDVALIDITY(&folderValidity); nsCOMPtr flagState; aSpec->GetFlagState(getter_AddRefs(flagState)); // remember what the supported user flags are. PRUint32 supportedUserFlags; aSpec->GetSupportedUserFlags(&supportedUserFlags); SetSupportedUserFlags(supportedUserFlags); m_uidValidity = folderValidity; if ((imapUIDValidity != folderValidity) /* && // if UIDVALIDITY Changed !NET_IsOffline() */) { NS_ASSERTION(PR_FALSE, "uid validity seems to have changed, blowing away db"); nsCOMPtr pathSpec; rv = GetPath(getter_AddRefs(pathSpec)); if (NS_FAILED(rv)) return rv; nsFileSpec dbName; rv = pathSpec->GetFileSpec(&dbName); if (NS_FAILED(rv)) return rv; nsCOMPtr msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr transferInfo; if (dbFolderInfo) dbFolderInfo->GetTransferInfo(getter_AddRefs(transferInfo)); if (mDatabase) { dbFolderInfo = nsnull; mDatabase->ForceClosed(); } mDatabase = nsnull; nsLocalFolderSummarySpec summarySpec(dbName); // Remove summary file. summarySpec.Delete(PR_FALSE); // Create a new summary file, update the folder message counts, and // Close the summary file db. rv = msgDBService->OpenFolderDB(this, PR_TRUE, PR_TRUE, getter_AddRefs(mDatabase)); // ********** Important ************* // David, help me here I don't know this is right or wrong if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) rv = NS_OK; if (NS_FAILED(rv) && mDatabase) { mDatabase->ForceClosed(); mDatabase = nsnull; } else if (NS_SUCCEEDED(rv) && mDatabase) { if (transferInfo) SetDBTransferInfo(transferInfo); SummaryChanged(); rv = NS_ERROR_UNEXPECTED; if (mDatabase) { if(mAddListener) mDatabase->AddListener(this); rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); } } // store the new UIDVALIDITY value if (NS_SUCCEEDED(rv) && dbFolderInfo) dbFolderInfo->SetImapUidValidity(folderValidity); // delete all my msgs, the keys are bogus now // add every message in this folder existingKeys.RemoveAll(); // keysToDelete.CopyArray(&existingKeys); if (flagState) { nsMsgKeyArray no_existingKeys; FindKeysToAdd(no_existingKeys, keysToFetch, numNewUnread, flagState); } if (NS_FAILED(rv)) dbName.Delete(PR_FALSE); } else if (!flagState /*&& !NET_IsOffline() */) // if there are no messages on the server { keysToDelete.CopyArray(&existingKeys); } else /* if ( !NET_IsOffline()) */ { FindKeysToDelete(existingKeys, keysToDelete, flagState); PRUint32 boxFlags; aSpec->GetBox_flags(&boxFlags); // if this is the result of an expunge then don't grab headers if (!(boxFlags & kJustExpunged)) FindKeysToAdd(existingKeys, keysToFetch, numNewUnread, flagState); } if (keysToDelete.GetSize()) { PRUint32 total; // It would be nice to notify RDF or whoever of a mass delete here. if (mDatabase) { mDatabase->DeleteMessages(&keysToDelete, nsnull); total = keysToDelete.GetSize(); } } // If we are performing biff for this folder, tell the // stand-alone biff about the new high water mark if (m_performingBiff) { if (numNewUnread > 0) { // We must ensure that the server knows that we are performing biff. // Otherwise the stand-alone biff won't fire. nsCOMPtr server; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) server->SetPerformingBiff(PR_TRUE); SetNumNewMessages(numNewUnread); } } SyncFlags(flagState); PRInt32 numUnreadFromServer; aSpec->GetNumUnseenMessages(&numUnreadFromServer); if (mDatabase&& mNumUnreadMessages + keysToFetch.GetSize() > numUnreadFromServer) mDatabase->SyncCounts(); if (keysToFetch.GetSize()) { PrepareToAddHeadersToMailDB(aProtocol, keysToFetch, aSpec); } else { // let the imap libnet module know that we don't need headers if (aProtocol) aProtocol->NotifyHdrsToDownload(nsnull, 0); PRBool gettingNewMessages; GetGettingNewMessages(&gettingNewMessages); if (gettingNewMessages) ProgressStatus(aProtocol,IMAP_NO_NEW_MESSAGES, nsnull); SetPerformingBiff(PR_FALSE); } } return rv; } NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxStatus( nsIImapProtocol* aProtocol, nsIMailboxSpec* aSpec) { NS_ENSURE_ARG_POINTER(aSpec); PRInt32 numRecent, numUnread; aSpec->GetNumRecentMessages(&numRecent); aSpec->GetNumUnseenMessages(&numUnread); // If m_numStatusUnseenMessages is 0, it means // this is the first time we've done a Status. // In that case, we count all the previous pending unread messages we know about // as unread messages. // We may want to do similar things with total messages, but the total messages // include deleted messages if the folder hasn't been expunged. PRInt32 previousUnreadMessages = (m_numStatusUnseenMessages) ? m_numStatusUnseenMessages : GetNumPendingUnread() + mNumUnreadMessages; if (numUnread != previousUnreadMessages) { // we're going to assume that recent messages are unread. ChangeNumPendingUnread(numUnread - previousUnreadMessages); ChangeNumPendingTotalMessages(numUnread - previousUnreadMessages); if (numUnread > previousUnreadMessages) { SetHasNewMessages(PR_TRUE); SetNumNewMessages(numUnread - previousUnreadMessages); SetBiffState(nsMsgBiffState_NewMail); } SummaryChanged(); } SetPerformingBiff(PR_FALSE); m_numStatusUnseenMessages = numUnread; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ParseMsgHdrs(nsIImapProtocol *aProtocol, nsIImapHeaderXferInfo *aHdrXferInfo) { PRInt32 numHdrs; nsCOMPtr headerInfo; nsCOMPtr aImapUrl; nsImapAction imapAction = nsIImapUrl::nsImapTest; // unused value. if (!mDatabase) GetDatabase(nsnull); nsresult rv = aHdrXferInfo->GetNumHeaders(&numHdrs); if (aProtocol) { (void) aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl)); if (aImapUrl) aImapUrl->GetImapAction(&imapAction); } for (PRUint32 i = 0; NS_SUCCEEDED(rv) && i < numHdrs; i++) { rv = aHdrXferInfo->GetHeader(i, getter_AddRefs(headerInfo)); NS_ENSURE_SUCCESS(rv, rv); if (!headerInfo) break; PRInt32 msgSize; nsMsgKey msgKey; PRBool containsKey; const char *msgHdrs; headerInfo->GetMsgSize(&msgSize); headerInfo->GetMsgUid(&msgKey); if (msgKey == nsMsgKey_None) // not a valid uid. continue; if (imapAction == nsIImapUrl::nsImapMsgPreview) { nsCOMPtr msgHdr; headerInfo->GetMsgHdrs(&msgHdrs); // create an input stream based on the hdr string. nsCOMPtr inputStream = do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv); NS_ENSURE_SUCCESS(rv, rv); inputStream->ShareData(msgHdrs, strlen(msgHdrs)); GetMessageHeader(msgKey, getter_AddRefs(msgHdr)); if (msgHdr) GetMsgPreviewTextFromStream(msgHdr, inputStream); continue; } if (mDatabase && NS_SUCCEEDED(mDatabase->ContainsKey(msgKey, &containsKey)) && containsKey) { NS_ASSERTION(PR_FALSE, "downloading hdrs for hdr we already have"); continue; } nsresult rv = SetupHeaderParseStream(msgSize, nsnull, nsnull); NS_ENSURE_SUCCESS(rv, rv); headerInfo->GetMsgHdrs(&msgHdrs); rv = ParseAdoptedHeaderLine(msgHdrs, msgKey); NS_ENSURE_SUCCESS(rv, rv); rv = NormalEndHeaderParseStream(aProtocol, aImapUrl); } return rv; } nsresult nsImapMailFolder::SetupHeaderParseStream(PRUint32 aSize, const char *content_type, nsIMailboxSpec *boxSpec) { if (!mDatabase) GetDatabase(nsnull); m_nextMessageByteLength = aSize; if (!m_msgParser) m_msgParser = do_CreateInstance(kParseMailMsgStateCID); else m_msgParser->Clear(); if (m_msgParser) { m_msgParser->SetMailDB(mDatabase); return m_msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState); } return NS_ERROR_OUT_OF_MEMORY; } nsresult nsImapMailFolder::ParseAdoptedHeaderLine(const char *aMessageLine, PRUint32 aMsgKey) { // we can get blocks that contain more than one line, // but they never contain partial lines const char *str = aMessageLine; m_curMsgUid = aMsgKey; m_msgParser->SetEnvelopePos(m_curMsgUid); // m_envelope_pos, for local folders, // is the msg key. Setting this will set the msg key for the new header. PRInt32 len = strlen(str); char *currentEOL = PL_strstr(str, MSG_LINEBREAK); const char *currentLine = str; while (currentLine < (str + len)) { if (currentEOL) { m_msgParser->ParseAFolderLine(currentLine, (currentEOL + MSG_LINEBREAK_LEN) - currentLine); currentLine = currentEOL + MSG_LINEBREAK_LEN; currentEOL = PL_strstr(currentLine, MSG_LINEBREAK); } else { m_msgParser->ParseAFolderLine(currentLine, PL_strlen(currentLine)); currentLine = str + len + 1; } } return NS_OK; } nsresult nsImapMailFolder::NormalEndHeaderParseStream(nsIImapProtocol *aProtocol, nsIImapUrl* imapUrl) { nsCOMPtr newMsgHdr; nsresult rv = NS_OK; if (m_msgParser) { nsMailboxParseState parseState; m_msgParser->GetState(&parseState); if (parseState == nsIMsgParseMailMsgState::ParseHeadersState) m_msgParser->ParseAFolderLine(CRLF, 2); m_msgParser->GetNewMsgHdr(getter_AddRefs(newMsgHdr)); } if (NS_SUCCEEDED(rv) && newMsgHdr) { char *headers; PRInt32 headersSize; nsCOMPtr msgWindow; nsCOMPtr msgUrl; if (imapUrl) { msgUrl = do_QueryInterface(imapUrl); if (msgUrl) msgUrl->GetMsgWindow(getter_AddRefs(msgWindow)); } nsCOMPtr server; rv = GetServer(getter_AddRefs(server)); if (NS_SUCCEEDED(rv)) // don't use NS_ENSURE_SUCCESS here; it's not a fatal error { nsXPIDLCString redirectorType; server->GetRedirectorType(getter_Copies(redirectorType)); // only notify redirected type servers of new hdrs for performance if (!redirectorType.IsEmpty()) NotifyFolderEvent(mImapHdrDownloadedAtom); } newMsgHdr->SetMessageKey(m_curMsgUid); TweakHeaderFlags(aProtocol, newMsgHdr); PRUint32 messageSize; if (NS_SUCCEEDED(newMsgHdr->GetMessageSize(&messageSize))) mFolderSize += messageSize; m_msgMovedByFilter = PR_FALSE; // If this is the inbox, try to apply filters. if (mFlags & MSG_FOLDER_FLAG_INBOX) { PRUint32 msgFlags; newMsgHdr->GetFlags(&msgFlags); if (!(msgFlags & (MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED))) // only fire on unread msgs that haven't been deleted { PRInt32 duplicateAction = nsIMsgIncomingServer::keepDups; if (server) server->GetIncomingDuplicateAction(&duplicateAction); if (duplicateAction != nsIMsgIncomingServer::keepDups) { PRBool isDup; server->IsNewHdrDuplicate(newMsgHdr, &isDup); if (isDup) { // we want to do something similar to applying filter hits. // if a dup is marked read, it shouldn't trigger biff. // Same for deleting it or moving it to trash. switch (duplicateAction) { case nsIMsgIncomingServer::deleteDups: { PRUint32 newFlags; newMsgHdr->OrFlags(MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED, &newFlags); nsMsgKeyArray keysToFlag; keysToFlag.Add(m_curMsgUid); StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull); m_msgMovedByFilter = PR_TRUE; } break; case nsIMsgIncomingServer::moveDupsToTrash: { nsCOMPtr trash; GetTrashFolder(getter_AddRefs(trash)); if (trash) { nsXPIDLCString trashUri; trash->GetURI(getter_Copies(trashUri)); nsresult err = MoveIncorporatedMessage(newMsgHdr, mDatabase, trashUri, nsnull, msgWindow); if (NS_SUCCEEDED(err)) m_msgMovedByFilter = PR_TRUE; } } break; case nsIMsgIncomingServer::markDupsRead: { PRUint32 newFlags; nsMsgKeyArray keysToFlag; keysToFlag.Add(m_curMsgUid); newMsgHdr->OrFlags(MSG_FLAG_READ, &newFlags); StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull); } break; } PRInt32 numNewMessages; GetNumNewMessages(PR_FALSE, &numNewMessages); SetNumNewMessages(numNewMessages - 1); } } rv = m_msgParser->GetAllHeaders(&headers, &headersSize); if (NS_SUCCEEDED(rv) && headers && !m_msgMovedByFilter) { if (m_filterList) { GetMoveCoalescer(); // not sure why we're doing this here. m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr, this, mDatabase, headers, headersSize, this, msgWindow, nsnull); } } } } // here we need to tweak flags from uid state.. if (mDatabase && (!m_msgMovedByFilter || ShowDeletedMessages())) { mDatabase->AddNewHdrToDB(newMsgHdr, PR_TRUE); nsCOMPtr notifier = do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID); if (notifier) notifier->NotifyItemAdded(newMsgHdr); } m_msgParser->Clear(); // clear out parser, because it holds onto a msg hdr. m_msgParser->SetMailDB(nsnull); // tell it to let go of the db too. // I don't think we want to do this - it does bad things like set the size incorrectly. // m_msgParser->FinishHeader(); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::AbortHeaderParseStream(nsIImapProtocol* aProtocol) { nsresult rv = NS_ERROR_FAILURE; return rv; } NS_IMETHODIMP nsImapMailFolder::BeginCopy(nsIMsgDBHdr *message) { nsresult rv = NS_ERROR_NULL_POINTER; if (!m_copyState) return rv; if (m_copyState->m_tmpFileSpec) // leftover file spec nuke it { PRBool isOpen = PR_FALSE; rv = m_copyState->m_tmpFileSpec->IsStreamOpen(&isOpen); if (isOpen) m_copyState->m_tmpFileSpec->CloseStream(); nsFileSpec fileSpec; m_copyState->m_tmpFileSpec->GetFileSpec(&fileSpec); if (fileSpec.Valid()) fileSpec.Delete(PR_FALSE); m_copyState->m_tmpFileSpec = nsnull; } if (message) m_copyState->m_message = do_QueryInterface(message, &rv); nsSpecialSystemDirectory tmpFileSpec(nsSpecialSystemDirectory::OS_TemporaryDirectory); tmpFileSpec += "nscpmsg.txt"; tmpFileSpec.MakeUnique(); rv = NS_NewFileSpecWithSpec(tmpFileSpec, getter_AddRefs(m_copyState->m_tmpFileSpec)); nsCOMPtr msgFile; if (NS_SUCCEEDED(rv)) rv = NS_FileSpecToIFile(&tmpFileSpec, getter_AddRefs(msgFile)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr fileOutputStream; rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutputStream), msgFile, -1, 00600); NS_ENSURE_SUCCESS(rv,rv); rv = NS_NewBufferedOutputStream(getter_AddRefs(m_copyState->m_msgFileStream), fileOutputStream, FOUR_K); NS_ENSURE_SUCCESS(rv,rv); if (!m_copyState->m_dataBuffer) m_copyState->m_dataBuffer = (char*) PR_CALLOC(COPY_BUFFER_SIZE+1); if (!m_copyState->m_dataBuffer) return NS_ERROR_OUT_OF_MEMORY; m_copyState->m_dataBufferSize = COPY_BUFFER_SIZE; return rv; } NS_IMETHODIMP nsImapMailFolder::CopyDataToOutputStreamForAppend(nsIInputStream *aIStream, PRInt32 aLength, nsIOutputStream *outputStream) { PRUint32 readCount; PRUint32 writeCount; if (!m_copyState) { nsImapMailCopyState* copyState = new nsImapMailCopyState(); m_copyState = do_QueryInterface(copyState); } if ( aLength + m_copyState->m_leftOver > m_copyState->m_dataBufferSize ) { m_copyState->m_dataBuffer = (char *) PR_REALLOC(m_copyState->m_dataBuffer, aLength + m_copyState->m_leftOver+ 1); if (!m_copyState->m_dataBuffer) return NS_ERROR_OUT_OF_MEMORY; m_copyState->m_dataBufferSize = aLength + m_copyState->m_leftOver; } char *start, *end; PRUint32 linebreak_len = 1; nsresult rv = aIStream->Read(m_copyState->m_dataBuffer+m_copyState->m_leftOver, aLength, &readCount); if (NS_FAILED(rv)) return rv; m_copyState->m_leftOver += readCount; m_copyState->m_dataBuffer[m_copyState->m_leftOver] = '\0'; start = m_copyState->m_dataBuffer; if (m_copyState->m_eatLF) { if (*start == nsCRT::LF) start++; m_copyState->m_eatLF = PR_FALSE; } end = PL_strpbrk(start, "\r\n"); if (end && *end == '\r' && *(end+1) == '\n') linebreak_len = 2; while (start && end) { if (PL_strncasecmp(start, "X-Mozilla-Status:", 17) && PL_strncasecmp(start, "X-Mozilla-Status2:", 18) && PL_strncmp(start, "From - ", 7)) { rv = outputStream->Write(start, end-start, &writeCount); rv = outputStream->Write(CRLF, 2, &writeCount); } start = end+linebreak_len; if (start >= m_copyState->m_dataBuffer+m_copyState->m_leftOver) { m_copyState->m_leftOver = 0; break; } linebreak_len = 1; end = PL_strpbrk(start, "\r\n"); if (end && *end == '\r') { if (*(end+1) == '\n') linebreak_len = 2; else if (! *(end+1)) // block might have split CRLF so remember if m_copyState->m_eatLF = PR_TRUE; // we should eat LF } if (start && !end) { m_copyState->m_leftOver -= (start - m_copyState->m_dataBuffer); memcpy(m_copyState->m_dataBuffer, start, m_copyState->m_leftOver+1); // including null } } return rv; } NS_IMETHODIMP nsImapMailFolder::CopyDataDone() { m_copyState = nsnull; return NS_OK; } // sICopyMessageListener methods, BeginCopy, CopyData, EndCopy, EndMove, StartMessage, EndMessage NS_IMETHODIMP nsImapMailFolder::CopyData(nsIInputStream *aIStream, PRInt32 aLength) { nsresult rv = NS_ERROR_NULL_POINTER; NS_ASSERTION(m_copyState && m_copyState->m_tmpFileSpec && m_copyState->m_dataBuffer, "Fatal copy operation error\n"); if (!m_copyState || !m_copyState->m_msgFileStream || !m_copyState->m_dataBuffer) return rv; return CopyDataToOutputStreamForAppend(aIStream, aLength, m_copyState->m_msgFileStream); } NS_IMETHODIMP nsImapMailFolder::EndCopy(PRBool copySucceeded) { nsresult rv = copySucceeded ? NS_OK : NS_ERROR_FAILURE; if (copySucceeded && m_copyState && m_copyState->m_msgFileStream) { nsCOMPtr urlListener; m_copyState->m_msgFileStream->Close(); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); nsCOMPtr copySupport; if (m_copyState) copySupport = do_QueryInterface(m_copyState); rv = imapService->AppendMessageFromFile(m_eventQueue, m_copyState->m_tmpFileSpec, this, "", PR_TRUE, m_copyState->m_selectedState, urlListener, nsnull, copySupport, m_copyState->m_msgWindow); } return rv; } NS_IMETHODIMP nsImapMailFolder::EndMove(PRBool moveSucceeded) { return NS_OK; } // this is the beginning of the next message copied NS_IMETHODIMP nsImapMailFolder::StartMessage() { return NS_OK; } // just finished the current message. NS_IMETHODIMP nsImapMailFolder::EndMessage(nsMsgKey key) { return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWindow *msgWindow, PRBool *applyMore) { NS_ENSURE_ARG_POINTER(applyMore); nsMsgRuleActionType actionType; nsXPIDLCString actionTargetFolderUri; PRUint32 newFlags; nsresult rv = NS_OK; // look at action - currently handle move #ifdef DEBUG_bienvenu printf("got a rule hit!\n"); #endif nsCOMPtr msgHdr; if (m_msgParser) m_msgParser->GetNewMsgHdr(getter_AddRefs(msgHdr)); if (!msgHdr) return NS_ERROR_NULL_POINTER; //fatal error, cannot apply filters PRBool deleteToTrash = DeleteIsMoveToTrash(); nsCOMPtr filterActionList; rv = NS_NewISupportsArray(getter_AddRefs(filterActionList)); NS_ENSURE_SUCCESS(rv, rv); rv = filter->GetSortedActionList(filterActionList); NS_ENSURE_SUCCESS(rv, rv); PRUint32 numActions; rv = filterActionList->Count(&numActions); NS_ENSURE_SUCCESS(rv, rv); PRBool loggingEnabled = PR_FALSE; if (m_filterList && numActions) (void)m_filterList->GetLoggingEnabled(&loggingEnabled); PRBool msgIsNew = PR_TRUE; for (PRUint32 actionIndex = 0; actionIndex < numActions; actionIndex++) { nsCOMPtr filterAction; filterActionList->QueryElementAt(actionIndex, NS_GET_IID(nsIMsgRuleAction), getter_AddRefs(filterAction)); if (!filterAction) continue; if (NS_SUCCEEDED(filterAction->GetType(&actionType))) { if (actionType == nsMsgFilterAction::MoveToFolder || actionType == nsMsgFilterAction::CopyToFolder) { filterAction->GetTargetFolderUri(getter_Copies(actionTargetFolderUri)); if (actionTargetFolderUri.IsEmpty()) { NS_ASSERTION(PR_FALSE, "actionTargetFolderUri is empty"); continue; } } PRUint32 msgFlags; nsMsgKey msgKey; nsCAutoString trashNameVal; msgHdr->GetFlags(&msgFlags); msgHdr->GetMessageKey(&msgKey); PRBool isRead = (msgFlags & MSG_FLAG_READ); switch (actionType) { case nsMsgFilterAction::Delete: { if (deleteToTrash) { // set value to trash folder nsCOMPtr mailTrash; rv = GetTrashFolder(getter_AddRefs(mailTrash)); if (NS_SUCCEEDED(rv) && mailTrash) rv = mailTrash->GetURI(getter_Copies(actionTargetFolderUri)); // msgHdr->OrFlags(MSG_FLAG_READ, &newFlags); // mark read in trash. } else // (!deleteToTrash) { msgHdr->OrFlags(MSG_FLAG_READ | MSG_FLAG_IMAP_DELETED, &newFlags); nsMsgKeyArray keysToFlag; keysToFlag.Add(msgKey); StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull); m_msgMovedByFilter = PR_TRUE; // this will prevent us from adding the header to the db. } msgIsNew = PR_FALSE; } // note that delete falls through to move. case nsMsgFilterAction::MoveToFolder: { // if moving to a different file, do it. nsXPIDLCString uri; rv = GetURI(getter_Copies(uri)); if ((const char*)actionTargetFolderUri && nsCRT::strcmp(uri, actionTargetFolderUri)) { msgHdr->GetFlags(&msgFlags); if (msgFlags & MSG_FLAG_MDN_REPORT_NEEDED && !isRead) { msgHdr->SetFlags(msgFlags & ~MSG_FLAG_MDN_REPORT_NEEDED); msgHdr->OrFlags(MSG_FLAG_MDN_REPORT_SENT, &newFlags); } nsresult err = MoveIncorporatedMessage(msgHdr, mDatabase, actionTargetFolderUri, filter, msgWindow); if (NS_SUCCEEDED(err)) m_msgMovedByFilter = PR_TRUE; } // don't apply any more filters, even if it was a move to the same folder *applyMore = PR_FALSE; } break; case nsMsgFilterAction::CopyToFolder: { nsXPIDLCString uri; rv = GetURI(getter_Copies(uri)); if (NS_STATIC_CAST(const char *, actionTargetFolderUri) && strcmp(uri, actionTargetFolderUri)) { // XXXshaver I'm not actually 100% what the right semantics are for // MDNs and copied messages, but I suspect deep down inside that // we probably want to suppress them only on the copies. msgHdr->GetFlags(&msgFlags); if (msgFlags & MSG_FLAG_MDN_REPORT_NEEDED && !isRead) { msgHdr->SetFlags(msgFlags & ~MSG_FLAG_MDN_REPORT_NEEDED); msgHdr->OrFlags(MSG_FLAG_MDN_REPORT_SENT, &newFlags); } nsCOMPtr messageArray; NS_NewISupportsArray(getter_AddRefs(messageArray)); messageArray->AppendElement(msgHdr); nsCOMPtr dstFolder; rv = GetExistingFolder(actionTargetFolderUri, getter_AddRefs(dstFolder)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = copyService->CopyMessages(this, messageArray, dstFolder, PR_FALSE, nsnull, msgWindow, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); } } break; case nsMsgFilterAction::MarkRead: { nsMsgKeyArray keysToFlag; keysToFlag.Add(msgKey); mDatabase->MarkHdrRead(msgHdr, PR_TRUE, nsnull); StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull); msgIsNew = PR_FALSE; } break; case nsMsgFilterAction::MarkFlagged: { nsMsgKeyArray keysToFlag; keysToFlag.Add(msgKey); mDatabase->MarkHdrMarked(msgHdr, PR_TRUE, nsnull); StoreImapFlags(kImapMsgFlaggedFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull); } break; case nsMsgFilterAction::KillThread: // The db will check for this flag when a hdr gets added to the db, and set the flag appropriately on the thread object msgHdr->OrFlags(MSG_FLAG_IGNORED, &newFlags); break; case nsMsgFilterAction::WatchThread: msgHdr->OrFlags(MSG_FLAG_WATCHED, &newFlags); break; case nsMsgFilterAction::ChangePriority: { nsMsgPriorityValue filterPriority; filterAction->GetPriority(&filterPriority); msgHdr->SetPriority(filterPriority); } break; case nsMsgFilterAction::Label: { nsMsgLabelValue filterLabel; filterAction->GetLabel(&filterLabel); msgHdr->SetLabel(filterLabel); nsMsgKeyArray keysToFlag; keysToFlag.Add(msgKey); StoreImapFlags((filterLabel << 9), PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize(), nsnull); } break; case nsMsgFilterAction::AddTag: { nsXPIDLCString keyword; filterAction->GetStrValue(getter_Copies(keyword)); nsCOMPtr messageArray; NS_NewISupportsArray(getter_AddRefs(messageArray)); messageArray->AppendElement(msgHdr); AddKeywordsToMessages(messageArray, keyword.get()); break; } case nsMsgFilterAction::JunkScore: { nsCAutoString junkScoreStr; PRInt32 junkScore; filterAction->GetJunkScore(&junkScore); junkScoreStr.AppendInt(junkScore); mDatabase->SetStringProperty(msgKey, "junkscore", junkScoreStr.get()); mDatabase->SetStringProperty(msgKey, "junkscoreorigin", /* ### should this be plugin? */"plugin"); if (junkScore == 100 || !junkScore) // if score is 0 or 100, set up to store junk status on server. { nsMsgKeyArray *keysToClassify = m_moveCoalescer->GetKeyBucket((junkScore == 100) ? 0 : 1); NS_ASSERTION(keysToClassify, "error getting key bucket"); if (keysToClassify) keysToClassify->Add(msgKey); if (junkScore == 100) msgIsNew = PR_FALSE; } } break; case nsMsgFilterAction::Forward: { nsXPIDLCString forwardTo; filterAction->GetStrValue(getter_Copies(forwardTo)); nsCOMPtr server; rv = GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); if (!forwardTo.IsEmpty()) { nsCOMPtr compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ; if (compService) { nsAutoString forwardStr; forwardStr.AssignWithConversion(forwardTo.get()); rv = compService->ForwardMessage(forwardStr, msgHdr, msgWindow, server); } } } break; case nsMsgFilterAction::Reply: { nsXPIDLCString replyTemplateUri; filterAction->GetStrValue(getter_Copies(replyTemplateUri)); nsCOMPtr server; GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); if (!replyTemplateUri.IsEmpty()) { nsCOMPtr compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ; if (compService) rv = compService->ReplyWithTemplate(msgHdr, replyTemplateUri, msgWindow, server); } } break; default: break; } if (loggingEnabled) { // only log if successful move, or non-move action if (m_msgMovedByFilter || (actionType != nsMsgFilterAction::MoveToFolder && (actionType != nsMsgFilterAction::Delete || !deleteToTrash))) (void) filter->LogRuleHit(filterAction, msgHdr); } } } if (!msgIsNew) { PRInt32 numNewMessages; GetNumNewMessages(PR_FALSE, &numNewMessages); SetNumNewMessages(numNewMessages - 1); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetImapFlags(const char *uids, PRInt32 flags, nsIURI **url) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->SetMessageFlags(m_eventQueue, this, this, url, uids, flags, PR_TRUE); } // "this" is the parent folder NS_IMETHODIMP nsImapMailFolder::PlaybackOfflineFolderCreate(const PRUnichar *aFolderName, nsIMsgWindow *aWindow, nsIURI **url) { NS_ENSURE_ARG_POINTER(aFolderName); nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->CreateFolder(m_eventQueue, this, aFolderName, this, url); } NS_IMETHODIMP nsImapMailFolder::ReplayOfflineMoveCopy(nsMsgKey *msgKeys, PRUint32 numKeys, PRBool isMove, nsIMsgFolder *aDstFolder, nsIUrlListener *aUrlListener, nsIMsgWindow *aWindow) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr resultUrl; nsCAutoString uids; AllocateUidStringFromKeys(msgKeys, numKeys, uids); rv = imapService->OnlineMessageCopy(m_eventQueue, this, uids.get(), aDstFolder, PR_TRUE, isMove, aUrlListener, getter_AddRefs(resultUrl), nsnull, aWindow); if (resultUrl) { nsCOMPtr mailnewsUrl = do_QueryInterface(resultUrl); if (mailnewsUrl) { nsCOMPtr folderListener = do_QueryInterface(aDstFolder); if (folderListener) mailnewsUrl->RegisterListener(folderListener); } } } return rv; } NS_IMETHODIMP nsImapMailFolder::StoreImapFlags(PRInt32 flags, PRBool addFlags, nsMsgKey *keys, PRUint32 numKeys, nsIUrlListener *aUrlListener) { nsresult rv = NS_OK; if (!WeAreOffline()) { nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCAutoString msgIds; AllocateUidStringFromKeys(keys, numKeys, msgIds); if (addFlags) { imapService->AddMessageFlags(m_eventQueue, this, aUrlListener ? aUrlListener : this, nsnull, msgIds.get(), flags, PR_TRUE); } else { imapService->SubtractMessageFlags(m_eventQueue, this, aUrlListener ? aUrlListener : this, nsnull, msgIds.get(), flags, PR_TRUE); } } } else { GetDatabase(nsnull); if (mDatabase) { PRUint32 total = numKeys; for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++) { nsCOMPtr op; rv = mDatabase->GetOfflineOpForKey(keys[keyIndex], PR_TRUE, getter_AddRefs(op)); SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS); if (NS_SUCCEEDED(rv) && op) { imapMessageFlagsType newFlags; op->GetNewFlags(&newFlags); if (addFlags) op->SetFlagOperation(newFlags | flags); else op->SetFlagOperation(newFlags & ~flags); } } mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); // flush offline flags } } return rv; } NS_IMETHODIMP nsImapMailFolder::LiteSelect(nsIUrlListener *aUrlListener) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->LiteSelectFolder(m_eventQueue, this, aUrlListener, nsnull); } nsresult nsImapMailFolder::GetFolderOwnerUserName(char **userName) { if ((mFlags & MSG_FOLDER_FLAG_IMAP_PERSONAL) || !(mFlags & (MSG_FOLDER_FLAG_IMAP_PUBLIC | MSG_FOLDER_FLAG_IMAP_OTHER_USER))) { // this is one of our personal mail folders // return our username on this host nsCOMPtr server; nsresult rv = GetServer(getter_AddRefs(server)); if (NS_SUCCEEDED(rv) && server) return server->GetUsername(userName); else return rv; } // the only other type of owner is if it's in the other users' namespace if (!(mFlags & MSG_FOLDER_FLAG_IMAP_OTHER_USER)) return NS_OK; if (m_ownerUserName.IsEmpty()) { nsXPIDLCString onlineName; GetOnlineName(getter_Copies(onlineName)); m_ownerUserName = nsIMAPNamespaceList::GetFolderOwnerNameFromPath(GetNamespaceForFolder(), onlineName.get()); } *userName = !m_ownerUserName.IsEmpty() ? ToNewCString(m_ownerUserName) : nsnull; return NS_OK; } // returns the online folder name, with the other users' namespace and his username // stripped out nsresult nsImapMailFolder::GetOwnersOnlineFolderName(char **retName) { nsXPIDLCString onlineName; GetOnlineName(getter_Copies(onlineName)); if (mFlags & MSG_FOLDER_FLAG_IMAP_OTHER_USER) { nsXPIDLCString user; GetFolderOwnerUserName(getter_Copies(user)); if (!onlineName.IsEmpty() && !user.IsEmpty()) { const char *where = PL_strstr(onlineName.get(), user.get()); NS_ASSERTION(where, "user name not in online name"); if (where) { const char *relativeFolder = where + strlen(user) + 1; if (!relativeFolder) // root of this user's personal namespace { *retName = PL_strdup(""); return NS_OK; } else { *retName = PL_strdup(relativeFolder); return NS_OK; } } } *retName = PL_strdup(onlineName.get()); return NS_OK; } else if (!(mFlags & MSG_FOLDER_FLAG_IMAP_PUBLIC)) { // We own this folder. *retName = nsIMAPNamespaceList::GetFolderNameWithoutNamespace(GetNamespaceForFolder(), onlineName); } else *retName = PL_strdup(onlineName.get()); return NS_OK; } nsIMAPNamespace *nsImapMailFolder::GetNamespaceForFolder() { if (!m_namespace) { #ifdef DEBUG_bienvenu // Make sure this isn't causing us to open the database NS_ASSERTION(m_hierarchyDelimiter != kOnlineHierarchySeparatorUnknown, "haven't set hierarchy delimiter"); #endif nsXPIDLCString serverKey; nsXPIDLCString onlineName; GetServerKey(getter_Copies(serverKey)); GetOnlineName(getter_Copies(onlineName)); PRUnichar hierarchyDelimiter; GetHierarchyDelimiter(&hierarchyDelimiter); m_namespace = nsIMAPNamespaceList::GetNamespaceForFolder(serverKey.get(), onlineName.get(), (char) hierarchyDelimiter); NS_ASSERTION(m_namespace, "didn't get namespace for folder"); if (m_namespace) { nsIMAPNamespaceList::SuggestHierarchySeparatorForNamespace(m_namespace, (char) hierarchyDelimiter); m_folderIsNamespace = nsIMAPNamespaceList::GetFolderIsNamespace(serverKey.get(), onlineName.get(), (char) hierarchyDelimiter, m_namespace); } } return m_namespace; } void nsImapMailFolder::SetNamespaceForFolder(nsIMAPNamespace *ns) { #ifdef DEBUG_bienvenu NS_ASSERTION(ns, "null namespace"); #endif m_namespace = ns; } nsresult nsImapMailFolder::GetServerAdminUrl(char **aAdminUrl) { nsCOMPtr imapServer; nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_SUCCEEDED(rv) && imapServer) rv = imapServer->GetManageMailAccountUrl(aAdminUrl); return rv; } NS_IMETHODIMP nsImapMailFolder::FolderPrivileges(nsIMsgWindow *window) { nsresult rv = NS_ERROR_NULL_POINTER; // if no window... if (window) { #ifdef DEBUG_bienvenu m_adminUrl.Assign("http://www.netscape.com"); #endif if (!m_adminUrl.IsEmpty()) { nsCOMPtr docShell; rv = window->GetRootDocShell(getter_AddRefs(docShell)); if (NS_SUCCEEDED(rv) && docShell) { nsCOMPtr uri; if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(uri), m_adminUrl.get()))) return rv; rv = docShell->LoadURI(uri, nsnull, nsIWebNavigation::LOAD_FLAGS_IS_LINK, PR_FALSE); } } else { nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); rv = imapService->GetFolderAdminUrl(m_eventQueue, this, window, this, nsnull); if (NS_SUCCEEDED(rv)) m_urlRunning = PR_TRUE; } } return rv; } NS_IMETHODIMP nsImapMailFolder::GetHasAdminUrl(PRBool *aBool) { NS_ENSURE_ARG_POINTER(aBool); nsXPIDLCString manageMailAccountUrl; nsresult rv = GetServerAdminUrl(getter_Copies(manageMailAccountUrl)); *aBool = (NS_SUCCEEDED(rv) && !manageMailAccountUrl.IsEmpty()); return rv; } NS_IMETHODIMP nsImapMailFolder::GetAdminUrl(char **aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = ToNewCString(m_adminUrl); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetAdminUrl(const char *adminUrl) { m_adminUrl = adminUrl; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetHdrParser(nsIMsgParseMailMsgState **aHdrParser) { NS_ENSURE_ARG_POINTER(aHdrParser); NS_IF_ADDREF(*aHdrParser = m_msgParser); return NS_OK; } // this is used to issue an arbitrary imap command on the passed in msgs. // It assumes the command needs to be run in the selected state. NS_IMETHODIMP nsImapMailFolder::IssueCommandOnMsgs(const char *command, const char *uids, nsIMsgWindow *aWindow, nsIURI **url) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->IssueCommandOnMsgs(m_eventQueue, this, aWindow, command, uids, url); } NS_IMETHODIMP nsImapMailFolder::FetchCustomMsgAttribute(const char *attribute, const char *uids, nsIMsgWindow *aWindow, nsIURI **url) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->FetchCustomMsgAttribute(m_eventQueue, this, aWindow, attribute, uids, url); } nsresult nsImapMailFolder::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr, nsIMsgDatabase *sourceDB, const nsACString& destFolderUri, nsIMsgFilter *filter, nsIMsgWindow *msgWindow) { nsresult err = NS_OK; if (m_moveCoalescer) { nsCOMPtr rdf(do_GetService(kRDFServiceCID, &err)); nsCOMPtr res; err = rdf->GetResource(destFolderUri, getter_AddRefs(res)); if (NS_FAILED(err)) return err; nsCOMPtr destIFolder(do_QueryInterface(res, &err)); if (NS_FAILED(err)) return err; if (destIFolder) { // check if the destination is a real folder (by checking for null parent) // and if it can file messages (e.g., servers or news folders can't file messages). // Or read only imap folders... PRBool canFileMessages = PR_TRUE; nsCOMPtr parentFolder; destIFolder->GetParent(getter_AddRefs(parentFolder)); if (parentFolder) destIFolder->GetCanFileMessages(&canFileMessages); if (filter && (!parentFolder || !canFileMessages)) { filter->SetEnabled(PR_FALSE); m_filterList->SaveToDefaultFile(); destIFolder->ThrowAlertMsg("filterDisabled",msgWindow); return NS_MSG_NOT_A_MAIL_FOLDER; } // put the header into the source db, since it needs to be there when we copy it // and we need a valid header to pass to StartAsyncCopyMessagesInto nsMsgKey keyToFilter; mailHdr->GetMessageKey(&keyToFilter); if (sourceDB && destIFolder) { PRBool imapDeleteIsMoveToTrash = DeleteIsMoveToTrash(); m_moveCoalescer->AddMove (destIFolder, keyToFilter); // For each folder, we need to keep track of the ids we want to move to that // folder - we used to store them in the MSG_FolderInfo and then when we'd finished // downloading headers, we'd iterate through all the folders looking for the ones // that needed messages moved into them - perhaps instead we could // keep track of nsIMsgFolder, nsMsgKeyArray pairs here in the imap code. // nsMsgKeyArray *idsToMoveFromInbox = msgFolder->GetImapIdsToMoveFromInbox(); // idsToMoveFromInbox->Add(keyToFilter); if (imapDeleteIsMoveToTrash) { } PRBool isRead = PR_FALSE; mailHdr->GetIsRead(&isRead); if (!isRead) destIFolder->SetFlag(MSG_FOLDER_FLAG_GOT_NEW); if (imapDeleteIsMoveToTrash) err = 0; } } } // we have to return an error because we do not actually move the message // it is done async and that can fail return err; } // both of these algorithms assume that key arrays and flag states are sorted by increasing key. void nsImapMailFolder::FindKeysToDelete(const nsMsgKeyArray &existingKeys, nsMsgKeyArray &keysToDelete, nsIImapFlagAndUidState *flagState) { PRBool showDeletedMessages = ShowDeletedMessages(); PRUint32 total = existingKeys.GetSize(); PRInt32 messageIndex; int onlineIndex=0; // current index into flagState for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++) { PRUint32 uidOfMessage; flagState->GetNumberOfMessages(&messageIndex); while ((onlineIndex < messageIndex) && (flagState->GetUidOfMessage(onlineIndex, &uidOfMessage), (existingKeys[keyIndex] > uidOfMessage) )) { onlineIndex++; } imapMessageFlagsType flags; flagState->GetUidOfMessage(onlineIndex, &uidOfMessage); flagState->GetMessageFlags(onlineIndex, &flags); // delete this key if it is not there or marked deleted if ( (onlineIndex >= messageIndex ) || (existingKeys[keyIndex] != uidOfMessage) || ((flags & kImapMsgDeletedFlag) && !showDeletedMessages) ) { nsMsgKey doomedKey = existingKeys[keyIndex]; if ((PRInt32) doomedKey <= 0 && doomedKey != nsMsgKey_None) continue; else keysToDelete.Add(existingKeys[keyIndex]); } flagState->GetUidOfMessage(onlineIndex, &uidOfMessage); if (existingKeys[keyIndex] == uidOfMessage) onlineIndex++; } } void nsImapMailFolder::FindKeysToAdd(const nsMsgKeyArray &existingKeys, nsMsgKeyArray &keysToFetch, PRUint32 &numNewUnread, nsIImapFlagAndUidState *flagState) { PRBool showDeletedMessages = ShowDeletedMessages(); int dbIndex=0; // current index into existingKeys PRInt32 existTotal, numberOfKnownKeys; PRInt32 messageIndex; numNewUnread = 0; existTotal = numberOfKnownKeys = existingKeys.GetSize(); flagState->GetNumberOfMessages(&messageIndex); for (PRInt32 flagIndex=0; flagIndex < messageIndex; flagIndex++) { PRUint32 uidOfMessage; flagState->GetUidOfMessage(flagIndex, &uidOfMessage); while ( (flagIndex < numberOfKnownKeys) && (dbIndex < existTotal) && existingKeys[dbIndex] < uidOfMessage) dbIndex++; if ( (flagIndex >= numberOfKnownKeys) || (dbIndex >= existTotal) || (existingKeys[dbIndex] != uidOfMessage ) ) { numberOfKnownKeys++; imapMessageFlagsType flags; flagState->GetMessageFlags(flagIndex, &flags); NS_ASSERTION(uidOfMessage != nsMsgKey_None, "got invalid msg key"); if (uidOfMessage && uidOfMessage != nsMsgKey_None && (showDeletedMessages || ! (flags & kImapMsgDeletedFlag))) { if (mDatabase) { PRBool dbContainsKey; if (NS_SUCCEEDED(mDatabase->ContainsKey(uidOfMessage, &dbContainsKey)) && dbContainsKey) { NS_ASSERTION(PR_FALSE, "db has key - flagState messed up?"); continue; } } keysToFetch.Add(uidOfMessage); if (! (flags & kImapMsgSeenFlag)) numNewUnread++; } } } } void nsImapMailFolder::PrepareToAddHeadersToMailDB(nsIImapProtocol* aProtocol, const nsMsgKeyArray &keysToFetch, nsIMailboxSpec *boxSpec) { PRUint32 *theKeys = (PRUint32 *) PR_Malloc( keysToFetch.GetSize() * sizeof(PRUint32) ); if (theKeys) { PRUint32 total = keysToFetch.GetSize(); for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++) theKeys[keyIndex] = keysToFetch[keyIndex]; // tell the imap thread which hdrs to download if (aProtocol) { aProtocol->NotifyHdrsToDownload(theKeys, total /*keysToFetch.GetSize() */); // now, tell it we don't need any bodies. aProtocol->NotifyBodysToDownload(nsnull, 0); } } else { if (aProtocol) aProtocol->NotifyHdrsToDownload(nsnull, 0); } } void nsImapMailFolder::TweakHeaderFlags(nsIImapProtocol* aProtocol, nsIMsgDBHdr *tweakMe) { if (mDatabase && aProtocol && tweakMe) { tweakMe->SetMessageKey(m_curMsgUid); tweakMe->SetMessageSize(m_nextMessageByteLength); PRBool foundIt = PR_FALSE; imapMessageFlagsType imap_flags; nsXPIDLCString customFlags; nsresult res = aProtocol->GetFlagsForUID(m_curMsgUid, &foundIt, &imap_flags, getter_Copies(customFlags)); if (NS_SUCCEEDED(res) && foundIt) { // make a mask and clear these message flags PRUint32 mask = MSG_FLAG_READ | MSG_FLAG_REPLIED | MSG_FLAG_MARKED | MSG_FLAG_IMAP_DELETED | MSG_FLAG_LABELS; PRUint32 dbHdrFlags; tweakMe->GetFlags(&dbHdrFlags); tweakMe->AndFlags(~mask, &dbHdrFlags); // set the new value for these flags PRUint32 newFlags = 0; if (imap_flags & kImapMsgSeenFlag) newFlags |= MSG_FLAG_READ; else // if (imap_flags & kImapMsgRecentFlag) newFlags |= MSG_FLAG_NEW; // Okay here is the MDN needed logic (if DNT header seen): /* if server support user defined flag: MDNSent flag set => clear kMDNNeeded flag MDNSent flag not set => do nothing, leave kMDNNeeded on else if not MSG_FLAG_NEW => clear kMDNNeeded flag MSG_FLAG_NEW => do nothing, leave kMDNNeeded on */ PRUint16 userFlags; res = aProtocol->GetSupportedUserFlags(&userFlags); if (NS_SUCCEEDED(res) && (userFlags & (kImapMsgSupportUserFlag | kImapMsgSupportMDNSentFlag))) { if (imap_flags & kImapMsgMDNSentFlag) { newFlags |= MSG_FLAG_MDN_REPORT_SENT; if (dbHdrFlags & MSG_FLAG_MDN_REPORT_NEEDED) tweakMe->AndFlags(~MSG_FLAG_MDN_REPORT_NEEDED, &dbHdrFlags); } } if (imap_flags & kImapMsgAnsweredFlag) newFlags |= MSG_FLAG_REPLIED; if (imap_flags & kImapMsgFlaggedFlag) newFlags |= MSG_FLAG_MARKED; if (imap_flags & kImapMsgDeletedFlag) newFlags |= MSG_FLAG_IMAP_DELETED; if (imap_flags & kImapMsgForwardedFlag) newFlags |= MSG_FLAG_FORWARDED; // db label flags are 0x0E000000 and imap label flags are 0x0E00 // so we need to shift 16 bits to the left to convert them. if (imap_flags & kImapMsgLabelFlags) { // we need to set label attribute on header because the dbview code // does msgHdr->GetLabel when asked to paint a row tweakMe->SetLabel((imap_flags & kImapMsgLabelFlags) >> 9); newFlags |= (imap_flags & kImapMsgLabelFlags) << 16; } if (newFlags) tweakMe->OrFlags(newFlags, &dbHdrFlags); if (!customFlags.IsEmpty()) (void) HandleCustomFlags(m_curMsgUid, tweakMe, customFlags); } } } NS_IMETHODIMP //nsImapMailFolder::SetupMsgWriteStream(nsIFileSpec * aFileSpec, PRBool addDummyEnvelope) nsImapMailFolder::SetupMsgWriteStream(const char * aNativeString, PRBool addDummyEnvelope) { nsresult rv = NS_ERROR_FAILURE; // if (!aFileSpec) // return NS_ERROR_NULL_POINTER; nsFileSpec fileSpec (aNativeString); // aFileSpec->GetFileSpec(&fileSpec); fileSpec.Delete(PR_FALSE); nsCOMPtr supports; rv = NS_NewIOFileStream(getter_AddRefs(supports), fileSpec, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 00700); m_tempMessageStream = do_QueryInterface(supports); if (m_tempMessageStream && addDummyEnvelope) { 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; m_tempMessageStream->Write(result.get(), result.Length(), &writeCount); result = "X-Mozilla-Status: 0001"; result += MSG_LINEBREAK; m_tempMessageStream->Write(result.get(), result.Length(), &writeCount); result = "X-Mozilla-Status2: 00000000"; result += MSG_LINEBREAK; m_tempMessageStream->Write(result.get(), result.Length(), &writeCount); } return rv; } NS_IMETHODIMP nsImapMailFolder::DownloadMessagesForOffline(nsISupportsArray *messages, nsIMsgWindow *window) { nsCAutoString messageIds; nsMsgKeyArray srcKeyArray; nsresult rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray); if (NS_FAILED(rv) || messageIds.IsEmpty()) return rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); SetNotifyDownloadedLines(PR_TRUE); rv = AcquireSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this)); if (NS_FAILED(rv)) { ThrowAlertMsg("operationFailedFolderBusy", window); return rv; } return imapService->DownloadMessagesForOffline(messageIds.get(), this, this, window); } NS_IMETHODIMP nsImapMailFolder::DownloadAllForOffline(nsIUrlListener *listener, nsIMsgWindow *msgWindow) { nsresult rv = NS_OK; nsCOMPtr runningURI; PRBool noSelect; GetFlag(MSG_FOLDER_FLAG_IMAP_NOSELECT, &noSelect); if (!noSelect) { nsCAutoString messageIdsToDownload; nsMsgKeyArray msgsToDownload; GetDatabase(msgWindow); m_downloadingFolderForOfflineUse = PR_TRUE; rv = AcquireSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this)); if (NS_FAILED(rv)) { ThrowAlertMsg("operationFailedFolderBusy", msgWindow); return rv; } SetNotifyDownloadedLines(PR_TRUE); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); // selecting the folder with m_downloadingFolderForOfflineUse true will cause // us to fetch any message bodies we don't have. rv = imapService->SelectFolder(m_eventQueue, this, listener, msgWindow, nsnull); if (NS_SUCCEEDED(rv)) m_urlRunning = PR_TRUE; } else return NS_MSG_FOLDER_UNREADABLE; return rv; } NS_IMETHODIMP nsImapMailFolder::GetNotifyDownloadedLines(PRBool *notifyDownloadedLines) { NS_ENSURE_ARG(notifyDownloadedLines); *notifyDownloadedLines = m_downloadMessageForOfflineUse; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetNotifyDownloadedLines(PRBool notifyDownloadedLines) { // ignore this if we're downloading the whole folder and someone says // to turn off downloading for offline use, which can happen if a 3rd party // app tries to stream a message while we're downloading for offline use. if (!notifyDownloadedLines && m_downloadingFolderForOfflineUse) return NS_OK; m_downloadMessageForOfflineUse = notifyDownloadedLines; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ParseAdoptedMsgLine(const char *adoptedMessageLine, nsMsgKey uidOfMessage) { PRUint32 count = 0; nsresult rv = NS_OK; // remember the uid of the message we're downloading. m_curMsgUid = uidOfMessage; if (m_downloadMessageForOfflineUse && !m_offlineHeader) { GetMessageHeader(uidOfMessage, getter_AddRefs(m_offlineHeader)); rv = StartNewOfflineMessage(); } // adoptedMessageLine is actually a string with a lot of message lines, separated by native line terminators // we need to count the number of MSG_LINEBREAK's to determine how much to increment m_numOfflineMsgLines by. if (m_downloadMessageForOfflineUse) { const char *nextLine = adoptedMessageLine; do { m_numOfflineMsgLines++; nextLine = PL_strstr(nextLine, MSG_LINEBREAK); if (nextLine) nextLine += MSG_LINEBREAK_LEN; } while (nextLine && *nextLine); } if (m_tempMessageStream) { nsCOMPtr seekable; seekable = do_QueryInterface(m_tempMessageStream); if (seekable) seekable->Seek(PR_SEEK_END, 0); rv = m_tempMessageStream->Write(adoptedMessageLine, PL_strlen(adoptedMessageLine), &count); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to write to stream"); } return rv; } void nsImapMailFolder::EndOfflineDownload() { if (m_tempMessageStream) { m_tempMessageStream->Close(); m_tempMessageStream = nsnull; if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } } NS_IMETHODIMP nsImapMailFolder::NormalEndMsgWriteStream(nsMsgKey uidOfMessage, PRBool markRead, nsIImapUrl *imapUrl) { nsresult res = NS_OK; PRBool commit = PR_FALSE; if (m_offlineHeader) EndNewOfflineMessage(); m_curMsgUid = uidOfMessage; if (commit && mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); return res; } NS_IMETHODIMP nsImapMailFolder::AbortMsgWriteStream() { return NS_ERROR_FAILURE; } // message move/copy related methods NS_IMETHODIMP nsImapMailFolder::OnlineCopyCompleted(nsIImapProtocol *aProtocol, ImapOnlineCopyState aCopyState) { NS_ENSURE_ARG_POINTER(aProtocol); nsresult rv; if (aCopyState == ImapOnlineCopyStateType::kSuccessfulCopy) { nsCOMPtr imapUrl; rv = aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl)); if (NS_FAILED(rv) || !imapUrl) return NS_ERROR_FAILURE; nsImapAction action; rv = imapUrl->GetImapAction(&action); if (NS_FAILED(rv)) return rv; if (action == nsIImapUrl::nsImapOnlineToOfflineMove) { nsXPIDLCString messageIds; rv = imapUrl->CreateListOfMessageIdsString(getter_Copies(messageIds)); if (NS_FAILED(rv)) return rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); return imapService->AddMessageFlags(m_eventQueue, this, nsnull, nsnull, messageIds, kImapMsgDeletedFlag, PR_TRUE); } /* unhandled action */ else return NS_ERROR_FAILURE; } /* unhandled copystate */ else { // whoops, this is the wrong folder - should use the source folder if (m_copyState) { nsCOMPtr srcFolder; srcFolder = do_QueryInterface(m_copyState->m_srcSupport, &rv); if (srcFolder) srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom); } return NS_ERROR_FAILURE; } return rv; } NS_IMETHODIMP nsImapMailFolder::PrepareToReleaseObject(nsISupports * aSupports) { NS_ASSERTION(!mSupportsToRelease, "can't prepare to release w/o releasing prev object"); mSupportsToRelease = aSupports; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ReleaseObject() { mSupportsToRelease = nsnull; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::CloseMockChannel(nsIImapMockChannel * aChannel) { aChannel->Close(); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ReleaseUrlCacheEntry(nsIMsgMailNewsUrl *aUrl) { if (aUrl) aUrl->SetMemCacheEntry(nsnull); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::BeginMessageUpload() { return NS_ERROR_FAILURE; } nsresult nsImapMailFolder::HandleCustomFlags(nsMsgKey uidOfMessage, nsIMsgDBHdr *dbHdr, nsXPIDLCString &keywords) { ToLowerCase(keywords); PRBool messageClassified = PR_TRUE; nsXPIDLCString::const_iterator b, e; if (FindInReadable(NS_LITERAL_CSTRING("NonJunk"), keywords.BeginReading(b), keywords.EndReading(e), nsCaseInsensitiveCStringComparator())) mDatabase->SetStringProperty(uidOfMessage, "junkscore", "0"); // Mac Mail uses "NotJunk" else if (FindInReadable(NS_LITERAL_CSTRING("NotJunk"), keywords.BeginReading(b), keywords.EndReading(e), nsCaseInsensitiveCStringComparator())) mDatabase->SetStringProperty(uidOfMessage, "junkscore", "0"); // ### TODO: we really should parse the keywords into space delimited keywords before checking else if (FindInReadable(NS_LITERAL_CSTRING("Junk"), keywords.BeginReading(b), keywords.EndReading(e), nsCaseInsensitiveCStringComparator())) { PRUint32 newFlags; dbHdr->AndFlags(~MSG_FLAG_NEW, &newFlags); mDatabase->SetStringProperty(uidOfMessage, "junkscore", "100"); } else messageClassified = PR_FALSE; if (messageClassified) { // only set the junkscore origin if it wasn't set before. We assume plugin since we // think that's the more common scenario. nsXPIDLCString existingProperty; dbHdr->GetStringProperty("junkscoreorigin", getter_Copies(existingProperty)); if (existingProperty.IsEmpty()) dbHdr->SetStringProperty("junkscoreorigin", "plugin"); } return dbHdr->SetStringProperty("keywords", keywords); } // synchronize the message flags in the database with the server flags nsresult nsImapMailFolder::SyncFlags(nsIImapFlagAndUidState *flagState) { nsresult rv = GetDatabase(nsnull); // we need a database for this NS_ENSURE_SUCCESS(rv, rv); // update all of the database flags PRInt32 messageIndex; PRUint32 messageSize; PRUint32 oldFolderSize = mFolderSize; // take this opportunity to recalculate the folder size: mFolderSize = 0; flagState->GetNumberOfMessages(&messageIndex); for (PRInt32 flagIndex=0; flagIndex < messageIndex; flagIndex++) { PRUint32 uidOfMessage; flagState->GetUidOfMessage(flagIndex, &uidOfMessage); imapMessageFlagsType flags; flagState->GetMessageFlags(flagIndex, &flags); nsCOMPtr dbHdr; PRBool containsKey; rv = mDatabase->ContainsKey(uidOfMessage , &containsKey); // if we don't have the header, don't diddle the flags. // GetMsgHdrForKey will create the header if it doesn't exist. if (NS_FAILED(rv) || !containsKey) continue; rv = mDatabase->GetMsgHdrForKey(uidOfMessage, getter_AddRefs(dbHdr)); if (NS_SUCCEEDED(dbHdr->GetMessageSize(&messageSize))) mFolderSize += messageSize; if (flags & kImapMsgCustomKeywordFlag) { nsXPIDLCString keywords; if (NS_SUCCEEDED(flagState->GetCustomFlags(uidOfMessage, getter_Copies(keywords)))) { if (!keywords.IsEmpty() && dbHdr && NS_SUCCEEDED(rv)) { HandleCustomFlags(uidOfMessage, dbHdr, keywords); } } } NotifyMessageFlagsFromHdr(dbHdr, uidOfMessage, flags); } if (oldFolderSize != mFolderSize) NotifyIntPropertyChanged(kFolderSizeAtom, oldFolderSize, mFolderSize); return NS_OK; } // helper routine to sync the flags on a given header nsresult nsImapMailFolder::NotifyMessageFlagsFromHdr(nsIMsgDBHdr *dbHdr, nsMsgKey msgKey, PRUint32 flags) { mDatabase->MarkHdrRead(dbHdr, (flags & kImapMsgSeenFlag) != 0, nsnull); mDatabase->MarkHdrReplied(dbHdr, (flags & kImapMsgAnsweredFlag) != 0, nsnull); mDatabase->MarkHdrMarked(dbHdr, (flags & kImapMsgFlaggedFlag) != 0, nsnull); mDatabase->MarkImapDeleted(msgKey, (flags & kImapMsgDeletedFlag) != 0, nsnull); // this turns on labels, but it doesn't handle the case where the user // unlabels a message on one machine, and expects it to be unlabeled // on their other machines. If I turn that on, I'll be removing all the labels // that were assigned before we started storing them on the server, which will // make some people very unhappy. if (flags & kImapMsgLabelFlags) mDatabase->SetLabel(msgKey, (flags & kImapMsgLabelFlags) >> 9); else { PRUint32 supportedFlags; GetSupportedUserFlags(&supportedFlags); if (supportedFlags & kImapMsgLabelFlags) mDatabase->SetLabel(msgKey, 0); } if (flags & kImapMsgMDNSentFlag) mDatabase->MarkMDNSent(msgKey, PR_TRUE, nsnull); return NS_OK; } // message flags operation - this is called (rarely) from the imap protocol, // proxied over from the imap thread to the ui thread NS_IMETHODIMP nsImapMailFolder::NotifyMessageFlags(PRUint32 flags, nsMsgKey msgKey) { if (NS_SUCCEEDED(GetDatabase(nsnull)) && mDatabase) { nsCOMPtr dbHdr; nsresult rv; PRBool containsKey; rv = mDatabase->ContainsKey(msgKey , &containsKey); // if we don't have the header, don't diddle the flags. // GetMsgHdrForKey will create the header if it doesn't exist. if (NS_FAILED(rv) || !containsKey) return rv; rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(dbHdr)); if(NS_SUCCEEDED(rv) && dbHdr) { NotifyMessageFlagsFromHdr(dbHdr, msgKey, flags); } } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::NotifyMessageDeleted(const char *onlineFolderName,PRBool deleteAllMsgs, const char *msgIdString) { const char *doomedKeyString = msgIdString; if (deleteAllMsgs) return NS_OK; char *keyTokenString = PL_strdup(doomedKeyString); nsMsgKeyArray affectedMessages; ParseUidString(keyTokenString, affectedMessages); if (doomedKeyString && !ShowDeletedMessages()) { if (affectedMessages.GetSize() > 0) // perhaps Search deleted these messages { GetDatabase(nsnull); if (mDatabase) mDatabase->DeleteMessages(&affectedMessages, nsnull); } } else if (doomedKeyString) // && !imapDeleteIsMoveToTrash { GetDatabase(nsnull); if (mDatabase) SetIMAPDeletedFlag(mDatabase, affectedMessages, nsnull); } PR_Free(keyTokenString); return NS_OK; } PRBool nsImapMailFolder::ShowDeletedMessages() { nsresult err; nsCOMPtr hostSession = do_GetService(kCImapHostSessionList, &err); PRBool showDeleted = PR_FALSE; if (NS_SUCCEEDED(err) && hostSession) { nsXPIDLCString serverKey; GetServerKey(getter_Copies(serverKey)); err = hostSession->GetShowDeletedMessagesForHost(serverKey, showDeleted); } // check for special folders that need to show deleted messages if (!showDeleted) { nsCOMPtr imapServer; nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_SUCCEEDED(rv) && imapServer) { // See if the redirector type has a different trash folder name (ie, not 'TRASH'). // If so then convert it to the beautified name (if configured) and compare it // against the current folder name. nsXPIDLCString specialTrashName; rv = imapServer->GetTrashFolderByRedirectorType(getter_Copies(specialTrashName)); if (NS_SUCCEEDED(rv)) { nsXPIDLString convertedName; rv = imapServer->ConvertFolderName(specialTrashName.get(), getter_Copies(convertedName)); if (NS_SUCCEEDED(rv)) { nsXPIDLString folderName; GetName(getter_Copies(folderName)); if (StringBeginsWith(folderName, convertedName, nsCaseInsensitiveStringComparator())) showDeleted = PR_TRUE; } } } } return showDeleted; } PRBool nsImapMailFolder::DeleteIsMoveToTrash() { nsresult err; nsCOMPtr hostSession = do_GetService(kCImapHostSessionList, &err); PRBool rv = PR_TRUE; if (NS_SUCCEEDED(err) && hostSession) { nsXPIDLCString serverKey; GetServerKey(getter_Copies(serverKey)); err = hostSession->GetDeleteIsMoveToTrashForHost(serverKey.get(), rv); } return rv; } nsresult nsImapMailFolder::GetTrashFolder(nsIMsgFolder **pTrashFolder) { if (!pTrashFolder) return NS_ERROR_NULL_POINTER; nsCOMPtr rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if(NS_SUCCEEDED(rv)) { PRUint32 numFolders; rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_TRASH, 1, &numFolders, pTrashFolder); if (numFolders != 1) rv = NS_ERROR_FAILURE; } return rv; } // store MSG_FLAG_IMAP_DELETED in the specified mailhdr records void nsImapMailFolder::SetIMAPDeletedFlag(nsIMsgDatabase *mailDB, const nsMsgKeyArray &msgids, PRBool markDeleted) { nsresult markStatus = 0; PRUint32 total = msgids.GetSize(); for (PRUint32 msgIndex=0; !markStatus && (msgIndex < total); msgIndex++) { markStatus = mailDB->MarkImapDeleted(msgids[msgIndex], markDeleted, nsnull); } } NS_IMETHODIMP nsImapMailFolder::GetMessageSizeFromDB(const char *id, PRBool idIsUid, PRUint32 *size) { nsresult rv = NS_ERROR_FAILURE; NS_ENSURE_ARG(size); *size = 0; (void) GetDatabase(nsnull); if (id && mDatabase) { PRUint32 key = atoi(id); nsCOMPtr mailHdr; NS_ASSERTION(idIsUid, "ids must be uids to get message size"); if (idIsUid) rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(mailHdr)); if (NS_SUCCEEDED(rv) && mailHdr) rv = mailHdr->GetMessageSize(size); } return rv; } NS_IMETHODIMP nsImapMailFolder::SetContentModified(nsIImapUrl *aImapUrl, nsImapContentModifiedType modified) { return aImapUrl->SetContentModified(modified); } NS_IMETHODIMP nsImapMailFolder::SetImageCacheSessionForUrl(nsIMsgMailNewsUrl *mailurl) { nsresult rv; nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr cacheSession; rv = imapService->GetCacheSession(getter_AddRefs(cacheSession)); if (NS_SUCCEEDED(rv) && cacheSession) rv = mailurl->SetImageCacheSession(cacheSession); return rv; } NS_IMETHODIMP nsImapMailFolder::GetCurMoveCopyMessageInfo(nsIImapUrl *runningUrl, PRTime *aDate, char **aKeywords, PRUint32 *aResult) { nsCOMPtr copyState; runningUrl->GetCopyState(getter_AddRefs(copyState)); if (copyState) { nsCOMPtr mailCopyState = do_QueryInterface(copyState); if (mailCopyState && mailCopyState->m_message) { nsMsgLabelValue label; PRUint32 supportedFlags = 0; mailCopyState->m_message->GetFlags(aResult); GetSupportedUserFlags(&supportedFlags); if (supportedFlags & (kImapMsgSupportUserFlag | kImapMsgLabelFlags)) { mailCopyState->m_message->GetLabel(&label); if (label != 0) *aResult |= label << 25; } if (aDate) mailCopyState->m_message->GetDate(aDate); if (aKeywords && (supportedFlags & (kImapMsgSupportUserFlag))) mailCopyState->m_message->GetStringProperty("keywords", aKeywords); } // if we don't have a source header, and it's not the drafts folder, // then mark the message read, since it must be an append to the // fcc or templates folder. else if (mailCopyState) *aResult = mailCopyState->m_newMsgFlags; } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { return NS_OK; } NS_IMETHODIMP nsImapMailFolder::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus) { return NS_OK; } NS_IMETHODIMP nsImapMailFolder::OnStartRunningUrl(nsIURI *aUrl) { NS_PRECONDITION(aUrl, "sanity check - need to be be running non-null url"); nsCOMPtr mailUrl = do_QueryInterface(aUrl); if (mailUrl) { PRBool updatingFolder; mailUrl->GetUpdatingFolder(&updatingFolder); m_updatingFolder = updatingFolder; } m_urlRunning = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode) { nsresult rv = NS_OK; PRBool endedOfflineDownload = PR_FALSE; m_urlRunning = PR_FALSE; m_updatingFolder = PR_FALSE; if (m_downloadingFolderForOfflineUse) { ReleaseSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this)); m_downloadingFolderForOfflineUse = PR_FALSE; endedOfflineDownload = PR_TRUE; EndOfflineDownload(); } nsCOMPtr session = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); if (aUrl) { nsCOMPtr msgWindow; nsCOMPtr mailUrl = do_QueryInterface(aUrl); nsCOMPtr imapUrl = do_QueryInterface(aUrl); PRBool folderOpen = PR_FALSE; if (mailUrl) mailUrl->GetMsgWindow(getter_AddRefs(msgWindow)); if (session) session->IsFolderOpenInWindow(this, &folderOpen); #ifdef DEBUG_bienvenu1 nsXPIDLCString urlSpec; aUrl->GetSpec(getter_Copies(urlSpec)); printf("stop running url %s\n", (const char *) urlSpec); #endif if (imapUrl) { DisplayStatusMsg(imapUrl, NS_LITERAL_STRING("").get()); nsImapAction imapAction = nsIImapUrl::nsImapTest; imapUrl->GetImapAction(&imapAction); if (imapAction == nsIImapUrl::nsImapMsgFetch || imapAction == nsIImapUrl::nsImapMsgDownloadForOffline) { ReleaseSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this)); SetNotifyDownloadedLines(PR_FALSE); if (!endedOfflineDownload) EndOfflineDownload(); } switch(imapAction) { case nsIImapUrl::nsImapDeleteMsg: case nsIImapUrl::nsImapOnlineMove: case nsIImapUrl::nsImapOnlineCopy: if (NS_SUCCEEDED(aExitCode)) { if (folderOpen) UpdateFolder(msgWindow); else UpdatePendingCounts(); } if (m_copyState) { nsCOMPtr srcFolder = do_QueryInterface(m_copyState->m_srcSupport, &rv); if (m_copyState->m_isMove && !m_copyState->m_isCrossServerOp) { if (NS_SUCCEEDED(aExitCode)) { nsCOMPtr srcDB; if (srcFolder) rv = srcFolder->GetMsgDatabase(msgWindow, getter_AddRefs(srcDB)); if (NS_SUCCEEDED(rv) && srcDB) { nsRefPtr msgTxn; nsMsgKeyArray srcKeyArray; if (m_copyState->m_allowUndo) { rv = m_copyState->m_undoMsgTxn->QueryInterface(NS_GET_IID(nsImapMoveCopyMsgTxn), getter_AddRefs(msgTxn)); if (msgTxn) msgTxn->GetSrcKeyArray(srcKeyArray); } else { nsCAutoString messageIds; rv = BuildIdsAndKeyArray(m_copyState->m_messages, messageIds, srcKeyArray); NS_ENSURE_SUCCESS(rv,rv); } if (!ShowDeletedMessages()) srcDB->DeleteMessages(&srcKeyArray, nsnull); else MarkMessagesImapDeleted(&srcKeyArray, PR_TRUE, srcDB); } srcFolder->EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE/* dbBatching*/); // even if we're showing deleted messages, // we still need to notify FE so it will show the imap deleted flag srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom); // is there a way to see that we think we have new msgs? nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) { PRBool showPreviewText; prefBranch->GetBoolPref("mail.biff.alert.show_preview", &showPreviewText); // if we're showing preview text, update ourselves if we got a new unread // message copied so that we can download the new headers and have a chance // to preview the msg bodies. if (!folderOpen && showPreviewText && m_copyState->m_unreadCount > 0 && ! (mFlags & (MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_JUNK))) UpdateFolder(msgWindow); } } else { srcFolder->EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE/* dbBatching*/); srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom); } } if (m_copyState->m_msgWindow && NS_SUCCEEDED(aExitCode)) //we should do this only if move/copy succeeds { nsCOMPtr txnMgr; m_copyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr)); if (txnMgr) { nsresult rv2 = txnMgr->DoTransaction(m_copyState->m_undoMsgTxn); NS_ASSERTION(NS_SUCCEEDED(rv2), "doing transaction failed"); } } (void) OnCopyCompleted(m_copyState->m_srcSupport, aExitCode); } // we're the dest folder of a move/copy - if we're not open in the ui, // then we should clear our nsMsgDatabase pointer. Otherwise, the db would // be open until the user selected it and then selected another folder. // but don't do this for the trash or inbox - we'll leave them open if (!folderOpen && ! (mFlags & (MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_INBOX))) SetMsgDatabase(nsnull); break; case nsIImapUrl::nsImapSubtractMsgFlags: { // this isn't really right - we'd like to know we were // deleting a message to start with, but it probably // won't do any harm. imapMessageFlagsType flags = 0; imapUrl->GetMsgFlags(&flags); //we need to subtract the delete flag in db only in case when we show deleted msgs if (flags & kImapMsgDeletedFlag && ShowDeletedMessages()) { nsCOMPtr db; rv = GetMsgDatabase(nsnull, getter_AddRefs(db)); if (NS_SUCCEEDED(rv) && db) { nsMsgKeyArray keyArray; char *keyString; imapUrl->CreateListOfMessageIdsString(&keyString); if (keyString) { ParseUidString(keyString, keyArray); MarkMessagesImapDeleted(&keyArray, PR_FALSE, db); db->Commit(nsMsgDBCommitType::kLargeCommit); nsCRT::free(keyString); } } } } break; case nsIImapUrl::nsImapAddMsgFlags: { imapMessageFlagsType flags = 0; imapUrl->GetMsgFlags(&flags); if (flags & kImapMsgDeletedFlag) { // we need to delete headers from db only when we don't show deleted msgs if (!ShowDeletedMessages()) { nsCOMPtr db; rv = GetMsgDatabase(nsnull, getter_AddRefs(db)); if (NS_SUCCEEDED(rv) && db) { nsMsgKeyArray keyArray; char *keyString = nsnull; imapUrl->CreateListOfMessageIdsString(&keyString); if (keyString) { ParseUidString(keyString, keyArray); db->DeleteMessages(&keyArray, nsnull); db->SetSummaryValid(PR_TRUE); db->Commit(nsMsgDBCommitType::kLargeCommit); nsCRT::free(keyString); } } } // see bug #188051 // only send the folder event only if we are deleting // (and not for other flag changes) NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom); } } break; case nsIImapUrl::nsImapAppendMsgFromFile: case nsIImapUrl::nsImapAppendDraftFromFile: if (m_copyState) { if (NS_SUCCEEDED(aExitCode)) { UpdatePendingCounts(); m_copyState->m_curIndex++; if (m_copyState->m_curIndex >= m_copyState->m_totalCount) { if (folderOpen) { // This gives a way for the caller to get notified // when the UpdateFolder url is done. nsCOMPtr saveUrlListener = m_urlListener; if (m_copyState->m_listener) m_urlListener = do_QueryInterface(m_copyState->m_listener); UpdateFolder(msgWindow); m_urlListener = saveUrlListener; } if (m_copyState->m_msgWindow && m_copyState->m_undoMsgTxn) { nsCOMPtr txnMgr; m_copyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr)); if (txnMgr) txnMgr->DoTransaction(m_copyState->m_undoMsgTxn); } (void) OnCopyCompleted(m_copyState->m_srcSupport, aExitCode); } } else //clear the copyState if copy has failed (void) OnCopyCompleted(m_copyState->m_srcSupport, aExitCode); } break; case nsIImapUrl::nsImapRenameFolder: if (NS_FAILED(aExitCode)) { nsCOMPtr folderRenameAtom; folderRenameAtom = do_GetAtom("RenameCompleted"); NotifyFolderEvent(folderRenameAtom); } break; case nsIImapUrl::nsImapDeleteAllMsgs: if (NS_SUCCEEDED(aExitCode)) { if (folderOpen) UpdateFolder(msgWindow); else { ChangeNumPendingTotalMessages(-mNumPendingTotalMessages); ChangeNumPendingUnread(-mNumPendingUnreadMessages); m_numStatusUnseenMessages = 0; } } break; case nsIImapUrl::nsImapListFolder: // check if folder is now verified - if not, // we should remove it? if (NS_SUCCEEDED(aExitCode) && !m_verifiedAsOnlineFolder) { nsCOMPtr parent; rv = GetParent(getter_AddRefs(parent)); if (NS_SUCCEEDED(rv) && parent) { nsCOMPtr imapParent = do_QueryInterface(parent); if (imapParent) imapParent->RemoveSubFolder(this); } } break; case nsIImapUrl::nsImapRefreshFolderUrls: // we finished getting an admin url for the folder. if (!m_adminUrl.IsEmpty()) FolderPrivileges(msgWindow); break; case nsIImapUrl::nsImapCreateFolder: if (NS_FAILED(aExitCode)) //if success notification already done { nsCOMPtr folderCreateAtom; folderCreateAtom = do_GetAtom("FolderCreateFailed"); NotifyFolderEvent(folderCreateAtom); } break; case nsIImapUrl::nsImapSubscribe: if (NS_SUCCEEDED(aExitCode) && msgWindow) { nsXPIDLCString canonicalFolderName; imapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(canonicalFolderName)); nsCOMPtr rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); if(NS_SUCCEEDED(rv) && rootFolder) { nsCOMPtr imapRoot = do_QueryInterface(rootFolder); if (imapRoot) { nsCOMPtr foundFolder; rv = imapRoot->FindOnlineSubFolder(canonicalFolderName, getter_AddRefs(foundFolder)); if (NS_SUCCEEDED(rv) && foundFolder) { nsXPIDLCString uri; nsCOMPtr msgFolder = do_QueryInterface(foundFolder); if (msgFolder) { msgFolder->GetURI(getter_Copies(uri)); msgWindow->SelectFolder(uri.get()); } } } } } break; default: break; } } // give base class a chance to send folder loaded notification... rv = nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode); // query it for a mailnews interface for now.... if (mailUrl) rv = mailUrl->UnRegisterListener(this); } SetGettingNewMessages(PR_FALSE); // if we're not running a url, we must not be getting new mail :-) if (m_urlListener) { m_urlListener->OnStopRunningUrl(aUrl, aExitCode); m_urlListener = nsnull; } return rv; } void nsImapMailFolder::UpdatePendingCounts() { if (m_copyState) { if (!m_copyState->m_isCrossServerOp) ChangeNumPendingTotalMessages(m_copyState->m_totalCount); else ChangeNumPendingTotalMessages(1); // count the moves that were unread int numUnread = m_copyState->m_unreadCount; if (numUnread) { m_numStatusUnseenMessages += numUnread; // adjust last status count by this delta. ChangeNumPendingUnread(numUnread); } SummaryChanged(); } } // nsIImapExtensionSink methods NS_IMETHODIMP nsImapMailFolder::ClearFolderRights() { SetFolderNeedsACLListed(PR_FALSE); delete m_folderACL; m_folderACL = new nsMsgIMAPFolderACL(this); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::AddFolderRights(const char *userName, const char *rights) { SetFolderNeedsACLListed(PR_FALSE); GetFolderACL()->SetFolderRightsForUser(userName, rights); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::RefreshFolderRights() { if (GetFolderACL()->GetIsFolderShared()) SetFlag(MSG_FOLDER_FLAG_PERSONAL_SHARED); else ClearFlag(MSG_FOLDER_FLAG_PERSONAL_SHARED); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetCopyResponseUid(const char* msgIdString, nsIImapUrl * aUrl) { // CopyMessages() only nsresult rv = NS_OK; nsRefPtr msgTxn; nsCOMPtr copyState; if (aUrl) aUrl->GetCopyState(getter_AddRefs(copyState)); if (copyState) { nsCOMPtr mailCopyState = do_QueryInterface(copyState, &rv); if (NS_FAILED(rv)) return rv; if (mailCopyState->m_undoMsgTxn) rv = mailCopyState->m_undoMsgTxn->QueryInterface(NS_GET_IID(nsImapMoveCopyMsgTxn), getter_AddRefs(msgTxn)); } if (msgTxn) msgTxn->SetCopyResponseUid(msgIdString); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::StartMessage(nsIMsgMailNewsUrl * aUrl) { nsCOMPtr imapUrl (do_QueryInterface(aUrl)); nsCOMPtr copyState; NS_ENSURE_TRUE(imapUrl, NS_ERROR_FAILURE); imapUrl->GetCopyState(getter_AddRefs(copyState)); if (copyState) { nsCOMPtr listener = do_QueryInterface(copyState); if (listener) listener->StartMessage(); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::EndMessage(nsIMsgMailNewsUrl * aUrl, nsMsgKey uidOfMessage) { nsCOMPtr imapUrl (do_QueryInterface(aUrl)); nsCOMPtr copyState; NS_ENSURE_TRUE(imapUrl, NS_ERROR_FAILURE); imapUrl->GetCopyState(getter_AddRefs(copyState)); if (copyState) { nsCOMPtr listener = do_QueryInterface(copyState); if (listener) listener->EndMessage(uidOfMessage); } return NS_OK; } #define WHITESPACE " \015\012" // token delimiter NS_IMETHODIMP nsImapMailFolder::NotifySearchHit(nsIMsgMailNewsUrl * aUrl, const char* searchHitLine) { nsresult rv = GetDatabase(nsnull /* don't need msg window, that's more for local mbox parsing */); if (!mDatabase || NS_FAILED(rv)) return rv; // expect search results in the form of "* SEARCH ..." // expect search results in the form of "* SEARCH ..." char *tokenString = nsCRT::strdup(searchHitLine); if (tokenString) { char *currentPosition = PL_strcasestr(tokenString, "SEARCH"); if (currentPosition) { currentPosition += strlen("SEARCH"); char *newStr; PRBool shownUpdateAlert = PR_FALSE; char *hitUidToken = nsCRT::strtok(currentPosition, WHITESPACE, &newStr); while (hitUidToken) { long naturalLong; // %l is 64 bits on OSF1 sscanf(hitUidToken, "%ld", &naturalLong); nsMsgKey hitUid = (nsMsgKey) naturalLong; nsCOMPtr hitHeader; rv = mDatabase->GetMsgHdrForKey(hitUid, getter_AddRefs(hitHeader)); if (NS_SUCCEEDED(rv) && hitHeader) { nsCOMPtr searchSession; nsCOMPtr searchAdapter; aUrl->GetSearchSession(getter_AddRefs(searchSession)); if (searchSession) { searchSession->GetRunningAdapter(getter_AddRefs(searchAdapter)); if (searchAdapter) searchAdapter->AddResultElement(hitHeader); } } else if (!shownUpdateAlert) { } hitUidToken = nsCRT::strtok(newStr, WHITESPACE, &newStr); } } nsCRT::free(tokenString); } else return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetAppendMsgUid(nsMsgKey aKey, nsIImapUrl * aUrl) { nsresult rv = NS_OK; nsCOMPtr copyState; if (aUrl) aUrl->GetCopyState(getter_AddRefs(copyState)); if (copyState) { nsCOMPtr mailCopyState = do_QueryInterface(copyState, &rv); if (NS_FAILED(rv)) return rv; if (mailCopyState->m_undoMsgTxn) // CopyMessages() { nsRefPtr msgTxn; rv = mailCopyState->m_undoMsgTxn->QueryInterface(NS_GET_IID(nsImapMoveCopyMsgTxn), getter_AddRefs(msgTxn)); if (NS_SUCCEEDED(rv)) msgTxn->AddDstKey(aKey); } else if (mailCopyState->m_listener) // CopyFileMessage(); // Draft/Template goes here mailCopyState->m_listener->SetMessageKey(aKey); } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetMessageId(nsIImapUrl * aUrl, nsACString &messageId) { nsresult rv = NS_OK; nsCOMPtr copyState; if (aUrl) aUrl->GetCopyState(getter_AddRefs(copyState)); if (copyState) { nsCOMPtr mailCopyState = do_QueryInterface(copyState, &rv); if (NS_FAILED(rv)) return rv; if (mailCopyState->m_listener) { nsCAutoString tempMessageId; rv = mailCopyState->m_listener->GetMessageId(&tempMessageId); messageId.Assign(tempMessageId); } } if (NS_SUCCEEDED(rv) && messageId.Length() > 0) { if (messageId.First() == '<') messageId.Cut(0, 1); if (messageId.Last() == '>') messageId.SetLength(messageId.Length() -1); } return rv; } NS_IMETHODIMP nsImapMailFolder::AddSearchResult(nsIImapProtocol* aProtocol, const char* searchHitLine) { return NS_ERROR_FAILURE; } NS_IMETHODIMP nsImapMailFolder::HeaderFetchCompleted(nsIImapProtocol* aProtocol) { nsCOMPtr msgWindow; // we might need this for the filter plugins. if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); SetSizeOnDisk(mFolderSize); PRInt32 numNewBiffMsgs = 0; if (m_performingBiff) GetNumNewMessages(PR_FALSE, &numNewBiffMsgs); PRBool pendingMoves = m_moveCoalescer && m_moveCoalescer->HasPendingMoves(); PlaybackCoalescedOperations(); if (aProtocol) { // check if we should download message bodies because it's the inbox and // the server is specified as one where where we download msg bodies automatically. // Or if we autosyncing all offline folders. nsCOMPtr imapServer; nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer)); PRBool autoDownloadNewHeaders = PR_FALSE; PRBool autoSyncOfflineStores = PR_FALSE; if (imapServer) imapServer->GetAutoSyncOfflineStores(&autoSyncOfflineStores); if (autoSyncOfflineStores || mFlags & MSG_FOLDER_FLAG_INBOX) { if (imapServer && mFlags & MSG_FOLDER_FLAG_INBOX && !autoSyncOfflineStores) imapServer->GetDownloadBodiesOnGetNewMail(&autoDownloadNewHeaders); // this isn't quite right - we only want to download bodies for new headers // but we don't know what the new headers are. We could query the inbox db // for new messages, if the above filter playback actually moves the filtered // messages before we get to this code. if (autoDownloadNewHeaders || autoSyncOfflineStores) { // acquire semaphore for offline store. If it fails, we won't download for offline use. if (NS_SUCCEEDED(AcquireSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this)))) m_downloadingFolderForOfflineUse = PR_TRUE; } } if (m_downloadingFolderForOfflineUse) { nsMsgKeyArray keysToDownload; GetBodysToDownload(&keysToDownload); if (keysToDownload.GetSize() > 0) SetNotifyDownloadedLines(PR_TRUE); aProtocol->NotifyBodysToDownload(keysToDownload.GetArray(), keysToDownload.GetSize()); } else aProtocol->NotifyBodysToDownload(nsnull, 0/*keysToFetch.GetSize() */); nsCOMPtr runningUri; aProtocol->GetRunningUrl(getter_AddRefs(runningUri)); if (runningUri) { nsCOMPtr mailnewsUrl = do_QueryInterface(runningUri); if (mailnewsUrl) mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow)); } } PRBool filtersRun; CallFilterPlugins(msgWindow, &filtersRun); if (!filtersRun && m_performingBiff && mDatabase && numNewBiffMsgs > 0 && (!pendingMoves || !ShowPreviewText())) { if (!pendingMoves) SetHasNewMessages(PR_TRUE); // If we are performing biff for this folder, tell the // stand-alone biff about the new high water mark // We must ensure that the server knows that we are performing biff. // Otherwise the stand-alone biff won't fire. nsCOMPtr server; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) server->SetPerformingBiff(PR_TRUE); SetBiffState(nsIMsgFolder::nsMsgBiffState_NewMail); if (server) server->SetPerformingBiff(PR_FALSE); m_performingBiff = PR_FALSE; } if (m_filterList) (void)m_filterList->FlushLogIfNecessary(); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetBiffStateAndUpdate(nsMsgBiffState biffState) { SetBiffState(biffState); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetUidValidity(PRInt32 *uidValidity) { NS_ENSURE_ARG(uidValidity); if (m_uidValidity == kUidUnknown) { nsCOMPtr db; nsCOMPtr dbFolderInfo; (void) GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(db)); if (db) db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (dbFolderInfo) dbFolderInfo->GetImapUidValidity((PRInt32 *) &m_uidValidity); } *uidValidity = m_uidValidity; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetUidValidity(PRInt32 uidValidity) { m_uidValidity = uidValidity; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::FillInFolderProps(nsIMsgImapFolderProps *aFolderProps) { NS_ENSURE_ARG(aFolderProps); PRUint32 folderTypeStringID; PRUint32 folderTypeDescStringID = 0; PRUint32 folderQuotaStatusStringID; nsXPIDLString folderType; nsXPIDLString folderTypeDesc; nsXPIDLString folderQuotaStatusDesc; nsCOMPtr bundle; nsresult rv = IMAPGetStringBundle(getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, rv); // get the host session list and get server capabilities. PRUint32 capability = kCapabilityUndefined; nsCOMPtr hostSession = do_GetService(kCImapHostSessionList, &rv); // if for some bizarre reason this fails, we'll still fall through to the normal sharing code if (NS_SUCCEEDED(rv) && hostSession) { nsXPIDLCString serverKey; GetServerKey(getter_Copies(serverKey)); hostSession->GetCapabilityForHost(serverKey, capability); // Figure out what to display in the Quota tab of the folder properties. // Does the server support quotas? if(capability & kQuotaCapability) { // Have we asked the server for quota information? if(m_folderQuotaCommandIssued) { // Has the server replied with storage quota info? if(m_folderQuotaDataIsValid) { // If so, set quota data folderQuotaStatusStringID = 0; aFolderProps->SetQuotaData(m_folderQuotaRoot, m_folderQuotaUsedKB, m_folderQuotaMaxKB); } else { // If not, there is no storage quota set on this folder folderQuotaStatusStringID = IMAP_QUOTA_STATUS_NOQUOTA; } } else { // The folder is not open, so no quota information is available folderQuotaStatusStringID = IMAP_QUOTA_STATUS_FOLDERNOTOPEN; } } else { // Either the server doesn't support quotas, or we don't know if it does // (e.g., because we don't have a connection yet). If the latter, we fall back // to saying that no information is available because the folder is not open. folderQuotaStatusStringID = (capability == kCapabilityUndefined) ? IMAP_QUOTA_STATUS_FOLDERNOTOPEN : IMAP_QUOTA_STATUS_NOTSUPPORTED; } if(folderQuotaStatusStringID == 0) { // Display quota data aFolderProps->ShowQuotaData(PR_TRUE); } else { // Hide quota data and show reason why it is not available aFolderProps->ShowQuotaData(PR_FALSE); rv = IMAPGetStringByID(folderQuotaStatusStringID, getter_Copies(folderQuotaStatusDesc)); if (NS_SUCCEEDED(rv)) aFolderProps->SetQuotaStatus(folderQuotaStatusDesc); } // See if the server supports ACL. // If not, just set the folder description to a string that says // the server doesn't support sharing, and return. if (! (capability & kACLCapability)) { rv = IMAPGetStringByID(IMAP_SERVER_DOESNT_SUPPORT_ACL, getter_Copies(folderTypeDesc)); if (NS_SUCCEEDED(rv)) aFolderProps->SetFolderTypeDescription(folderTypeDesc); aFolderProps->ServerDoesntSupportACL(); return NS_OK; } } if (mFlags & MSG_FOLDER_FLAG_IMAP_PUBLIC) { folderTypeStringID = IMAP_PUBLIC_FOLDER_TYPE_NAME; folderTypeDescStringID = IMAP_PUBLIC_FOLDER_TYPE_DESCRIPTION; } else if (mFlags & MSG_FOLDER_FLAG_IMAP_OTHER_USER) { folderTypeStringID = IMAP_OTHER_USERS_FOLDER_TYPE_NAME; nsXPIDLCString owner; nsXPIDLString uniOwner; GetFolderOwnerUserName(getter_Copies(owner)); if (owner.IsEmpty()) { rv = IMAPGetStringByID(folderTypeStringID, getter_Copies(uniOwner)); // Another user's folder, for which we couldn't find an owner name NS_ASSERTION(PR_FALSE, "couldn't get owner name for other user's folder"); } else { // is this right? It doesn't leak, does it? CopyASCIItoUTF16(owner, uniOwner); } const PRUnichar *params[] = { uniOwner.get() }; rv = bundle->FormatStringFromID(IMAP_OTHER_USERS_FOLDER_TYPE_DESCRIPTION, params, 1, getter_Copies(folderTypeDesc)); } else if (GetFolderACL()->GetIsFolderShared()) { folderTypeStringID = IMAP_PERSONAL_SHARED_FOLDER_TYPE_NAME; folderTypeDescStringID = IMAP_PERSONAL_SHARED_FOLDER_TYPE_DESCRIPTION; } else { folderTypeStringID = IMAP_PERSONAL_SHARED_FOLDER_TYPE_NAME; folderTypeDescStringID = IMAP_PERSONAL_FOLDER_TYPE_DESCRIPTION; } rv = IMAPGetStringByID(folderTypeStringID, getter_Copies(folderType)); if (NS_SUCCEEDED(rv)) aFolderProps->SetFolderType(folderType); if (folderTypeDesc.IsEmpty() && folderTypeDescStringID != 0) rv = IMAPGetStringByID(folderTypeDescStringID, getter_Copies(folderTypeDesc)); if (!folderTypeDesc.IsEmpty()) aFolderProps->SetFolderTypeDescription(folderTypeDesc.get()); nsXPIDLString rightsString; rv = CreateACLRightsStringForFolder(getter_Copies(rightsString)); if (NS_SUCCEEDED(rv)) aFolderProps->SetFolderPermissions(rightsString.get()); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetAclFlags(PRUint32 aclFlags) { nsresult rv = NS_OK; if (m_aclFlags != aclFlags) { nsCOMPtr dbFolderInfo; PRBool dbWasOpen = (mDatabase != nsnull); rv = GetDatabase(nsnull); m_aclFlags = aclFlags; if (mDatabase) { rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (NS_SUCCEEDED(rv) && dbFolderInfo) dbFolderInfo->SetUint32Property("aclFlags", aclFlags); // if setting the acl flags caused us to open the db, release the ref // because on startup, we might get acl on all folders,which will // leave a lot of db's open. if (!dbWasOpen) { mDatabase->Close(PR_TRUE /* commit changes */); mDatabase = nsnull; } } } return rv; } NS_IMETHODIMP nsImapMailFolder::GetAclFlags(PRUint32 *aclFlags) { NS_ENSURE_ARG_POINTER(aclFlags); nsresult rv = NS_OK; ReadDBFolderInfo(PR_FALSE); // update cache first. if (m_aclFlags == -1) // -1 means invalid value, so get it from db. { nsCOMPtr dbFolderInfo; PRBool dbWasOpen = (mDatabase != nsnull); rv = GetDatabase(nsnull); if (mDatabase) { rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (NS_SUCCEEDED(rv) && dbFolderInfo) { rv = dbFolderInfo->GetUint32Property("aclFlags", 0, aclFlags); m_aclFlags = *aclFlags; } // if getting the acl flags caused us to open the db, release the ref // because on startup, we might get acl on all folders,which will // leave a lot of db's open. if (!dbWasOpen) { mDatabase->Close(PR_TRUE /* commit changes */); mDatabase = nsnull; } } } else *aclFlags = m_aclFlags; return rv; } nsresult nsImapMailFolder::SetSupportedUserFlags(PRUint32 userFlags) { nsCOMPtr dbFolderInfo; nsresult rv = GetDatabase(nsnull); m_supportedUserFlags = userFlags; if (mDatabase) { rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (NS_SUCCEEDED(rv) && dbFolderInfo) dbFolderInfo->SetUint32Property("imapFlags", userFlags); } return rv; } nsresult nsImapMailFolder::GetSupportedUserFlags(PRUint32 *userFlags) { NS_ENSURE_ARG_POINTER(userFlags); nsresult rv = NS_OK; ReadDBFolderInfo(PR_FALSE); // update cache first. if (m_supportedUserFlags == 0) // 0 means invalid value, so get it from db. { nsCOMPtr dbFolderInfo; rv = GetDatabase(nsnull); if (mDatabase) { rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (NS_SUCCEEDED(rv) && dbFolderInfo) { rv = dbFolderInfo->GetUint32Property("imapFlags", 0, userFlags); m_supportedUserFlags = *userFlags; } } } else *userFlags = m_supportedUserFlags; return rv; } NS_IMETHODIMP nsImapMailFolder::GetCanIOpenThisFolder(PRBool *aBool) { NS_ENSURE_ARG_POINTER(aBool); PRBool noSelect; GetFlag(MSG_FOLDER_FLAG_IMAP_NOSELECT, &noSelect); *aBool = (noSelect) ? PR_FALSE : GetFolderACL()->GetCanIReadFolder(); return NS_OK; } ///////// nsMsgIMAPFolderACL class /////////////////////////////// // This string is defined in the ACL RFC to be "anyone" #define IMAP_ACL_ANYONE_STRING "anyone" /* static */PRBool nsMsgIMAPFolderACL::FreeHashRights(nsHashKey *aKey, void *aData, void *closure) { PR_FREEIF(aData); return PR_TRUE; } nsMsgIMAPFolderACL::nsMsgIMAPFolderACL(nsImapMailFolder *folder) { NS_ASSERTION(folder, "need folder"); m_folder = folder; m_rightsHash = new nsHashtable(24); m_aclCount = 0; BuildInitialACLFromCache(); } nsMsgIMAPFolderACL::~nsMsgIMAPFolderACL() { m_rightsHash->Reset(FreeHashRights, nsnull); delete m_rightsHash; } // We cache most of our own rights in the MSG_FOLDER_PREF_* flags void nsMsgIMAPFolderACL::BuildInitialACLFromCache() { nsCAutoString myrights; PRUint32 startingFlags; m_folder->GetAclFlags(&startingFlags); if (startingFlags & IMAP_ACL_READ_FLAG) myrights += "r"; if (startingFlags & IMAP_ACL_STORE_SEEN_FLAG) myrights += "s"; if (startingFlags & IMAP_ACL_WRITE_FLAG) myrights += "w"; if (startingFlags & IMAP_ACL_INSERT_FLAG) myrights += "i"; if (startingFlags & IMAP_ACL_POST_FLAG) myrights += "p"; if (startingFlags & IMAP_ACL_CREATE_SUBFOLDER_FLAG) myrights +="c"; if (startingFlags & IMAP_ACL_DELETE_FLAG) myrights += "dt"; if (startingFlags & IMAP_ACL_ADMINISTER_FLAG) myrights += "a"; if (!myrights.IsEmpty()) SetFolderRightsForUser(nsnull, myrights.get()); } void nsMsgIMAPFolderACL::UpdateACLCache() { PRUint32 startingFlags = 0; m_folder->GetAclFlags(&startingFlags); if (GetCanIReadFolder()) startingFlags |= IMAP_ACL_READ_FLAG; else startingFlags &= ~IMAP_ACL_READ_FLAG; if (GetCanIStoreSeenInFolder()) startingFlags |= IMAP_ACL_STORE_SEEN_FLAG; else startingFlags &= ~IMAP_ACL_STORE_SEEN_FLAG; if (GetCanIWriteFolder()) startingFlags |= IMAP_ACL_WRITE_FLAG; else startingFlags &= ~IMAP_ACL_WRITE_FLAG; if (GetCanIInsertInFolder()) startingFlags |= IMAP_ACL_INSERT_FLAG; else startingFlags &= ~IMAP_ACL_INSERT_FLAG; if (GetCanIPostToFolder()) startingFlags |= IMAP_ACL_POST_FLAG; else startingFlags &= ~IMAP_ACL_POST_FLAG; if (GetCanICreateSubfolder()) startingFlags |= IMAP_ACL_CREATE_SUBFOLDER_FLAG; else startingFlags &= ~IMAP_ACL_CREATE_SUBFOLDER_FLAG; if (GetCanIDeleteInFolder()) startingFlags |= IMAP_ACL_DELETE_FLAG; else startingFlags &= ~IMAP_ACL_DELETE_FLAG; if (GetCanIAdministerFolder()) startingFlags |= IMAP_ACL_ADMINISTER_FLAG; else startingFlags &= ~IMAP_ACL_ADMINISTER_FLAG; m_folder->SetAclFlags(startingFlags); } PRBool nsMsgIMAPFolderACL::SetFolderRightsForUser(const char *userName, const char *rights) { PRBool ret = PR_FALSE; nsXPIDLCString myUserName; nsCOMPtr server; nsresult rv = m_folder->GetServer(getter_AddRefs(server)); NS_ASSERTION(NS_SUCCEEDED(rv), "error getting server"); if (NS_FAILED(rv)) return PR_FALSE; // we need the real user name to match with what the imap server returns // in the acl response. server->GetRealUsername(getter_Copies(myUserName)); nsCAutoString ourUserName; if (!userName) ourUserName.Assign(myUserName); else ourUserName.Assign(userName); if(ourUserName.IsEmpty()) return ret; ToLowerCase(ourUserName); char *rightsWeOwn = PL_strdup(rights); nsCStringKey hashKey(ourUserName); if (rightsWeOwn) { char *oldValue = (char *) m_rightsHash->Get(&hashKey); if (oldValue) { PR_Free(oldValue); m_rightsHash->Remove(&hashKey); m_aclCount--; NS_ASSERTION(m_aclCount >= 0, "acl count can't go negative"); } m_aclCount++; ret = (m_rightsHash->Put(&hashKey, rightsWeOwn) == 0); } if (myUserName.Equals(ourUserName) || ourUserName.EqualsLiteral(IMAP_ACL_ANYONE_STRING)) { // if this is setting an ACL for me, cache it in the folder pref flags UpdateACLCache(); } return ret; } const char *nsMsgIMAPFolderACL::GetRightsStringForUser(const char *inUserName) { nsXPIDLCString userName; userName.Assign(inUserName); if (userName.IsEmpty()) { nsCOMPtr server; nsresult rv = m_folder->GetServer(getter_AddRefs(server)); NS_ASSERTION(NS_SUCCEEDED(rv), "error getting server"); if (NS_FAILED(rv)) return nsnull; // we need the real user name to match with what the imap server returns // in the acl response. server->GetRealUsername(getter_Copies(userName)); } ToLowerCase(userName); nsCStringKey userKey(userName.get()); return (const char *)m_rightsHash->Get(&userKey); } // First looks for individual user; then looks for 'anyone' if the user isn't found. // Returns defaultIfNotFound, if neither are found. PRBool nsMsgIMAPFolderACL::GetFlagSetInRightsForUser(const char *userName, char flag, PRBool defaultIfNotFound) { const char *flags = GetRightsStringForUser(userName); if (!flags) { const char *anyoneFlags = GetRightsStringForUser(IMAP_ACL_ANYONE_STRING); if (!anyoneFlags) return defaultIfNotFound; else return (strchr(anyoneFlags, flag) != nsnull); } else return (strchr(flags, flag) != nsnull); } PRBool nsMsgIMAPFolderACL::GetCanUserLookupFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'l', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserReadFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'r', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserStoreSeenInFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 's', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserWriteFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'w', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserInsertInFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'i', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserPostToFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'p', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserCreateSubfolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'c', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserDeleteInFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'd', PR_FALSE) || GetFlagSetInRightsForUser(userName, 't', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanUserAdministerFolder(const char *userName) { return GetFlagSetInRightsForUser(userName, 'a', PR_FALSE); } PRBool nsMsgIMAPFolderACL::GetCanILookupFolder() { return GetFlagSetInRightsForUser(nsnull, 'l', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIReadFolder() { return GetFlagSetInRightsForUser(nsnull, 'r', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIStoreSeenInFolder() { return GetFlagSetInRightsForUser(nsnull, 's', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIWriteFolder() { return GetFlagSetInRightsForUser(nsnull, 'w', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIInsertInFolder() { return GetFlagSetInRightsForUser(nsnull, 'i', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIPostToFolder() { return GetFlagSetInRightsForUser(nsnull, 'p', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanICreateSubfolder() { return GetFlagSetInRightsForUser(nsnull, 'c', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIDeleteInFolder() { return GetFlagSetInRightsForUser(nsnull, 'd', PR_TRUE) || GetFlagSetInRightsForUser(nsnull, 't', PR_TRUE); } PRBool nsMsgIMAPFolderACL::GetCanIAdministerFolder() { return GetFlagSetInRightsForUser(nsnull, 'a', PR_TRUE); } // We use this to see if the ACLs think a folder is shared or not. // We will define "Shared" in 5.0 to mean: // At least one user other than the currently authenticated user has at least one // explicitly-listed ACL right on that folder. PRBool nsMsgIMAPFolderACL::GetIsFolderShared() { // If we have more than one ACL count for this folder, which means that someone // other than ourself has rights on it, then it is "shared." if (m_aclCount > 1) return PR_TRUE; // Or, if "anyone" has rights to it, it is shared. nsCStringKey hashKey(IMAP_ACL_ANYONE_STRING); const char *anyonesRights = (const char *)m_rightsHash->Get(&hashKey); return (anyonesRights != nsnull); } PRBool nsMsgIMAPFolderACL::GetDoIHaveFullRightsForFolder() { return (GetCanIReadFolder() && GetCanIWriteFolder() && GetCanIInsertInFolder() && GetCanIAdministerFolder() && GetCanICreateSubfolder() && GetCanIDeleteInFolder() && GetCanILookupFolder() && GetCanIStoreSeenInFolder() && GetCanIPostToFolder()); } // Returns a newly allocated string describing these rights nsresult nsMsgIMAPFolderACL::CreateACLRightsString(PRUnichar **rightsString) { nsAutoString rights; nsXPIDLString curRight; nsCOMPtr bundle; nsresult rv = IMAPGetStringBundle(getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, rv); if (GetDoIHaveFullRightsForFolder()) { return bundle->GetStringFromID(IMAP_ACL_FULL_RIGHTS, rightsString); } else { if (GetCanIReadFolder()) { bundle->GetStringFromID(IMAP_ACL_READ_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanIWriteFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_WRITE_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanIInsertInFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_INSERT_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanILookupFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_LOOKUP_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanIStoreSeenInFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_SEEN_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanIDeleteInFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_DELETE_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanICreateSubfolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_CREATE_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanIPostToFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_POST_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } if (GetCanIAdministerFolder()) { if (!rights.IsEmpty()) rights.AppendLiteral(", "); bundle->GetStringFromID(IMAP_ACL_ADMINISTER_RIGHT, getter_Copies(curRight)); rights.Append(curRight); } } *rightsString = ToNewUnicode(rights); return rv; } NS_IMETHODIMP nsImapMailFolder::GetPath(nsIFileSpec ** aPathName) { nsresult rv; if (! m_pathName) { m_pathName = new nsNativeFileSpec(""); if (! m_pathName) return NS_ERROR_OUT_OF_MEMORY; rv = nsImapURI2Path(kImapRootURI, mURI.get(), *m_pathName); // printf("constructing path %s\n", (const char *) *m_pathName); if (NS_FAILED(rv)) return rv; } rv = NS_NewFileSpecWithSpec(*m_pathName, aPathName); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetPath(nsIFileSpec * aPathName) { nsMsgDBFolder::SetPath(aPathName); // call base class so mPath will get set too if (!aPathName) return NS_ERROR_NULL_POINTER; // not sure why imap has m_pathName and doesn't just use mPath. if (!m_pathName) { m_pathName = new nsFileSpec(""); if (! m_pathName) return NS_ERROR_OUT_OF_MEMORY; } return aPathName->GetFileSpec(m_pathName); } nsresult nsImapMailFolder::DisplayStatusMsg(nsIImapUrl *aImapUrl, const PRUnichar *msg) { nsCOMPtr mockChannel; aImapUrl->GetMockChannel(getter_AddRefs(mockChannel)); if (mockChannel) { nsCOMPtr progressSink; mockChannel->GetProgressEventSink(getter_AddRefs(progressSink)); if (progressSink) { nsCOMPtr request = do_QueryInterface(mockChannel); if (!request) return NS_ERROR_FAILURE; progressSink->OnStatus(request, nsnull, NS_OK, msg); // XXX i18n message } } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ProgressStatus(nsIImapProtocol* aProtocol, PRUint32 aMsgId, const PRUnichar *extraInfo) { nsXPIDLString progressMsg; nsCOMPtr server; nsresult rv = GetServer(getter_AddRefs(server)); if (NS_SUCCEEDED(rv) && server) { nsCOMPtr serverSink = do_QueryInterface(server); if (serverSink) serverSink->GetImapStringByID(aMsgId, getter_Copies(progressMsg)); } if (progressMsg.IsEmpty()) IMAPGetStringByID(aMsgId, getter_Copies(progressMsg)); if (aProtocol && !progressMsg.IsEmpty()) { nsCOMPtr imapUrl; aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl)); if (imapUrl) { if (extraInfo) { PRUnichar *printfString = nsTextFormatter::smprintf(progressMsg, extraInfo); if (printfString) progressMsg.Adopt(printfString); } DisplayStatusMsg(imapUrl, progressMsg); } } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::PercentProgress(nsIImapProtocol* aProtocol, const PRUnichar *aMessage, PRInt32 aCurrentProgress, PRInt32 aMaxProgress) { if (aProtocol) { nsCOMPtr imapUrl; aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl)); if (imapUrl) { nsCOMPtr mockChannel; imapUrl->GetMockChannel(getter_AddRefs(mockChannel)); if (mockChannel) { nsCOMPtr progressSink; mockChannel->GetProgressEventSink(getter_AddRefs(progressSink)); if (progressSink) { nsCOMPtr request = do_QueryInterface(mockChannel); if (!request) return NS_ERROR_FAILURE; // XXX handle 64-bit ints for real progressSink->OnProgress(request, nsnull, nsUint64(aCurrentProgress), nsUint64(aMaxProgress)); if (aMessage) progressSink->OnStatus(request, nsnull, NS_OK, aMessage); // XXX i18n message } } } } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::CopyNextStreamMessage(PRBool copySucceeded, nsISupports *copyState) { //if copy has failed it could be either user interrupted it or for some other reason //don't do any subsequent copies or delete src messages if it is move if (!copySucceeded) return NS_OK; nsresult rv; nsCOMPtr mailCopyState = do_QueryInterface(copyState, &rv); if (NS_FAILED(rv)) return rv; if (!mailCopyState->m_streamCopy) return NS_OK; if (mailCopyState->m_curIndex < mailCopyState->m_totalCount) { mailCopyState->m_message = do_QueryElementAt(mailCopyState->m_messages, mailCopyState->m_curIndex, &rv); if (NS_SUCCEEDED(rv)) { PRBool isRead; mailCopyState->m_message->GetIsRead(&isRead); mailCopyState->m_unreadCount = (isRead) ? 0 : 1; rv = CopyStreamMessage(mailCopyState->m_message, this, mailCopyState->m_msgWindow, mailCopyState->m_isMove); } } else { if (mailCopyState->m_isMove) { nsCOMPtr srcFolder = do_QueryInterface(mailCopyState->m_srcSupport, &rv); if (NS_SUCCEEDED(rv) && srcFolder) { srcFolder->DeleteMessages(mailCopyState->m_messages, nsnull, PR_TRUE, PR_TRUE, nsnull, PR_FALSE); // we want to send this notification after the source messages have // been deleted. nsCOMPtr popFolder = do_QueryInterface(srcFolder); if (popFolder) //needed if move pop->imap to notify FE srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom); } } } return rv; } NS_IMETHODIMP nsImapMailFolder::SetUrlState(nsIImapProtocol* aProtocol, nsIMsgMailNewsUrl* aUrl, PRBool isRunning, nsresult statusCode) { if (!isRunning) { ProgressStatus(aProtocol, IMAP_DONE, nsnull); m_urlRunning = PR_FALSE; // if no protocol, then we're reading from the mem or disk cache // and we don't want to end the offline download just yet. if (aProtocol) { EndOfflineDownload(); if (m_downloadingFolderForOfflineUse) { ReleaseSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this)); m_downloadingFolderForOfflineUse = PR_FALSE; } } } if (aUrl) return aUrl->SetUrlState(isRunning, statusCode); return statusCode; } // used when copying from local mail folder, or other imap server) nsresult nsImapMailFolder::CopyMessagesWithStream(nsIMsgFolder* srcFolder, nsISupportsArray* messages, PRBool isMove, PRBool isCrossServerOp, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener* listener, PRBool allowUndo) { nsresult rv = NS_ERROR_NULL_POINTER; if (!srcFolder || !messages) return rv; nsCOMPtr aSupport(do_QueryInterface(srcFolder, &rv)); if (NS_FAILED(rv)) return rv; rv = InitCopyState(aSupport, messages, isMove, PR_FALSE, isCrossServerOp, /* new message flags, not used */0, listener, msgWindow, allowUndo); if(NS_FAILED(rv)) return rv; m_copyState->m_streamCopy = PR_TRUE; // ** jt - needs to create server to server move/copy undo msg txn if (m_copyState->m_allowUndo) { nsCAutoString messageIds; nsMsgKeyArray srcKeyArray; nsCOMPtr urlListener; rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray); nsImapMoveCopyMsgTxn* undoMsgTxn = new nsImapMoveCopyMsgTxn; if (!undoMsgTxn || NS_FAILED(undoMsgTxn->Init(srcFolder, &srcKeyArray, messageIds.get(), this, PR_TRUE, isMove, m_eventQueue, urlListener))) { delete undoMsgTxn; return NS_ERROR_OUT_OF_MEMORY; } if (isMove) { if (mFlags & MSG_FOLDER_FLAG_TRASH) undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg); else undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg); } else { undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg); } rv = undoMsgTxn->QueryInterface( NS_GET_IID(nsImapMoveCopyMsgTxn), getter_AddRefs(m_copyState->m_undoMsgTxn) ); } nsCOMPtr aMessage; aMessage = do_QueryElementAt(messages, 0, &rv); if (NS_SUCCEEDED(rv)) CopyStreamMessage(aMessage, this, msgWindow, isMove); return rv; //we are clearing copy state in CopyMessages on failure } nsresult nsImapMailFolder::GetClearedOriginalOp(nsIMsgOfflineImapOperation *op, nsIMsgOfflineImapOperation **originalOp, nsIMsgDatabase **originalDB) { nsIMsgOfflineImapOperation *returnOp = nsnull; nsOfflineImapOperationType opType; op->GetOperation(&opType); NS_ASSERTION(opType & nsIMsgOfflineImapOperation::kMoveResult, "not an offline move op"); nsXPIDLCString sourceFolderURI; op->GetSourceFolderURI(getter_Copies(sourceFolderURI)); nsCOMPtr res; nsresult rv; nsCOMPtr rdf(do_GetService(kRDFServiceCID, &rv)); if (NS_FAILED(rv)) return rv; rv = rdf->GetResource(sourceFolderURI, getter_AddRefs(res)); if (NS_SUCCEEDED(rv)) { nsCOMPtr sourceFolder(do_QueryInterface(res, &rv)); if (NS_SUCCEEDED(rv) && sourceFolder) { if (sourceFolder) { nsCOMPtr folderInfo; sourceFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), originalDB); if (*originalDB) { nsMsgKey originalKey; op->GetMessageKey(&originalKey); rv = (*originalDB)->GetOfflineOpForKey(originalKey, PR_FALSE, &returnOp); if (NS_SUCCEEDED(rv) && returnOp) { nsXPIDLCString moveDestination; nsXPIDLCString thisFolderURI; GetURI(getter_Copies(thisFolderURI)); returnOp->GetDestinationFolderURI(getter_Copies(moveDestination)); if (!nsCRT::strcmp(moveDestination, thisFolderURI)) returnOp->ClearOperation(nsIMsgOfflineImapOperation::kMoveResult); } } } } } NS_IF_ADDREF(returnOp); *originalOp = returnOp; return rv; } nsresult nsImapMailFolder::GetOriginalOp(nsIMsgOfflineImapOperation *op, nsIMsgOfflineImapOperation **originalOp, nsIMsgDatabase **originalDB) { nsIMsgOfflineImapOperation *returnOp = nsnull; nsXPIDLCString sourceFolderURI; op->GetSourceFolderURI(getter_Copies(sourceFolderURI)); nsCOMPtr res; nsresult rv; nsCOMPtr rdf(do_GetService(kRDFServiceCID, &rv)); if (NS_FAILED(rv)) return rv; rv = rdf->GetResource(sourceFolderURI, getter_AddRefs(res)); if (NS_SUCCEEDED(rv)) { nsCOMPtr sourceFolder(do_QueryInterface(res, &rv)); if (NS_SUCCEEDED(rv) && sourceFolder) { if (sourceFolder) { nsCOMPtr folderInfo; sourceFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), originalDB); if (*originalDB) { nsMsgKey originalKey; op->GetMessageKey(&originalKey); rv = (*originalDB)->GetOfflineOpForKey(originalKey, PR_FALSE, &returnOp); } } } } NS_IF_ADDREF(returnOp); *originalOp = returnOp; return rv; } nsresult nsImapMailFolder::CopyOfflineMsgBody(nsIMsgFolder *srcFolder, nsIMsgDBHdr *destHdr, nsIMsgDBHdr *origHdr) { nsCOMPtr outputStream; nsresult rv = GetOfflineStoreOutputStream(getter_AddRefs(outputStream)); nsCOMPtr seekable; seekable = do_QueryInterface(outputStream); if (seekable) { nsMsgKey messageOffset; PRUint32 messageSize; origHdr->GetMessageOffset(&messageOffset); origHdr->GetOfflineMessageSize(&messageSize); if (!messageSize) { nsCOMPtr localFolder = do_QueryInterface(srcFolder); if (localFolder) //can just use regular message size origHdr->GetMessageSize(&messageSize); } PRInt64 tellPos; seekable->Tell(&tellPos); nsInt64 curStorePos = tellPos; destHdr->SetMessageOffset((PRUint32) curStorePos); nsCOMPtr offlineStoreInputStream; rv = srcFolder->GetOfflineStoreInputStream(getter_AddRefs(offlineStoreInputStream)); if (NS_SUCCEEDED(rv) && offlineStoreInputStream) { nsCOMPtr seekStream = do_QueryInterface(offlineStoreInputStream); NS_ASSERTION(seekStream, "non seekable stream - can't read from offline msg"); if (seekStream) { rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, messageOffset); if (NS_SUCCEEDED(rv)) { // now, copy the dest folder offline store msg to the temp file PRInt32 inputBufferSize = 10240; char *inputBuffer = (char *) PR_Malloc(inputBufferSize); PRInt32 bytesLeft; PRUint32 bytesRead, bytesWritten; bytesLeft = messageSize; rv = (inputBuffer) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; while (bytesLeft > 0 && NS_SUCCEEDED(rv)) { rv = offlineStoreInputStream->Read(inputBuffer, inputBufferSize, &bytesRead); if (NS_SUCCEEDED(rv) && bytesRead > 0) { rv = outputStream->Write(inputBuffer, PR_MIN((PRInt32) bytesRead, bytesLeft), &bytesWritten); NS_ASSERTION((PRInt32) bytesWritten == PR_MIN((PRInt32) bytesRead, bytesLeft), "wrote out incorrect number of bytes"); } else break; bytesLeft -= bytesRead; } PR_FREEIF(inputBuffer); outputStream->Flush(); } } } if (NS_SUCCEEDED(rv)) { PRUint32 resultFlags; destHdr->OrFlags(MSG_FLAG_OFFLINE, &resultFlags); destHdr->SetOfflineMessageSize(messageSize); } } return rv; } // this imap folder is the destination of an offline move/copy. // We are either offline, or doing a pseudo-offline delete (where we do an offline // delete, load the next message, then playback the offline delete). nsresult nsImapMailFolder::CopyMessagesOffline(nsIMsgFolder* srcFolder, nsISupportsArray* messages, PRBool isMove, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener* listener) { NS_ENSURE_ARG(messages); nsresult rv = NS_OK; nsresult stopit = 0; nsCOMPtr sourceMailDB; nsCOMPtr srcDbFolderInfo; srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(srcDbFolderInfo), getter_AddRefs(sourceMailDB)); PRBool deleteToTrash = PR_FALSE; PRBool deleteImmediately = PR_FALSE; PRUint32 srcCount; messages->Count(&srcCount); nsCOMPtr imapServer; rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_SUCCEEDED(rv) && imapServer) { nsMsgImapDeleteModel deleteModel; imapServer->GetDeleteModel(&deleteModel); deleteToTrash = (deleteModel == nsMsgImapDeleteModels::MoveToTrash); deleteImmediately = (deleteModel == nsMsgImapDeleteModels::DeleteNoTrash); } if (sourceMailDB) { // save the future ops in the source DB, if this is not a imap->local copy/move nsCOMPtr txnMgr; if (msgWindow) msgWindow->GetTransactionManager(getter_AddRefs(txnMgr)); if (txnMgr) txnMgr->BeginBatch(); GetDatabase(nsnull); if (mDatabase) { // get the highest key in the dest db, so we can make up our fake keys PRBool highWaterDeleted = PR_FALSE; nsMsgKey fakeBase = 1; nsCOMPtr folderInfo; rv = mDatabase->GetDBFolderInfo(getter_AddRefs(folderInfo)); NS_ENSURE_SUCCESS(rv, rv); nsMsgKey highWaterMark = nsMsgKey_None; folderInfo->GetHighWater(&highWaterMark); fakeBase += highWaterMark; // put fake message in destination db, delete source if move for (PRUint32 sourceKeyIndex=0; !stopit && (sourceKeyIndex < srcCount); sourceKeyIndex++) { PRBool messageReturningHome = PR_FALSE; nsXPIDLCString sourceFolderURI; srcFolder->GetURI(getter_Copies(sourceFolderURI)); nsXPIDLCString originalSrcFolderURI; if (sourceFolderURI.get()) originalSrcFolderURI.Adopt(nsCRT::strdup(sourceFolderURI.get())); nsCOMPtr message; message = do_QueryElementAt(messages, sourceKeyIndex); nsMsgKey originalKey; if (message) { rv = message->GetMessageKey(&originalKey); } else { NS_ASSERTION(PR_FALSE, "bad msg in src array"); continue; } nsCOMPtr sourceOp; rv = sourceMailDB->GetOfflineOpForKey(originalKey, PR_TRUE, getter_AddRefs(sourceOp)); if (NS_SUCCEEDED(rv) && sourceOp) { srcFolder->SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS); nsCOMPtr originalDB; nsOfflineImapOperationType opType; sourceOp->GetOperation(&opType); // if we already have an offline op for this key, then we need to see if it was // moved into the source folder while offline if (opType == nsIMsgOfflineImapOperation::kMoveResult) // offline move { // gracious me, we are moving something we already moved while offline! // find the original operation and clear it! nsCOMPtr originalOp; rv = GetClearedOriginalOp(sourceOp, getter_AddRefs(originalOp), getter_AddRefs(originalDB)); if (originalOp) { nsXPIDLCString originalString; nsXPIDLCString srcFolderURI; srcFolder->GetURI(getter_Copies(srcFolderURI)); sourceOp->GetSourceFolderURI(getter_Copies(originalString)); sourceOp->GetMessageKey(&originalKey); originalSrcFolderURI.Adopt(originalString.get() ? nsCRT::strdup(originalString.get()) : 0); if (isMove) sourceMailDB->RemoveOfflineOp(sourceOp); sourceOp = originalOp; if (!nsCRT::strcmp(originalSrcFolderURI, srcFolderURI)) { messageReturningHome = PR_TRUE; originalDB->RemoveOfflineOp(originalOp); } } } if (!messageReturningHome) { nsXPIDLCString folderURI; GetURI(getter_Copies(folderURI)); if (isMove) { PRUint32 msgSize; PRUint32 msgFlags; imapMessageFlagsType newImapFlags = 0; message->GetMessageSize(&msgSize); message->GetFlags(&msgFlags); sourceOp->SetDestinationFolderURI(folderURI); // offline move sourceOp->SetOperation(nsIMsgOfflineImapOperation::kMsgMoved); sourceOp->SetMsgSize(msgSize); newImapFlags = msgFlags & 0x7; if (msgFlags & MSG_FLAG_FORWARDED) newImapFlags |= kImapMsgForwardedFlag; sourceOp->SetNewFlags(newImapFlags); } else sourceOp->AddMessageCopyOperation(folderURI); // offline copy nsMsgKeyArray srcKeyArray; nsCOMPtr urlListener; sourceOp->GetOperation(&opType); srcKeyArray.Add(originalKey); rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); nsImapOfflineTxn *undoMsgTxn = new nsImapOfflineTxn(srcFolder, &srcKeyArray, this, isMove, opType, message, m_eventQueue, urlListener); if (undoMsgTxn) { if (isMove) undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg); else undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg); // we're adding this undo action before the delete is successful. This is evil, // but 4.5 did it as well. if (txnMgr) txnMgr->DoTransaction(undoMsgTxn); } } PRBool hasMsgOffline = PR_FALSE; srcFolder->HasMsgOffline(originalKey, &hasMsgOffline); // if (hasMsgOffline) // CopyOfflineMsgBody(srcFolder, originalKey); } else stopit = NS_ERROR_FAILURE; nsCOMPtr mailHdr; rv = sourceMailDB->GetMsgHdrForKey(originalKey, getter_AddRefs(mailHdr)); if (NS_SUCCEEDED(rv) && mailHdr) { PRBool successfulCopy = PR_FALSE; nsMsgKey srcDBhighWaterMark; srcDbFolderInfo->GetHighWater(&srcDBhighWaterMark); highWaterDeleted = !highWaterDeleted && isMove && deleteToTrash && (originalKey == srcDBhighWaterMark); nsCOMPtr newMailHdr; rv = mDatabase->CopyHdrFromExistingHdr(fakeBase + sourceKeyIndex, mailHdr, PR_TRUE, getter_AddRefs(newMailHdr)); if (!newMailHdr || NS_FAILED(rv)) { NS_ASSERTION(PR_FALSE, "failed to copy hdr"); stopit = rv; } if (NS_SUCCEEDED(stopit)) { PRBool hasMsgOffline = PR_FALSE; srcFolder->HasMsgOffline(originalKey, &hasMsgOffline); if (hasMsgOffline) CopyOfflineMsgBody(srcFolder, newMailHdr, mailHdr); nsCOMPtr destOp; mDatabase->GetOfflineOpForKey(fakeBase + sourceKeyIndex, PR_TRUE, getter_AddRefs(destOp)); if (destOp) { // check if this is a move back to the original mailbox, in which case // we just delete the offline operation. if (messageReturningHome) { mDatabase->RemoveOfflineOp(destOp); } else { SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS); destOp->SetSourceFolderURI(originalSrcFolderURI); destOp->SetMessageKey(originalKey); { nsCOMPtr urlListener; QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); nsMsgKeyArray keyArray; keyArray.Add(fakeBase + sourceKeyIndex); nsImapOfflineTxn *undoMsgTxn = new nsImapOfflineTxn(this, &keyArray, this, isMove, nsIMsgOfflineImapOperation::kAddedHeader, newMailHdr, m_eventQueue, urlListener); if (undoMsgTxn) { if (txnMgr) txnMgr->DoTransaction(undoMsgTxn); } } } } else stopit = NS_ERROR_FAILURE; } successfulCopy = NS_SUCCEEDED(stopit); nsMsgKey msgKey; mailHdr->GetMessageKey(&msgKey); if (isMove && successfulCopy) { nsMsgKeyArray srcKeyArray; nsCOMPtr urlListener; srcKeyArray.Add(msgKey); rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); nsOfflineImapOperationType opType = nsIMsgOfflineImapOperation::kDeletedMsg; if (!deleteToTrash) opType = nsIMsgOfflineImapOperation::kMsgMarkedDeleted; srcKeyArray.Add(msgKey); nsImapOfflineTxn *undoMsgTxn = new nsImapOfflineTxn(srcFolder, &srcKeyArray, this, isMove, opType, mailHdr, m_eventQueue, urlListener); if (undoMsgTxn) { if (isMove) { if (mFlags & MSG_FOLDER_FLAG_TRASH) undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg); else undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg); } else { undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg); } if (txnMgr) txnMgr->DoTransaction(undoMsgTxn); } if (deleteToTrash || deleteImmediately) sourceMailDB->DeleteMessage(msgKey, nsnull, PR_FALSE); else sourceMailDB->MarkImapDeleted(msgKey,PR_TRUE,nsnull); // offline delete } if (!successfulCopy) highWaterDeleted = PR_FALSE; } } if (isMove) sourceMailDB->Commit(nsMsgDBCommitType::kLargeCommit); mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); SummaryChanged(); srcFolder->SummaryChanged(); } if (txnMgr) txnMgr->EndBatch(); } nsCOMPtr srcSupport = do_QueryInterface(srcFolder); OnCopyCompleted(srcSupport, rv); if (NS_SUCCEEDED(rv) && isMove) srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom); return rv; } NS_IMETHODIMP nsImapMailFolder::CopyMessages(nsIMsgFolder* srcFolder, nsISupportsArray* messages, PRBool isMove, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener* listener, PRBool isFolder, //isFolder for future use when we do cross-server folder move/copy PRBool allowUndo) { nsresult rv = NS_OK; nsCAutoString messageIds; nsMsgKeyArray srcKeyArray; nsCOMPtr urlListener; nsCOMPtr srcSupport; nsCOMPtr copySupport; if (!(mFlags & (MSG_FOLDER_FLAG_TRASH|MSG_FOLDER_FLAG_JUNK))) SetMRUTime(); if (WeAreOffline()) return CopyMessagesOffline(srcFolder, messages, isMove, msgWindow, listener); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); srcSupport = do_QueryInterface(srcFolder); nsCOMPtr srcServer; nsCOMPtr dstServer; rv = srcFolder->GetServer(getter_AddRefs(srcServer)); if(NS_FAILED(rv)) goto done; rv = GetServer(getter_AddRefs(dstServer)); if(NS_FAILED(rv)) goto done; NS_ENSURE_TRUE(dstServer, NS_ERROR_NULL_POINTER); PRBool sameServer; rv = dstServer->Equals(srcServer, &sameServer); if (NS_FAILED(rv)) goto done; PRUint32 supportedUserFlags; GetSupportedUserFlags(&supportedUserFlags); if (! (supportedUserFlags & kImapMsgSupportUserFlag)) { PRUint32 count = 0; PRUint32 i; rv = messages->Count(&count); if (NS_FAILED(rv)) return rv; // make sure database is open to set special flags below if (!mDatabase) GetDatabase(nsnull); // check if any msg hdr has special flags or properties set // that we need to set on the dest hdr for (i = 0; i < count; i++) { nsCOMPtr msgDBHdr = do_QueryElementAt(messages, i, &rv); if (mDatabase && msgDBHdr) { nsMsgLabelValue label; nsXPIDLCString junkScore, junkScoreOrigin; nsMsgPriorityValue priority; msgDBHdr->GetStringProperty("junkscore", getter_Copies(junkScore)); msgDBHdr->GetStringProperty("junkscoreorigin", getter_Copies(junkScoreOrigin)); if (!junkScore.IsEmpty()) // ignore already scored messages. mDatabase->SetAttributesOnPendingHdr(msgDBHdr, "junkscore", junkScore.get(), 0); if (!junkScoreOrigin.IsEmpty()) mDatabase->SetAttributesOnPendingHdr(msgDBHdr, "junkscoreorigin", junkScoreOrigin.get(), 0); msgDBHdr->GetLabel(&label); if (label != 0) { nsCAutoString labelStr; labelStr.AppendInt(label); mDatabase->SetAttributesOnPendingHdr(msgDBHdr, "label", labelStr.get(), 0); } msgDBHdr->GetPriority(&priority); if(priority != 0) { nsCAutoString priorityStr; priorityStr.AppendInt(priority); mDatabase->SetAttributesOnPendingHdr(msgDBHdr, "priority", priorityStr.get(), 0); } nsXPIDLCString keywords; msgDBHdr->GetStringProperty("keywords", getter_Copies(keywords)); if (!keywords.IsEmpty()) mDatabase->SetAttributesOnPendingHdr(msgDBHdr, "keywords", keywords.get(), 0); } } } // if the folders aren't on the same server, do a stream base copy if (!sameServer) { rv = CopyMessagesWithStream(srcFolder, messages, isMove, PR_TRUE, msgWindow, listener, allowUndo); goto done; } rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray); if(NS_FAILED(rv)) goto done; rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); rv = InitCopyState(srcSupport, messages, isMove, PR_TRUE, PR_FALSE, /* newMsgFlags, not used */0, listener, msgWindow, allowUndo); if (NS_FAILED(rv)) goto done; m_copyState->m_curIndex = m_copyState->m_totalCount; if (isMove) srcFolder->EnableNotifications(allMessageCountNotifications, PR_FALSE, PR_TRUE/* dbBatching*/); //disable message count notification copySupport = do_QueryInterface(m_copyState); if (imapService) rv = imapService->OnlineMessageCopy(m_eventQueue, srcFolder, messageIds.get(), this, PR_TRUE, isMove, urlListener, nsnull, copySupport, msgWindow); if (m_copyState->m_allowUndo) if (NS_SUCCEEDED(rv)) { nsImapMoveCopyMsgTxn* undoMsgTxn = new nsImapMoveCopyMsgTxn; if (!undoMsgTxn || NS_FAILED(undoMsgTxn->Init(srcFolder, &srcKeyArray, messageIds.get(), this, PR_TRUE, isMove, m_eventQueue, urlListener))) { delete undoMsgTxn; return NS_ERROR_OUT_OF_MEMORY; } if (isMove) { if (mFlags & MSG_FOLDER_FLAG_TRASH) undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg); else undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg); } else { undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg); } rv = undoMsgTxn->QueryInterface( NS_GET_IID(nsImapMoveCopyMsgTxn), getter_AddRefs(m_copyState->m_undoMsgTxn) ); } else NS_ASSERTION(PR_FALSE, "online copy failed"); done: if (NS_FAILED(rv)) { (void) OnCopyCompleted(srcSupport, PR_FALSE); if (isMove) { srcFolder->EnableNotifications(allMessageCountNotifications, PR_TRUE, PR_TRUE/* dbBatching*/); //enable message count notification NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom); } } return rv; } class nsImapFolderCopyState : public nsIUrlListener, public nsIMsgCopyServiceListener { public: nsImapFolderCopyState(nsIMsgFolder *destParent, nsIMsgFolder *srcFolder, PRBool isMoveFolder, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener *listener); ~nsImapFolderCopyState(); NS_DECL_ISUPPORTS NS_DECL_NSIURLLISTENER NS_DECL_NSIMSGCOPYSERVICELISTENER nsresult StartNextCopy(); nsresult AdvanceToNextFolder(nsresult aStatus); protected: nsCOMPtr m_destParent; nsCOMPtr m_srcFolder; PRBool m_isMoveFolder; nsCOMPtr m_copySrvcListener; nsCOMPtr m_msgWindow; PRInt32 m_childIndex; nsCOMPtr m_srcChildFolders; nsCOMPtr m_destParents; }; NS_IMPL_ISUPPORTS2(nsImapFolderCopyState, nsIUrlListener, nsIMsgCopyServiceListener) nsImapFolderCopyState::nsImapFolderCopyState(nsIMsgFolder *destParent, nsIMsgFolder *srcFolder, PRBool isMoveFolder, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener *listener) { m_destParent = destParent; m_srcFolder = srcFolder; m_isMoveFolder = isMoveFolder; m_msgWindow = msgWindow; m_copySrvcListener = listener; m_childIndex = -1; m_srcChildFolders = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID); m_destParents = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID); } nsImapFolderCopyState::~nsImapFolderCopyState() { } nsresult nsImapFolderCopyState::StartNextCopy() { nsresult rv; // first make sure dest folder exists. nsCOMPtr imapService = do_GetService (NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsXPIDLString folderName; m_srcFolder->GetName(getter_Copies(folderName)); nsCOMPtr pEventQService = do_GetService(kEventQueueServiceCID, &rv); nsCOMPtr eventQueue; if (NS_SUCCEEDED(rv) && pEventQService) pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQueue)); rv = imapService->EnsureFolderExists(eventQueue, m_destParent, folderName.get(), this, nsnull); } return rv; } nsresult nsImapFolderCopyState::AdvanceToNextFolder(nsresult aStatus) { nsresult rv = NS_OK; m_childIndex++; PRUint32 childCount = 0; if (m_srcChildFolders) m_srcChildFolders->Count(&childCount); if (m_childIndex >= childCount) { if (m_copySrvcListener) rv = m_copySrvcListener->OnStopCopy(aStatus); Release(); } else { m_destParent = do_QueryElementAt(m_destParents, m_childIndex, &rv); m_srcFolder = do_QueryElementAt(m_srcChildFolders, m_childIndex, &rv); rv = StartNextCopy(); } return rv; } NS_IMETHODIMP nsImapFolderCopyState::OnStartRunningUrl(nsIURI *aUrl) { NS_PRECONDITION(aUrl, "sanity check - need to be be running non-null url"); return NS_OK; } NS_IMETHODIMP nsImapFolderCopyState::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode) { if (NS_FAILED(aExitCode)) { if (m_copySrvcListener) m_copySrvcListener->OnStopCopy(aExitCode); Release(); return aExitCode; // or NS_OK??? } nsresult rv = NS_OK; if (aUrl) { nsCOMPtr imapUrl = do_QueryInterface(aUrl); if (imapUrl) { nsImapAction imapAction = nsIImapUrl::nsImapTest; imapUrl->GetImapAction(&imapAction); switch(imapAction) { case nsIImapUrl::nsImapEnsureExistsFolder: { nsCOMPtr newMsgFolder; nsXPIDLString folderName; nsXPIDLCString utf7LeafName; m_srcFolder->GetName(getter_Copies(folderName)); rv = CopyUTF16toMUTF7(folderName, utf7LeafName); rv = m_destParent->FindSubFolder(utf7LeafName, getter_AddRefs(newMsgFolder)); NS_ENSURE_SUCCESS(rv,rv); // check if the source folder has children. If it does, list them // into m_srcChildFolders, and set m_destParents for the // corresponding indexes to the newly created folder. PRUint32 childCount; m_srcFolder->Count(&childCount); for (PRUint32 childIndex = 0; childIndex < childCount; childIndex++) { nsCOMPtr child = do_QueryElementAt(m_srcFolder, childIndex, &rv); if (NS_SUCCEEDED(rv)) { m_srcChildFolders->InsertElementAt(child, m_childIndex + childIndex + 1); m_destParents->InsertElementAt(newMsgFolder, m_childIndex + childIndex + 1); } } nsCOMPtr messages; rv = m_srcFolder->GetMessages(m_msgWindow, getter_AddRefs(messages)); nsCOMPtr msgSupportsArray; NS_NewISupportsArray(getter_AddRefs(msgSupportsArray)); PRBool hasMoreElements; nsCOMPtr aSupport; if (messages) messages->HasMoreElements(&hasMoreElements); if (!hasMoreElements) return AdvanceToNextFolder(NS_OK); while (hasMoreElements && NS_SUCCEEDED(rv)) { rv = messages->GetNext(getter_AddRefs(aSupport)); rv = msgSupportsArray->AppendElement(aSupport); messages->HasMoreElements(&hasMoreElements); } nsCOMPtr copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) rv = copyService->CopyMessages(m_srcFolder, msgSupportsArray, newMsgFolder, m_isMoveFolder, this, m_msgWindow, PR_FALSE /* allowUndo */); } break; } } } return rv; } NS_IMETHODIMP nsImapFolderCopyState::OnStartCopy() { return NS_OK; } /* void OnProgress (in PRUint32 aProgress, in PRUint32 aProgressMax); */ NS_IMETHODIMP nsImapFolderCopyState::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax) { return NS_ERROR_NOT_IMPLEMENTED; } /* void SetMessageKey (in PRUint32 aKey); */ NS_IMETHODIMP nsImapFolderCopyState::SetMessageKey(PRUint32 aKey) { return NS_ERROR_NOT_IMPLEMENTED; } /* [noscript] void GetMessageId (in nsCString aMessageId); */ NS_IMETHODIMP nsImapFolderCopyState::GetMessageId(nsCString * aMessageId) { return NS_ERROR_NOT_IMPLEMENTED; } /* void OnStopCopy (in nsresult aStatus); */ NS_IMETHODIMP nsImapFolderCopyState::OnStopCopy(nsresult aStatus) { if (NS_SUCCEEDED(aStatus)) return AdvanceToNextFolder(aStatus); if (m_copySrvcListener) (void) m_copySrvcListener->OnStopCopy(aStatus); delete this; return NS_OK; } // "this" is the parent of the copied folder. NS_IMETHODIMP nsImapMailFolder::CopyFolder(nsIMsgFolder* srcFolder, PRBool isMoveFolder, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener* listener) { NS_ENSURE_ARG_POINTER(srcFolder); nsresult rv = NS_OK; if (isMoveFolder) //move folder permitted when dstFolder and the srcFolder are on same server { PRUint32 folderFlags = 0; if (srcFolder) srcFolder->GetFlags(&folderFlags); // if our source folder is a virtual folder if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL) { nsCOMPtr newMsgFolder; nsXPIDLString folderName; srcFolder->GetName(getter_Copies(folderName)); nsCAutoString tempSafeFolderName; tempSafeFolderName.AssignWithConversion(folderName.get()); NS_MsgHashIfNecessary(tempSafeFolderName); nsAutoString safeFolderName; safeFolderName.AssignWithConversion(tempSafeFolderName); srcFolder->ForceDBClosed(); nsCOMPtr oldPathSpec; rv = srcFolder->GetPath(getter_AddRefs(oldPathSpec)); NS_ENSURE_SUCCESS(rv,rv); nsFileSpec oldPath; rv = oldPathSpec->GetFileSpec(&oldPath); NS_ENSURE_SUCCESS(rv,rv); nsLocalFolderSummarySpec summarySpec(oldPath); nsCOMPtr newPathSpec; rv = GetPath(getter_AddRefs(newPathSpec)); NS_ENSURE_SUCCESS(rv,rv); nsFileSpec newPath; rv = newPathSpec->GetFileSpec(&newPath); NS_ENSURE_SUCCESS(rv,rv); if (!newPath.IsDirectory()) { AddDirectorySeparator(newPath); newPath.CreateDirectory(); } rv = CheckIfFolderExists(folderName.get(), this, msgWindow); if(NS_FAILED(rv)) return rv; rv = summarySpec.CopyToDir(newPath); NS_ENSURE_SUCCESS(rv, rv); rv = AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder)); NS_ENSURE_SUCCESS(rv, rv); newMsgFolder->SetPrettyName(folderName.get()); PRUint32 flags; srcFolder->GetFlags(&flags); newMsgFolder->SetFlags(flags); NotifyItemAdded(newMsgFolder); // now remove the old folder nsCOMPtr msgParent; srcFolder->GetParentMsgFolder(getter_AddRefs(msgParent)); srcFolder->SetParent(nsnull); if (msgParent) { msgParent->PropagateDelete(srcFolder, PR_FALSE, msgWindow); // The files have already been moved, so delete storage PR_FALSE oldPath.Delete(PR_FALSE); //berkeley mailbox nsCOMPtr srcDB; // we need to force closed the source db srcFolder->Delete(); nsCOMPtr parentPathSpec; rv = msgParent->GetPath(getter_AddRefs(parentPathSpec)); NS_ENSURE_SUCCESS(rv,rv); nsFileSpec parentPath; rv = parentPathSpec->GetFileSpec(&parentPath); NS_ENSURE_SUCCESS(rv,rv); AddDirectorySeparator(parentPath); nsDirectoryIterator i(parentPath, PR_FALSE); // i.Exists() checks if the directory is empty or not if (parentPath.IsDirectory() && !i.Exists()) parentPath.Delete(PR_TRUE); } } else // non-virtual folder { nsCOMPtr imapService = do_GetService (NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr urlListener = do_QueryInterface(srcFolder); PRBool match = PR_FALSE; PRBool confirmed = PR_FALSE; if (mFlags & MSG_FOLDER_FLAG_TRASH) { rv = srcFolder->MatchOrChangeFilterDestination(nsnull, PR_FALSE, &match); if (match) { srcFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirmed); // should we return an error to copy service? // or send a notification? if (!confirmed) return NS_OK; } } rv = imapService->MoveFolder(m_eventQueue, srcFolder, this, urlListener, msgWindow, nsnull); } } } else // copying folder (should only be across server?) { nsImapFolderCopyState *folderCopier = new nsImapFolderCopyState( this, srcFolder, isMoveFolder, msgWindow, listener); NS_ADDREF(folderCopier); // it owns itself. return folderCopier->StartNextCopy(); } return rv; } NS_IMETHODIMP nsImapMailFolder::CopyFileMessage(nsIFileSpec* fileSpec, nsIMsgDBHdr* msgToReplace, PRBool isDraftOrTemplate, PRUint32 aNewMsgFlags, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener* listener) { nsresult rv = NS_ERROR_NULL_POINTER; nsMsgKey key = 0xffffffff; nsCAutoString messageId; nsCOMPtr urlListener; nsCOMPtr messages; nsCOMPtr srcSupport = do_QueryInterface(fileSpec, &rv); rv = NS_NewISupportsArray(getter_AddRefs(messages)); if (NS_FAILED(rv)) return OnCopyCompleted(srcSupport, rv); nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return OnCopyCompleted(srcSupport, rv); rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener)); if (msgToReplace) { rv = msgToReplace->GetMessageKey(&key); if (NS_SUCCEEDED(rv)) messageId.AppendInt((PRInt32) key); } rv = InitCopyState(srcSupport, messages, PR_FALSE, isDraftOrTemplate, PR_FALSE, aNewMsgFlags, listener, msgWindow, PR_FALSE); if (NS_FAILED(rv)) return OnCopyCompleted(srcSupport, rv); m_copyState->m_streamCopy = PR_TRUE; nsCOMPtr copySupport; if( m_copyState ) copySupport = do_QueryInterface(m_copyState); if (!isDraftOrTemplate) m_copyState->m_totalCount = 1; rv = imapService->AppendMessageFromFile(m_eventQueue, fileSpec, this, messageId.get(), PR_TRUE, isDraftOrTemplate, urlListener, nsnull, copySupport, msgWindow); if (NS_FAILED(rv)) return OnCopyCompleted(srcSupport, rv); return rv; } nsresult nsImapMailFolder::CopyStreamMessage(nsIMsgDBHdr* message, nsIMsgFolder* dstFolder, // should be this nsIMsgWindow *aMsgWindow, PRBool isMove) { nsresult rv = NS_ERROR_NULL_POINTER; if (!m_copyState) return rv; nsCOMPtr copyStreamListener = do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr copyListener(do_QueryInterface(dstFolder, &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr srcFolder(do_QueryInterface(m_copyState->m_srcSupport, &rv)); if (NS_FAILED(rv)) return rv; rv = copyStreamListener->Init(srcFolder, copyListener, nsnull); if (NS_FAILED(rv)) return rv; nsCOMPtr msgHdr(do_QueryInterface(message)); if (!msgHdr) return NS_ERROR_FAILURE; nsXPIDLCString uri; srcFolder->GetUriForMsg(msgHdr, getter_Copies(uri)); if (!m_copyState->m_msgService) rv = GetMessageServiceFromURI(uri, getter_AddRefs(m_copyState->m_msgService)); if (NS_SUCCEEDED(rv) && m_copyState->m_msgService) { nsCOMPtr streamListener(do_QueryInterface(copyStreamListener, &rv)); if(NS_FAILED(rv) || !streamListener) return NS_ERROR_NO_INTERFACE; // put up status message here, if copying more than one message. if (m_copyState->m_totalCount > 1) { nsXPIDLString dstFolderName, progressText; GetName(getter_Copies(dstFolderName)); nsAutoString curMsgString; nsAutoString totalMsgString; totalMsgString.AppendInt(m_copyState->m_totalCount); curMsgString.AppendInt(m_copyState->m_curIndex + 1); const PRUnichar *formatStrings[3] = {curMsgString.get(), totalMsgString.get(), dstFolderName.get() }; nsCOMPtr bundle; rv = IMAPGetStringBundle(getter_AddRefs(bundle)); NS_ENSURE_SUCCESS(rv, rv); rv = bundle->FormatStringFromID(IMAP_COPYING_MESSAGE_OF, formatStrings, 3, getter_Copies(progressText)); nsCOMPtr statusFeedback; if (m_copyState->m_msgWindow) m_copyState->m_msgWindow->GetStatusFeedback(getter_AddRefs(statusFeedback)); if (statusFeedback) { statusFeedback->ShowStatusString(progressText); PRInt32 percent; percent = (100 * m_copyState->m_curIndex) / (PRInt32) m_copyState->m_totalCount; { statusFeedback->ShowProgress(percent); } } } rv = m_copyState->m_msgService->CopyMessage(uri, streamListener, isMove && !m_copyState->m_isCrossServerOp, nsnull, aMsgWindow, nsnull); } return rv; } nsImapMailCopyState::nsImapMailCopyState() : m_isMove(PR_FALSE), m_selectedState(PR_FALSE), m_isCrossServerOp(PR_FALSE), m_curIndex(0), m_totalCount(0), m_streamCopy(PR_FALSE), m_dataBuffer(nsnull), m_dataBufferSize(0), m_leftOver(0), m_allowUndo(PR_FALSE), m_eatLF(PR_FALSE), m_newMsgFlags(0) { } nsImapMailCopyState::~nsImapMailCopyState() { PR_Free(m_dataBuffer); if (m_msgService && m_message) { nsCOMPtr srcFolder = do_QueryInterface(m_srcSupport); if (srcFolder) { nsXPIDLCString uri; srcFolder->GetUriForMsg(m_message, getter_Copies(uri)); } } if (m_tmpFileSpec) { PRBool isOpen = PR_FALSE; nsFileSpec fileSpec; if (isOpen) m_tmpFileSpec->CloseStream(); m_tmpFileSpec->GetFileSpec(&fileSpec); if (fileSpec.Valid()) fileSpec.Delete(PR_FALSE); } } NS_IMPL_THREADSAFE_ISUPPORTS1(nsImapMailCopyState, nsImapMailCopyState) nsresult nsImapMailFolder::InitCopyState(nsISupports* srcSupport, nsISupportsArray* messages, PRBool isMove, PRBool selectedState, PRBool acrossServers, PRUint32 newMsgFlags, nsIMsgCopyServiceListener* listener, nsIMsgWindow *msgWindow, PRBool allowUndo) { nsresult rv = NS_OK; if (!srcSupport || !messages) return NS_ERROR_NULL_POINTER; NS_ASSERTION(!m_copyState, "move/copy already in progress"); if (m_copyState) return NS_ERROR_FAILURE; nsImapMailCopyState* copyState = new nsImapMailCopyState(); m_copyState = do_QueryInterface(copyState); if (!m_copyState) return NS_ERROR_OUT_OF_MEMORY; m_copyState->m_isCrossServerOp = acrossServers; if (srcSupport) m_copyState->m_srcSupport = do_QueryInterface(srcSupport, &rv); if (NS_SUCCEEDED(rv)) { m_copyState->m_messages = do_QueryInterface(messages, &rv); rv = messages->Count(&m_copyState->m_totalCount); if (!m_copyState->m_isCrossServerOp) { if (NS_SUCCEEDED(rv)) { PRUint32 numUnread = 0; for (PRUint32 keyIndex=0; keyIndex < m_copyState->m_totalCount; keyIndex++) { nsCOMPtr message = do_QueryElementAt(m_copyState->m_messages, keyIndex, &rv); // if the key is not there, then assume what the caller tells us to. PRBool isRead = PR_FALSE; PRUint32 flags; if (message ) { message->GetFlags(&flags); isRead = flags & MSG_FLAG_READ; } if (!isRead) numUnread++; } m_copyState->m_unreadCount = numUnread; } } else { nsCOMPtr message = do_QueryElementAt(m_copyState->m_messages, m_copyState->m_curIndex, &rv); // if the key is not there, then assume what the caller tells us to. PRBool isRead = PR_FALSE; PRUint32 flags; if (message ) { message->GetFlags(&flags); isRead = flags & MSG_FLAG_READ; } m_copyState->m_unreadCount = (isRead) ? 0 : 1; } } m_copyState->m_isMove = isMove; m_copyState->m_newMsgFlags = newMsgFlags; m_copyState->m_allowUndo = allowUndo; m_copyState->m_selectedState = selectedState; m_copyState->m_msgWindow = msgWindow; if (listener) m_copyState->m_listener = do_QueryInterface(listener, &rv); return rv; } nsresult nsImapMailFolder::OnCopyCompleted(nsISupports *srcSupport, nsresult rv) { m_copyState = nsnull; nsresult result; nsCOMPtr copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &result); if (NS_SUCCEEDED(result)) copyService->NotifyCompletion(srcSupport, this, rv); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::MatchName(nsString *name, PRBool *matches) { if (!matches) return NS_ERROR_NULL_POINTER; PRBool isInbox = mName.LowerCaseEqualsLiteral("inbox"); if (isInbox) *matches = mName.Equals(*name, nsCaseInsensitiveStringComparator()); else *matches = mName.Equals(*name); return NS_OK; } nsresult nsImapMailFolder::CreateBaseMessageURI(const char *aURI) { return nsCreateImapBaseMessageURI(aURI, &mBaseMessageURI); } NS_IMETHODIMP nsImapMailFolder::GetFolderURL(char **aFolderURL) { NS_ENSURE_ARG_POINTER(aFolderURL); nsCOMPtr rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); NS_ENSURE_SUCCESS(rv, rv); nsXPIDLCString rootURI; rootFolder->GetURI(getter_Copies(rootURI)); NS_ASSERTION(mURI.Length() > rootURI.Length(), "Should match with a folder name!"); nsAdoptingCString escapedName(nsEscape(mURI.get() + rootURI.Length(), url_Path)); if (escapedName.IsEmpty()) return NS_ERROR_OUT_OF_MEMORY; *aFolderURL = ToNewCString(rootURI + escapedName); if (!*aFolderURL) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetFolderNeedsSubscribing(PRBool *bVal) { if (!bVal) return NS_ERROR_NULL_POINTER; *bVal = m_folderNeedsSubscribing; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetFolderNeedsSubscribing(PRBool bVal) { m_folderNeedsSubscribing = bVal; return NS_OK; } nsMsgIMAPFolderACL * nsImapMailFolder::GetFolderACL() { if (!m_folderACL) m_folderACL = new nsMsgIMAPFolderACL(this); return m_folderACL; } nsresult nsImapMailFolder::CreateACLRightsStringForFolder(PRUnichar **rightsString) { NS_ENSURE_ARG_POINTER(rightsString); GetFolderACL(); // lazy create if (m_folderACL) { return m_folderACL->CreateACLRightsString(rightsString); } return NS_ERROR_NULL_POINTER; } NS_IMETHODIMP nsImapMailFolder::GetFolderNeedsACLListed(PRBool *bVal) { NS_ENSURE_ARG_POINTER(bVal); PRBool dontNeedACLListed = !m_folderNeedsACLListed; // if we haven't acl listed, and it's not a no select folder or the inbox, // then we'll list the acl if it's not a namespace. if (m_folderNeedsACLListed && !(mFlags & (MSG_FOLDER_FLAG_IMAP_NOSELECT | MSG_FOLDER_FLAG_INBOX))) GetIsNamespace(&dontNeedACLListed); *bVal = !dontNeedACLListed; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetFolderNeedsACLListed(PRBool bVal) { m_folderNeedsACLListed = bVal; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetIsNamespace(PRBool *aResult) { NS_ENSURE_ARG_POINTER(aResult); nsresult rv = NS_OK; if (!m_namespace) { #ifdef DEBUG_bienvenu // Make sure this isn't causing us to open the database NS_ASSERTION(m_hierarchyDelimiter != kOnlineHierarchySeparatorUnknown, "hierarchy delimiter not set"); #endif nsXPIDLCString onlineName; nsXPIDLCString serverKey; GetServerKey(getter_Copies(serverKey)); GetOnlineName(getter_Copies(onlineName)); PRUnichar hierarchyDelimiter; GetHierarchyDelimiter(&hierarchyDelimiter); nsCOMPtr hostSession = do_GetService(kCImapHostSessionList, &rv); if (NS_SUCCEEDED(rv) && hostSession) { m_namespace = nsIMAPNamespaceList::GetNamespaceForFolder(serverKey.get(), onlineName.get(), (char) hierarchyDelimiter); if (m_namespace == nsnull) { if (mFlags & MSG_FOLDER_FLAG_IMAP_OTHER_USER) { rv = hostSession->GetDefaultNamespaceOfTypeForHost(serverKey.get(), kOtherUsersNamespace, m_namespace); } else if (mFlags & MSG_FOLDER_FLAG_IMAP_PUBLIC) { rv = hostSession->GetDefaultNamespaceOfTypeForHost(serverKey.get(), kPublicNamespace, m_namespace); } else { rv = hostSession->GetDefaultNamespaceOfTypeForHost(serverKey.get(), kPersonalNamespace, m_namespace); } } NS_ASSERTION(m_namespace, "failed to get namespace"); if (m_namespace) { nsIMAPNamespaceList::SuggestHierarchySeparatorForNamespace(m_namespace, (char) hierarchyDelimiter); m_folderIsNamespace = nsIMAPNamespaceList::GetFolderIsNamespace(serverKey.get(), onlineName.get(), (char) hierarchyDelimiter, m_namespace); } } } *aResult = m_folderIsNamespace; return rv; } NS_IMETHODIMP nsImapMailFolder::SetIsNamespace(PRBool isNamespace) { m_folderIsNamespace = isNamespace; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::ResetNamespaceReferences() { nsXPIDLCString serverKey; nsXPIDLCString onlineName; GetServerKey(getter_Copies(serverKey)); GetOnlineName(getter_Copies(onlineName)); PRUnichar hierarchyDelimiter; GetHierarchyDelimiter(&hierarchyDelimiter); m_namespace = nsIMAPNamespaceList::GetNamespaceForFolder(serverKey.get(), onlineName.get(), (char) hierarchyDelimiter); // NS_ASSERTION(m_namespace, "resetting null namespace"); if (m_namespace) m_folderIsNamespace = nsIMAPNamespaceList::GetFolderIsNamespace(serverKey.get(), onlineName.get(), (char) hierarchyDelimiter, m_namespace); else m_folderIsNamespace = PR_FALSE; nsCOMPtr aEnumerator; GetSubFolders(getter_AddRefs(aEnumerator)); if (!aEnumerator) return NS_OK; nsCOMPtr aSupport; nsresult rv = aEnumerator->First(); while (NS_SUCCEEDED(rv)) { rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport)); nsCOMPtr folder = do_QueryInterface(aSupport, &rv); if (NS_FAILED(rv)) return rv; folder->ResetNamespaceReferences(); rv = aEnumerator->Next(); } return rv; } NS_IMETHODIMP nsImapMailFolder::FindOnlineSubFolder(const char *targetOnlineName, nsIMsgImapMailFolder **aResultFolder) { nsresult rv = NS_OK; nsXPIDLCString onlineName; GetOnlineName(getter_Copies(onlineName)); if (onlineName.Equals(targetOnlineName)) { return QueryInterface(NS_GET_IID(nsIMsgImapMailFolder), (void **) aResultFolder); } nsCOMPtr aEnumerator; GetSubFolders(getter_AddRefs(aEnumerator)); if (!aEnumerator) return NS_OK; nsCOMPtr aSupport; rv = aEnumerator->First(); while (NS_SUCCEEDED(rv)) { rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport)); nsCOMPtr folder = do_QueryInterface(aSupport, &rv); if (NS_FAILED(rv)) return rv; rv = folder->FindOnlineSubFolder(targetOnlineName, aResultFolder); if (*aResultFolder) return rv; rv = aEnumerator->Next(); } return rv; } NS_IMETHODIMP nsImapMailFolder::GetFolderNeedsAdded(PRBool *bVal) { if (!bVal) return NS_ERROR_NULL_POINTER; *bVal = m_folderNeedsAdded; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetFolderNeedsAdded(PRBool bVal) { m_folderNeedsAdded = bVal; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetFolderQuotaCommandIssued(PRBool *aCmdIssued) { NS_ENSURE_ARG_POINTER(aCmdIssued); *aCmdIssued = m_folderQuotaCommandIssued; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaCommandIssued(PRBool aCmdIssued) { m_folderQuotaCommandIssued = aCmdIssued; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaData(const nsACString &aFolderQuotaRoot, PRUint32 aFolderQuotaUsedKB, PRUint32 aFolderQuotaMaxKB) { m_folderQuotaDataIsValid = PR_TRUE; m_folderQuotaRoot = aFolderQuotaRoot; m_folderQuotaUsedKB = aFolderQuotaUsedKB; m_folderQuotaMaxKB = aFolderQuotaMaxKB; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetQuota(PRBool* aValid, PRUint32* aUsed, PRUint32* aMax) { NS_ENSURE_ARG_POINTER(aValid); NS_ENSURE_ARG_POINTER(aUsed); NS_ENSURE_ARG_POINTER(aMax); *aValid = m_folderQuotaDataIsValid; *aUsed = m_folderQuotaUsedKB; *aMax = m_folderQuotaMaxKB; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::PerformExpand(nsIMsgWindow *aMsgWindow) { nsresult rv; PRBool usingSubscription = PR_FALSE; nsCOMPtr imapServer; rv = GetImapIncomingServer(getter_AddRefs(imapServer)); if (NS_FAILED(rv) || !imapServer) return NS_ERROR_FAILURE; rv = imapServer->GetUsingSubscription(&usingSubscription); if (NS_SUCCEEDED(rv) && !usingSubscription) { nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) rv = imapService->DiscoverChildren(m_eventQueue, this, this, m_onlineFolderName.get(), nsnull); } return rv; } NS_IMETHODIMP nsImapMailFolder::RenameClient(nsIMsgWindow *msgWindow, nsIMsgFolder *msgFolder, const char* oldName, const char* newName ) { nsresult rv = NS_OK; nsCOMPtr pathSpec; rv = GetPath(getter_AddRefs(pathSpec)); if (NS_FAILED(rv)) return rv; nsFileSpec path; rv = pathSpec->GetFileSpec(&path); if (NS_FAILED(rv)) return rv; nsCOMPtr oldImapFolder = do_QueryInterface(msgFolder, &rv); if (NS_FAILED(rv)) return rv; PRUnichar hierarchyDelimiter = '/'; oldImapFolder->GetHierarchyDelimiter(&hierarchyDelimiter); PRInt32 boxflags=0; oldImapFolder->GetBoxFlags(&boxflags); nsAutoString newLeafName; nsAutoString newNameString; newNameString.AssignWithConversion(newName); newLeafName = newNameString; nsAutoString parentName; nsAutoString folderNameStr; PRInt32 folderStart = newLeafName.RFindChar('/'); //internal use of hierarchyDelimiter is always '/' if (folderStart > 0) { newNameString.Right(newLeafName, newLeafName.Length() - folderStart - 1); CreateDirectoryForFolder(path); //needed when we move a folder to a folder with no subfolders. } // if we get here, it's really a leaf, and "this" is the parent. folderNameStr = newLeafName; // Create an empty database for this mail folder, set its name from the user nsCOMPtr mailDBFactory; nsCOMPtr child; nsCOMPtr imapFolder; nsCOMPtr msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID); if (msgDBService) { nsCOMPtr unusedDB; nsCOMPtr dbFileSpec; nsCAutoString proposedDBName; proposedDBName.AssignWithConversion(newLeafName); // warning, path will be changed rv = CreateFileSpecForDB(proposedDBName.get(), path, getter_AddRefs(dbFileSpec)); NS_ENSURE_SUCCESS(rv,rv); // it's OK to use openMailDBFromFileSpec and not OpenFolderDB here, since we don't use the DB. rv = msgDBService->OpenMailDBFromFileSpec(dbFileSpec, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(unusedDB)); if (NS_SUCCEEDED(rv) && unusedDB) { //need to set the folder name nsCOMPtr folderInfo; rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo)); //Now let's create the actual new folder rv = AddSubfolderWithPath(folderNameStr, dbFileSpec, getter_AddRefs(child)); if (!child || NS_FAILED(rv)) return rv; nsXPIDLString unicodeName; rv = CopyMUTF7toUTF16(proposedDBName, unicodeName); if (NS_SUCCEEDED(rv)) child->SetName(unicodeName); imapFolder = do_QueryInterface(child); if (imapFolder) { nsCAutoString onlineName(m_onlineFolderName); if (!onlineName.IsEmpty()) onlineName.Append(char(hierarchyDelimiter)); onlineName.AppendWithConversion(folderNameStr); imapFolder->SetVerifiedAsOnlineFolder(PR_TRUE); imapFolder->SetOnlineName(onlineName.get()); imapFolder->SetHierarchyDelimiter(hierarchyDelimiter); imapFolder->SetBoxFlags(boxflags); // store the online name as the mailbox name in the db folder info // I don't think anyone uses the mailbox name, so we'll use it // to restore the online name when blowing away an imap db. if (folderInfo) { nsAutoString unicodeOnlineName; unicodeOnlineName.AssignWithConversion(onlineName.get()); folderInfo->SetMailboxName(unicodeOnlineName); } PRBool changed = PR_FALSE; msgFolder->MatchOrChangeFilterDestination(child, PR_FALSE /*caseInsensitive*/, &changed); if (changed) msgFolder->AlertFilterChanged(msgWindow); } unusedDB->SetSummaryValid(PR_TRUE); unusedDB->Commit(nsMsgDBCommitType::kLargeCommit); unusedDB->Close(PR_TRUE); child->RenameSubFolders(msgWindow, msgFolder); nsCOMPtr msgParent; msgFolder->GetParentMsgFolder(getter_AddRefs(msgParent)); msgFolder->SetParent(nsnull); msgParent->PropagateDelete(msgFolder, PR_TRUE, nsnull); // Reset online status now that the folder is renamed. nsCOMPtr oldImapFolder = do_QueryInterface(msgFolder); if (oldImapFolder) oldImapFolder->SetVerifiedAsOnlineFolder(PR_FALSE); NotifyItemAdded(child); } } return rv; } NS_IMETHODIMP nsImapMailFolder::RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder) { nsresult rv = NS_OK; m_initialized = PR_TRUE; nsCOMPtr aEnumerator; oldFolder->GetSubFolders(getter_AddRefs(aEnumerator)); nsCOMPtr aSupport; rv = aEnumerator->First(); while (NS_SUCCEEDED(rv)) { rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport)); nsCOMPtrmsgFolder = do_QueryInterface(aSupport); nsCOMPtr folder = do_QueryInterface(msgFolder, &rv); if (NS_FAILED(rv)) return rv; PRUnichar hierarchyDelimiter = '/'; folder->GetHierarchyDelimiter(&hierarchyDelimiter); PRInt32 boxflags; folder->GetBoxFlags(&boxflags); PRBool verified; folder->GetVerifiedAsOnlineFolder(&verified); nsCOMPtr oldPathSpec; rv = msgFolder->GetPath(getter_AddRefs(oldPathSpec)); if (NS_FAILED(rv)) return rv; nsFileSpec oldPath; rv = oldPathSpec->GetFileSpec(&oldPath); if (NS_FAILED(rv)) return rv; nsCOMPtr newParentPathSpec; rv = GetPath(getter_AddRefs(newParentPathSpec)); if (NS_FAILED(rv)) return rv; nsFileSpec newParentPath; rv = newParentPathSpec->GetFileSpec(&newParentPath); if (NS_FAILED(rv)) return rv; rv = AddDirectorySeparator(newParentPath); newParentPath += oldPath.GetLeafName(); nsCString newPathStr(newParentPath.GetNativePathCString()); nsCOMPtr newPathSpec; rv = NS_NewFileSpec(getter_AddRefs(newPathSpec)); if (NS_FAILED(rv)) return rv; rv = newPathSpec->SetNativePath(newPathStr.get()); nsFileSpec newPath; rv = newPathSpec->GetFileSpec(&newPath); if (NS_FAILED(rv)) return rv; nsCOMPtr dbFileSpec; NS_NewFileSpecWithSpec(newPath, getter_AddRefs(dbFileSpec)); nsCOMPtr child; nsXPIDLString folderName; rv = msgFolder->GetName(getter_Copies(folderName)); if (folderName.IsEmpty() || NS_FAILED(rv)) return rv; nsXPIDLCString utf7LeafName; rv = CopyUTF16toMUTF7(folderName, utf7LeafName); NS_ENSURE_SUCCESS(rv, rv); // XXX : Fix this non-sense by fixing AddSubfolderWithPath nsAutoString unicodeLeafName; CopyASCIItoUTF16(utf7LeafName, unicodeLeafName); rv = AddSubfolderWithPath(unicodeLeafName, dbFileSpec, getter_AddRefs(child)); if (!child || NS_FAILED(rv)) return rv; child->SetName(folderName); nsCOMPtr imapFolder = do_QueryInterface(child); nsXPIDLCString onlineName; GetOnlineName(getter_Copies(onlineName)); nsCAutoString onlineCName(onlineName); onlineCName.Append(char(hierarchyDelimiter)); onlineCName.Append(utf7LeafName.get()); if (imapFolder) { imapFolder->SetVerifiedAsOnlineFolder(verified); imapFolder->SetOnlineName(onlineCName.get()); imapFolder->SetHierarchyDelimiter(hierarchyDelimiter); imapFolder->SetBoxFlags(boxflags); PRBool changed = PR_FALSE; msgFolder->MatchOrChangeFilterDestination(child, PR_FALSE /*caseInsensitive*/, &changed); if (changed) msgFolder->AlertFilterChanged(msgWindow); child->RenameSubFolders(msgWindow, msgFolder); } rv = aEnumerator->Next(); } return rv; } NS_IMETHODIMP nsImapMailFolder::IsCommandEnabled(const char *command, PRBool *result) { NS_ENSURE_ARG_POINTER(result); NS_ENSURE_ARG_POINTER(command); *result = PR_TRUE; if(WeAreOffline() && ((nsCRT::strcmp(command, "cmd_renameFolder") == 0) || (nsCRT::strcmp(command, "cmd_compactFolder") == 0) || (nsCRT::strcmp(command, "cmd_delete") == 0) || (nsCRT::strcmp(command, "button_delete") == 0))) *result = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetCanFileMessages(PRBool *aCanFileMessages) { nsresult rv; *aCanFileMessages = PR_TRUE; nsCOMPtr server; rv = GetServer(getter_AddRefs(server)); if (NS_SUCCEEDED(rv) && server) rv = server->GetCanFileMessagesOnServer(aCanFileMessages); if (*aCanFileMessages) rv = nsMsgDBFolder::GetCanFileMessages(aCanFileMessages); if (*aCanFileMessages) { PRBool noSelect; GetFlag(MSG_FOLDER_FLAG_IMAP_NOSELECT, &noSelect); *aCanFileMessages = (noSelect) ? PR_FALSE : GetFolderACL()->GetCanIInsertInFolder(); return NS_OK; } return rv; } NS_IMETHODIMP nsImapMailFolder::GetCanDeleteMessages(PRBool *aCanDeleteMessages) { NS_ENSURE_ARG_POINTER(aCanDeleteMessages); *aCanDeleteMessages = GetFolderACL()->GetCanIDeleteInFolder(); return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetPerformingBiff(PRBool *aPerformingBiff) { if (!aPerformingBiff) return NS_ERROR_NULL_POINTER; *aPerformingBiff = m_performingBiff; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetPerformingBiff(PRBool aPerformingBiff) { m_performingBiff = aPerformingBiff; return NS_OK; } NS_IMETHODIMP nsImapMailFolder::SetFilterList(nsIMsgFilterList *aMsgFilterList) { m_filterList = aMsgFilterList; return nsMsgDBFolder::SetFilterList(aMsgFilterList); } nsresult nsImapMailFolder::GetMoveCoalescer() { if (!m_moveCoalescer) { m_moveCoalescer = new nsImapMoveCoalescer(this, nsnull /* msgWindow */); NS_ENSURE_TRUE (m_moveCoalescer, NS_ERROR_OUT_OF_MEMORY); m_moveCoalescer->AddRef(); } return NS_OK; } nsresult nsImapMailFolder::SpamFilterClassifyMessage(const char *aURI, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin) { ++m_numFilterClassifyRequests; return aJunkMailPlugin->ClassifyMessage(aURI, aMsgWindow, this); } nsresult nsImapMailFolder::SpamFilterClassifyMessages(const char **aURIArray, PRUint32 aURICount, nsIMsgWindow *aMsgWindow, nsIJunkMailPlugin *aJunkMailPlugin) { m_numFilterClassifyRequests += aURICount; return aJunkMailPlugin->ClassifyMessages(aURICount, aURIArray, aMsgWindow, this); } NS_IMETHODIMP nsImapMailFolder::StoreCustomKeywords(nsIMsgWindow *aMsgWindow, const char *aFlagsToAdd, const char *aFlagsToSubtract, nsMsgKey *aKeysToStore, PRUint32 aNumKeys, nsIURI **_retval) { nsresult rv; if (WeAreOffline()) { GetDatabase(nsnull); if (mDatabase) { for (PRUint32 keyIndex = 0; keyIndex < aNumKeys; keyIndex++) { nsCOMPtr op; rv = mDatabase->GetOfflineOpForKey(aKeysToStore[keyIndex], PR_TRUE, getter_AddRefs(op)); SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS); if (NS_SUCCEEDED(rv) && op) { if (aFlagsToAdd) op->AddKeywordToAdd(aFlagsToAdd); if (aFlagsToSubtract) op->AddKeywordToRemove(aFlagsToSubtract); } } mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); // flush offline ops return rv; } } nsCOMPtr imapService(do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsCAutoString msgIds; AllocateUidStringFromKeys(aKeysToStore, aNumKeys, msgIds); return imapService->StoreCustomKeywords(m_eventQueue, this, aMsgWindow, aFlagsToAdd, aFlagsToSubtract, msgIds.get(), _retval); } NS_IMETHODIMP nsImapMailFolder::NotifyIfNewMail() { return PerformBiffNotifications(); } PRBool nsImapMailFolder::ShowPreviewText() { PRBool showPreviewText = PR_FALSE; nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefBranch) prefBranch->GetBoolPref("mail.biff.alert.show_preview", &showPreviewText); return showPreviewText; } nsresult nsImapMailFolder::PlaybackCoalescedOperations() { if (m_moveCoalescer) { nsMsgKeyArray *junkKeysToClassify = m_moveCoalescer->GetKeyBucket(0); nsMsgKeyArray *nonJunkKeysToClassify = m_moveCoalescer->GetKeyBucket(1); if (junkKeysToClassify && junkKeysToClassify->GetSize() > 0) StoreCustomKeywords(m_moveCoalescer->GetMsgWindow(), "Junk", "", junkKeysToClassify->GetArray(), junkKeysToClassify->GetSize(), nsnull); if (nonJunkKeysToClassify && nonJunkKeysToClassify->GetSize() > 0) StoreCustomKeywords(m_moveCoalescer->GetMsgWindow(), "NonJunk", "", nonJunkKeysToClassify->GetArray(), nonJunkKeysToClassify->GetSize(), nsnull); junkKeysToClassify->RemoveAll(); nonJunkKeysToClassify->RemoveAll(); return m_moveCoalescer->PlaybackMoves(ShowPreviewText()); } return NS_OK; // must not be any coalesced operations } NS_IMETHODIMP nsImapMailFolder::SetJunkScoreForMessages(nsISupportsArray *aMessages, const char *aJunkScore) { NS_ENSURE_ARG(aMessages); nsresult rv = nsMsgDBFolder::SetJunkScoreForMessages(aMessages, aJunkScore); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keys; nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keys); NS_ENSURE_SUCCESS(rv, rv); StoreCustomKeywords(nsnull, (*aJunkScore == '0') ? "NonJunk" : "Junk", "", keys.GetArray(), keys.GetSize(), nsnull); if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } NS_IMETHODIMP nsImapMailFolder::OnMessageClassified(const char *aMsgURI, nsMsgJunkStatus aClassification) { nsXPIDLCString spamFolderURI; nsCOMPtr server; nsresult rv = GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr msgHdr; rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr)); NS_ENSURE_SUCCESS(rv, rv); nsMsgKey msgKey; rv = msgHdr->GetMessageKey(&msgKey); NS_ENSURE_SUCCESS(rv, rv); mDatabase->SetStringProperty(msgKey, "junkscore", (aClassification == nsIJunkMailPlugin::JUNK) ? "100" : "0"); mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "plugin"); GetMoveCoalescer(); if (m_moveCoalescer) { nsMsgKeyArray *keysToClassify = m_moveCoalescer->GetKeyBucket((aClassification == nsIJunkMailPlugin::JUNK) ? 0 : 1); NS_ASSERTION(keysToClassify, "error getting key bucket"); if (keysToClassify) keysToClassify->Add(msgKey); } if (aClassification == nsIJunkMailPlugin::JUNK) { nsCOMPtr spamSettings; rv = GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); rv = server->GetSpamSettings(getter_AddRefs(spamSettings)); NS_ENSURE_SUCCESS(rv, rv); PRBool markAsReadOnSpam; (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam); if (markAsReadOnSpam) { if (!m_junkMessagesToMarkAsRead) NS_NewISupportsArray(getter_AddRefs(m_junkMessagesToMarkAsRead)); m_junkMessagesToMarkAsRead->AppendElement(msgHdr); } PRBool willMoveMessage = PR_FALSE; // don't do the move when we are opening up // the junk mail folder or the trash folder // or when manually classifying messages in those folders if (!(mFlags & MSG_FOLDER_FLAG_JUNK || mFlags & MSG_FOLDER_FLAG_TRASH)) { PRBool moveOnSpam; (void)spamSettings->GetMoveOnSpam(&moveOnSpam); if (moveOnSpam) { rv = spamSettings->GetSpamFolderURI(getter_Copies(spamFolderURI)); NS_ENSURE_SUCCESS(rv,rv); if (!spamFolderURI.IsEmpty()) { nsCOMPtr folder; rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder)); if (NS_SUCCEEDED(rv) && folder) { rv = folder->SetFlag(MSG_FOLDER_FLAG_JUNK); NS_ENSURE_SUCCESS(rv,rv); if (NS_SUCCEEDED(GetMoveCoalescer())) { m_moveCoalescer->AddMove(folder, msgKey); willMoveMessage = PR_TRUE; } } else { // XXX TODO // JUNK MAIL RELATED // the listener should do // rv = folder->SetFlag(MSG_FOLDER_FLAG_JUNK); // NS_ENSURE_SUCCESS(rv,rv); // if (NS_SUCCEEDED(GetMoveCoalescer())) { // m_moveCoalescer->AddMove(folder, msgKey); // willMoveMessage = PR_TRUE; // } rv = GetOrCreateFolder(spamFolderURI, nsnull /* aListener */); NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed"); } } } } rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage); NS_ENSURE_SUCCESS(rv,rv); } if (--m_numFilterClassifyRequests == 0) { if (m_junkMessagesToMarkAsRead) { PRUint32 count; m_junkMessagesToMarkAsRead->Count(&count); if (count > 0) { rv = MarkMessagesRead(m_junkMessagesToMarkAsRead, true); NS_ENSURE_SUCCESS(rv,rv); m_junkMessagesToMarkAsRead->SizeTo(0); } } PRBool pendingMoves = m_moveCoalescer && m_moveCoalescer->HasPendingMoves(); PlaybackCoalescedOperations(); // If we are performing biff for this folder, tell the server object if ((!pendingMoves || !ShowPreviewText()) && m_performingBiff) { // we don't need to adjust the num new messages in this folder because // the playback moves code already did that. (void) PerformBiffNotifications(); nsCOMPtr server; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) server->SetPerformingBiff(PR_FALSE); m_performingBiff = PR_FALSE; } } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::GetShouldDownloadAllHeaders(PRBool *aResult) { *aResult = PR_FALSE; //for just the inbox, we check if the filter list has arbitary headers. //for all folders, check if we have a spam plugin that requires all headers if (mFlags & MSG_FOLDER_FLAG_INBOX) { nsCOMPtr filterList; nsresult rv = GetFilterList(nsnull, getter_AddRefs(filterList)); NS_ENSURE_SUCCESS(rv,rv); rv = filterList->GetShouldDownloadAllHeaders(aResult); if (*aResult) return rv; } nsCOMPtr filterPlugin; nsCOMPtr server; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server)))) server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin)); return (filterPlugin) ? filterPlugin->GetShouldDownloadAllHeaders(aResult) : NS_OK; } void nsImapMailFolder::GetTrashFolderName(nsAString &aFolderName) { nsCOMPtr server; nsCOMPtr imapServer; if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) imapServer = do_QueryInterface(server); if (imapServer) { nsXPIDLString trashFolderName; if (NS_SUCCEEDED(imapServer->GetTrashFolderName(getter_Copies(trashFolderName)))) { aFolderName = trashFolderName; } } } NS_IMETHODIMP nsImapMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, PRUint32 aNumKeys, PRBool aLocalOnly, nsIUrlListener *aUrlListener, PRBool *aAsyncResults) { NS_ENSURE_ARG_POINTER(aKeysToFetch); NS_ENSURE_ARG_POINTER(aAsyncResults); nsMsgKeyArray keysToFetchFromServer; *aAsyncResults = PR_FALSE; nsresult rv = NS_OK; for (PRUint32 i = 0; i < aNumKeys; i++) { nsCOMPtr msgHdr; nsXPIDLCString prevBody; rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr)); NS_ENSURE_SUCCESS(rv, rv); // ignore messages that already have a preview body. msgHdr->GetStringProperty("preview", getter_Copies(prevBody)); if (!prevBody.IsEmpty()) continue; /* check if message is in memory cache or offline store. */ nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr msgService = do_QueryInterface(imapService); nsCOMPtr url; nsCOMPtr inputStream; nsXPIDLCString messageUri; rv = GetUriForMsg(msgHdr, getter_Copies(messageUri)); NS_ENSURE_SUCCESS(rv,rv); rv = msgService->GetUrlForUri(messageUri, getter_AddRefs(url), nsnull); NS_ENSURE_SUCCESS(rv,rv); nsCAutoString urlSpec; url->GetAsciiSpec(urlSpec); nsCOMPtr cacheSession; rv = imapService->GetCacheSession(getter_AddRefs(cacheSession)); NS_ENSURE_SUCCESS(rv, rv); PRInt32 uidValidity; GetUidValidity(&uidValidity); // stick the uid validity in front of the url, so that if the uid validity // changes, we won't re-use the wrong cache entries. nsCAutoString cacheKey; cacheKey.AppendInt(uidValidity, 16); cacheKey.Append(urlSpec); nsCOMPtr cacheEntry; // if mem cache entry is broken or empty, go to next message. rv = cacheSession->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ, PR_FALSE, getter_AddRefs(cacheEntry)); if (cacheEntry) { rv = cacheEntry->OpenInputStream(0, getter_AddRefs(inputStream)); if (NS_SUCCEEDED(rv)) { PRUint32 bytesAvailable = 0; rv = inputStream->Available(&bytesAvailable); if (!bytesAvailable) continue; rv = GetMsgPreviewTextFromStream(msgHdr, inputStream); } } else // lets look in the offline store { PRUint32 msgFlags; msgHdr->GetFlags(&msgFlags); nsMsgKey msgKey; msgHdr->GetMessageKey(&msgKey); if (msgFlags & MSG_FLAG_OFFLINE) { nsMsgKey messageOffset; PRUint32 messageSize; GetOfflineFileStream(msgKey, &messageOffset, &messageSize, getter_AddRefs(inputStream)); if (inputStream) rv = GetMsgPreviewTextFromStream(msgHdr, inputStream); } else if (!aLocalOnly) keysToFetchFromServer.Add(msgKey); } } if (keysToFetchFromServer.GetSize() > 0) { nsCOMPtr imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); PRUint32 msgCount = keysToFetchFromServer.GetSize(); nsCAutoString messageIds; AllocateImapUidString(keysToFetchFromServer.GetArray(), msgCount, nsnull, messageIds); rv = imapService->GetBodyStart(m_eventQueue, this, aUrlListener, messageIds.get(), 2048, nsnull); *aAsyncResults = PR_TRUE; // the preview text will be available async... } return NS_OK; } NS_IMETHODIMP nsImapMailFolder::AddKeywordsToMessages(nsISupportsArray *aMessages, const char *aKeywords) { nsresult rv = nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keys; rv = BuildIdsAndKeyArray(aMessages, messageIds, keys); NS_ENSURE_SUCCESS(rv, rv); rv = StoreCustomKeywords(nsnull, aKeywords, nsnull, keys.GetArray(), keys.GetSize(), nsnull); if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } NS_IMETHODIMP nsImapMailFolder::RemoveKeywordsFromMessages(nsISupportsArray *aMessages, const char *aKeywords) { nsresult rv = nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords); if (NS_SUCCEEDED(rv)) { nsCAutoString messageIds; nsMsgKeyArray keys; nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keys); NS_ENSURE_SUCCESS(rv, rv); rv = StoreCustomKeywords(nsnull, nsnull, aKeywords, keys.GetArray(), keys.GetSize(), nsnull); if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); } return rv; } NS_IMETHODIMP nsImapMailFolder::GetCustomIdentity(nsIMsgIdentity **aIdentity) { if (mFlags & MSG_FOLDER_FLAG_IMAP_OTHER_USER) { nsresult rv; PRBool delegateOtherUsersFolders = PR_FALSE; nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (NS_SUCCEEDED(rv)) prefBranch->GetBoolPref("mail.imap.delegateOtherUsersFolders", &delegateOtherUsersFolders); // if we're automatically delegating other user's folders, we need to // cons up an e-mail address for the other user. We do that by // taking the other user's name and the current user's domain name, // assuming they'll be the same. So, @ if (delegateOtherUsersFolders) { nsCOMPtr server = do_QueryReferent(mServer, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr ourIdentity; nsCOMPtr retIdentity; nsCOMPtr account; nsXPIDLCString foldersUserName; nsXPIDLCString ourEmailAddress; accountManager->FindAccountForServer(server, getter_AddRefs(account)); NS_ENSURE_SUCCESS(rv, rv); account->GetDefaultIdentity(getter_AddRefs(ourIdentity)); NS_ENSURE_SUCCESS(rv, rv); ourIdentity->GetEmail(getter_Copies(ourEmailAddress)); PRInt32 atPos = ourEmailAddress.FindChar('@'); if (atPos != -1) { nsXPIDLCString otherUsersEmailAddress; GetFolderOwnerUserName(getter_Copies(otherUsersEmailAddress)); otherUsersEmailAddress.Append(Substring(ourEmailAddress, atPos, ourEmailAddress.Length())); nsCOMPtr identities; rv = accountManager->GetIdentitiesForServer(server, getter_AddRefs(identities)); NS_ENSURE_SUCCESS(rv, rv); PRUint32 numIdentities; rv = identities->Count(&numIdentities); NS_ENSURE_SUCCESS(rv, rv); for (PRUint32 identityIndex = 0; identityIndex < numIdentities; identityIndex++) { nsCOMPtr identity = do_QueryElementAt(identities, identityIndex); if (!identity) continue; nsXPIDLCString identityEmail; identity->GetEmail(getter_Copies(identityEmail)); if (identityEmail.Equals(otherUsersEmailAddress)) { retIdentity = identity;; break; } } if (!retIdentity) { // create the identity rv = accountManager->CreateIdentity(getter_AddRefs(retIdentity)); NS_ENSURE_SUCCESS(rv, rv); retIdentity->SetEmail(otherUsersEmailAddress); nsCOMPtr account; accountManager->FindAccountForServer(server, getter_AddRefs(account)); NS_ENSURE_SUCCESS(rv, rv); account->AddIdentity(retIdentity); } } if (retIdentity) { NS_ADDREF(*aIdentity = retIdentity); return NS_OK; } } } return nsMsgDBFolder::GetCustomIdentity(aIdentity); }