gecko-dev/mailnews/imap/src/nsImapMailFolder.cpp
2004-04-03 17:59:21 +00:00

7650 lines
247 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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, 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Seth Spitzer <sspitzer@netscape.com>
* Lorenzo Colitti <lorenzo@colitti.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 NPL, 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 NPL, 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 "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 "nsMsgUtf7Utils.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 <time.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<nsISimpleEnumerator> dirIterator;
rv = srcDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
if (NS_FAILED(rv)) return rv;
rv = dirIterator->HasMoreElements(&hasMore);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFile> 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<nsIFile> destClone;
rv = destDir->Clone(getter_AddRefs(destClone));
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsILocalFile> 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, nsString());
}
}
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_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<nsIEventQueueService> 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(nsIImapExtensionSink)
NS_IMPL_QUERY_BODY(nsIImapMiscellaneousSink)
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
nsAutoString str; str.AssignWithConversion(nsFilePath(path));
str += sep;
path = nsFilePath(str);
}
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;
}
NS_IMETHODIMP nsImapMailFolder::AddSubfolderWithPath(nsAutoString *name, nsIFileSpec *dbPath,
nsIMsgFolder **child)
{
if(!child)
return NS_ERROR_NULL_POINTER;
nsresult rv = NS_OK;
nsCOMPtr<nsIRDFService> 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 <nsIMsgFolder> 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<nsIRDFResource> res;
rv = rdf->GetResource(uri, getter_AddRefs(res));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
if (NS_FAILED(rv))
return rv;
folder->SetPath(dbPath);
nsCOMPtr<nsIMsgImapMailFolder> 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->Equals(NS_LITERAL_STRING("Inbox"),
nsCaseInsensitiveStringComparator()))
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->Equals(NS_LITERAL_STRING("Sent"), nsCaseInsensitiveStringComparator()))
folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
else if(name->Equals(NS_LITERAL_STRING("Drafts"), nsCaseInsensitiveStringComparator()))
folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
else if (name->Equals(NS_LITERAL_STRING("Templates"), nsCaseInsensitiveStringComparator()))
folder->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
#endif
}
folder->SetFlags(flags);
//at this point we must be ok and we don't want to return failure in case GetIsServer failed.
rv = NS_OK;
nsCOMPtr <nsISupports> supports = do_QueryInterface(folder);
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<nsIMsgFolder> child;
nsCOMPtr<nsIMsgIncomingServer> server;
nsCOMPtr<nsIImapIncomingServer> imapServer;
if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
imapServer = do_QueryInterface(server);
PRBool isServer;
rv = GetIsServer(&isServer);
char *folderName;
for (nsDirectoryIterator dir(path, PR_FALSE); dir.Exists(); dir++)
{
nsFileSpec currentFolderPath = dir.Spec();
folderName = currentFolderPath.GetLeafName();
currentFolderNameStr.AssignWithConversion(folderName);
if (isServer && imapServer)
{
PRBool isPFC;
imapServer->GetIsPFC(folderName, &isPFC);
if (isPFC)
{
nsCOMPtr <nsIMsgFolder> pfcFolder;
imapServer->GetPFC(PR_TRUE, getter_AddRefs(pfcFolder));
continue;
}
// should check if this is the PFC
}
if (nsShouldIgnoreFile(currentFolderNameStr))
{
PL_strfree(folderName);
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 <nsIMsgFolderCacheElement> cacheElement;
nsCOMPtr <nsIFileSpec> curFolder;
nsCOMPtr <nsIFileSpec> dbFile;
NS_NewFileSpecWithSpec(currentFolderPath, getter_AddRefs(dbFile));
// don't strip off the .msf in currentFolderPath.
currentFolderPath.SetLeafName(folderName);
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;
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))
imapServer->CreatePRUnicharStringFromUTF7(onlineFullUtf7Name, getter_Copies(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
utf7LeafName.AssignWithConversion(onlineFullUtf7Name);
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 <nsIFileSpec> 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());
}
PL_strfree(folderName);
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::GetSubFolders(nsIEnumerator* *result)
{
PRBool isServer;
nsresult rv = GetIsServer(&isServer);
if (!m_initialized)
{
nsCOMPtr<nsIFileSpec> 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 <nsIMsgFolder> 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<nsIFileSpec> pathSpec;
nsresult rv = GetPath(getter_AddRefs(pathSpec));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIMsgDatabase> mailDBFactory;
rv = nsComponentManager::CreateInstance(kCImapDB, nsnull, NS_GET_IID(nsIMsgDatabase), (void **) getter_AddRefs(mailDBFactory));
if (NS_SUCCEEDED(rv) && mailDBFactory)
folderOpen = mailDBFactory->OpenFolderDB(this, PR_TRUE, PR_FALSE, getter_AddRefs(mDatabase));
if(folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING || folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
folderOpen = mailDBFactory->OpenFolderDB(this, PR_TRUE, PR_TRUE, getter_AddRefs(mDatabase));
if(mDatabase)
{
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<nsIMsgIncomingServer> 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->ConfigureTemporaryReturnReceiptsFilter(m_filterList);
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add MDN filter");
}
}
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
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);
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<nsIMsgAccountManager> 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<nsIMsgIncomingServer> 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're already running a url/select...
if (NS_SUCCEEDED(rv) && !m_urlRunning && selectFolder)
{
nsCOMPtr <nsIEventQueue> eventQ;
nsCOMPtr<nsIEventQueueService> pEventQService =
do_GetService(kEventQueueServiceCID, &rv);
if (NS_SUCCEEDED(rv) && pEventQService)
pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
getter_AddRefs(eventQ));
nsCOMPtr <nsIURI> url;
rv = imapService->SelectFolder(eventQ, this, this, msgWindow, getter_AddRefs(url));
if (url && m_urlListener)
{
nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(url);
if (mailnewsUrl)
{
mailnewsUrl->RegisterListener(m_urlListener);
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_urlRunning) // if we're already running a 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).Equals(NS_LITERAL_STRING("Inbox"),nsCaseInsensitiveStringComparator()) ) // Inbox, a special folder
{
ThrowAlertMsg("folderExists", msgWindow);
return NS_MSG_FOLDER_EXISTS;
}
nsCOMPtr<nsIImapService> 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.
nsCOMPtr<nsIFileSpec> pathSpec;
rv = GetPath(getter_AddRefs(pathSpec));
if (NS_FAILED(rv)) return rv;
nsFileSpec path;
rv = pathSpec->GetFileSpec(&path);
if (NS_FAILED(rv)) return rv;
// if (!path.Exists())
// {
// path.CreateDir();
// }
rv = CreateDirectoryForFolder(path);
if(NS_FAILED(rv))
return rv;
nsAutoString leafName; leafName.AssignWithConversion(folderName);
nsAutoString folderNameStr;
nsAutoString parentName = leafName;
PRInt32 folderStart = leafName.FindChar('/');
if (folderStart > 0)
{
nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRDFResource> res;
nsCOMPtr<nsIMsgImapMailFolder> 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
nsCAutoString safeParentName;
safeParentName.AssignWithConversion(parentName);
NS_MsgHashIfNecessary(safeParentName);
path += safeParentName.get();
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<nsIMsgDatabase> mailDBFactory;
nsCOMPtr<nsIMsgFolder> child;
rv = nsComponentManager::CreateInstance(kCImapDB, nsnull, NS_GET_IID(nsIMsgDatabase), (void **) getter_AddRefs(mailDBFactory));
if (NS_SUCCEEDED(rv) && mailDBFactory)
{
nsCOMPtr<nsIMsgDatabase> unusedDB;
nsCOMPtr <nsIFileSpec> dbFileSpec;
// warning, path will be changed
rv = CreateFileSpecForDB(folderName, path, getter_AddRefs(dbFileSpec));
NS_ENSURE_SUCCESS(rv,rv);
rv = mailDBFactory->Open(dbFileSpec, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(unusedDB));
if (NS_SUCCEEDED(rv) && unusedDB)
{
//need to set the folder name
nsCOMPtr <nsIDBFolderInfo> folderInfo;
rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
// if(NS_SUCCEEDED(rv))
// {
// ### DMB used to be leafNameFromUser?
// folderInfo->SetMailboxName(&folderNameStr);
// }
//Now let's create the actual new folder
rv = AddSubfolderWithPath(&folderNameStr, dbFileSpec, getter_AddRefs(child));
// if (NS_SUCCEEDED(rv) && child)
// child->SetPath(dbFileSpec);
nsCOMPtr <nsIMsgImapMailFolder> 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 = CreateUnicodeStringFromUtf7(folderName, getter_Copies(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);
}
}
if (!suppressNotification)
{
nsCOMPtr <nsIAtom> folderCreateAtom;
if(NS_SUCCEEDED(rv) && child)
{
nsCOMPtr<nsISupports> childSupports(do_QueryInterface(child));
nsCOMPtr<nsISupports> folderSupports;
rv = QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(folderSupports));
if(childSupports && NS_SUCCEEDED(rv))
{
NotifyItemAdded(folderSupports, childSupports, "folderView");
folderCreateAtom = do_GetAtom("FolderCreateCompleted");
child->NotifyFolderEvent(folderCreateAtom);
}
}
else
{
folderCreateAtom = do_GetAtom("FolderCreateFailed");
NotifyFolderEvent(folderCreateAtom);
}
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::List()
{
nsresult rv;
nsCOMPtr<nsIImapService> 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<nsISupportsArray> folders;
nsresult rv = NS_NewISupportsArray(getter_AddRefs(folders));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISupports> folderSupport = do_QueryInterface(which, &rv);
if (NS_FAILED(rv)) return rv;
folders->AppendElement(folderSupport);
which->Delete();
return nsMsgDBFolder::DeleteSubFolders(folders, nsnull);
}
NS_IMETHODIMP nsImapMailFolder::CreateStorageIfMissing(nsIUrlListener* urlListener)
{
nsresult status = NS_OK;
nsCOMPtr <nsIMsgFolder> 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<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &status));
if (NS_FAILED(status)) return status;
nsCOMPtr<nsIRDFResource> 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<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && imapService)
{
nsCOMPtr <nsIURI> 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;
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)
{
if (!aHierarchyDelimiter)
return NS_ERROR_NULL_POINTER;
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;
// compact offline store, if folder configured for offline use.
if (mFlags & MSG_FOLDER_FLAG_OFFLINE)
CompactOfflineStore(aMsgWindow);
nsCOMPtr<nsIImapService> 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<nsISupportsArray> folderArray;
if (!aFolderArray)
{
nsCOMPtr<nsIMsgFolder> rootFolder;
nsCOMPtr<nsISupportsArray> 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<nsISupports> supports = getter_AddRefs(allDescendents->ElementAt(i));
nsCOMPtr<nsIMsgFolder> 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 <nsIMsgFolderCompactor> 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<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr <nsIURI> uri;
rv = imapService->UpdateFolderStatus(m_eventQueue, this, aListener, getter_AddRefs(uri));
if (uri)
{
nsCOMPtr <nsIMsgMailNewsUrl> 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<nsIMsgFolder> trashFolder;
nsresult rv = GetTrashFolder(getter_AddRefs(trashFolder));
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIMsgAccountManager> 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<nsIImapIncomingServer> 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<nsIMsgDatabase> trashDB;
if (WeAreOffline())
{
nsCOMPtr <nsIMsgDatabase> trashDB;
rv = trashFolder->GetMsgDatabase(nsnull, getter_AddRefs(trashDB));
if (NS_SUCCEEDED(rv) && trashDB)
{
nsMsgKey fakeKey;
trashDB->GetNextFakeOfflineMsgKey(&fakeKey);
nsCOMPtr <nsIMsgOfflineImapOperation> op;
rv = trashDB->GetOfflineOpForKey(fakeKey, PR_TRUE, getter_AddRefs(op));
trashFolder->SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS);
op->SetOperation(nsIMsgOfflineImapOperation::kDeleteAllMsgs);
}
return rv;
}
nsCOMPtr <nsIDBFolderInfo> transferInfo;
rv = trashFolder->GetDBTransferInfo(getter_AddRefs(transferInfo));
rv = trashFolder->Delete(); // delete summary spec
trashFolder->SetDBTransferInfo(transferInfo);
trashFolder->SetSizeOnDisk(0);
nsCOMPtr<nsIImapService> imapService =
do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
PRBool hasSubfolders = PR_FALSE;
rv = trashFolder->GetHasSubFolders(&hasSubfolders);
if (hasSubfolders)
{
nsCOMPtr<nsIEnumerator> aEnumerator;
nsCOMPtr<nsISupports> aSupport;
nsCOMPtr<nsIMsgFolder> aFolder;
nsCOMPtr<nsISupportsArray> aSupportsArray;
rv = NS_NewISupportsArray(getter_AddRefs(aSupportsArray));
if (NS_FAILED(rv)) return rv;
rv = trashFolder->GetSubFolders(getter_AddRefs(aEnumerator));
PRBool confirmDeletion;
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv))
prefBranch->GetBoolPref("mail.imap.confirm_emptyTrashFolderDeletion", &confirmDeletion);
nsXPIDLString confirmationStr;
nsCOMPtr<nsIStringBundle> bundle;
nsCOMPtr<nsIDOMWindowInternal> parentWindow;
nsCOMPtr<nsIPromptService> promptService;
if (confirmDeletion)
{
IMAPGetStringByID(IMAP_EMPTY_TRASH_CONFIRM, getter_Copies(confirmationStr));
promptService = do_GetService("@mozilla.org/embedcomp/prompt-service;1");
nsCOMPtr<nsIDocShell> docShell;
(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))
{
PRBool confirmed = PR_TRUE;
PRInt32 dlgResult = -1;
rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport));
if (confirmDeletion)
{
nsXPIDLString statusString, confirmText;
nsCOMPtr <nsIMsgFolder> 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<nsIUrlListener> 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<nsIEnumerator> aEnumerator;
nsCOMPtr<nsISupports> aSupport;
nsCOMPtr<nsIMsgFolder> aFolder;
nsCOMPtr<nsISupportsArray> 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))
{
PRBool confirmed = PR_TRUE;
PRInt32 dlgResult = -1;
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<nsIFileSpec> 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);
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::Rename (const PRUnichar *newName, nsIMsgWindow *msgWindow )
{
nsresult rv = NS_ERROR_FAILURE;
nsAutoString newNameStr(newName);
if (newNameStr.FindChar(m_hierarchyDelimiter,0) != -1)
{
nsCOMPtr<nsIDocShell> docShell;
if (msgWindow)
msgWindow->GetRootDocShell(getter_AddRefs(docShell));
if (docShell)
{
nsCOMPtr<nsIStringBundle> 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<nsIPrompt> dialog(do_GetInterface(docShell));
if (dialog && alertString)
dialog->Alert(nsnull, alertString);
}
}
return NS_ERROR_FAILURE;
}
nsCOMPtr <nsIImapIncomingServer> incomingImapServer;
GetImapIncomingServer(getter_AddRefs(incomingImapServer));
if (incomingImapServer)
RecursiveCloseActiveConnections(incomingImapServer);
nsCOMPtr<nsIImapService> 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<nsIMsgImapMailFolder> folder;
mSubFolders->Count(&cnt);
if (cnt > 0)
{
for (i = 0; i < cnt; i++)
{
folder = do_QueryElementAt(mSubFolders, i);
if (folder)
folder->RecursiveCloseActiveConnections(incomingImapServer);
nsCOMPtr<nsIMsgFolder> 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<nsIMsgImapMailFolder> 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)
{
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<nsIFileSpec> oldPathSpec;
rv = GetPath(getter_AddRefs(oldPathSpec));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFileSpec> 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)
{
oldPathSpec->GetFileSpec(&dirSpec);
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<nsILocalFile> srcDir = (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsILocalFile> 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);
PRBool isServer = PR_FALSE;
GetIsServer(&isServer);
if (!isServer)
{
nsCOMPtr<nsIImapIncomingServer> 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<nsIMsgIncomingServer> 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<nsIMsgIncomingServer> server;
if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
{
nsCOMPtr <nsIImapIncomingServer> 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());
else if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Forwarded)
StoreImapFlags(kImapMsgForwardedFlag, PR_TRUE, messageIDs.GetArray(), messageIDs.GetSize());
}
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;
rv = StoreImapFlags(kImapMsgSeenFlag, markRead, keysToMarkRead.GetArray(), keysToMarkRead.GetSize());
mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
}
return rv;
}
NS_IMETHODIMP
nsImapMailFolder::SetLabelForMessages(nsISupportsArray *aMessages, nsMsgLabelValue aLabel)
{
NS_ENSURE_ARG(aMessages);
nsCAutoString messageIds;
nsMsgKeyArray keysToLabel;
nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keysToLabel);
NS_ENSURE_SUCCESS(rv, rv);
return StoreImapFlags((aLabel << 9), PR_TRUE, keysToLabel.GetArray(), keysToLabel.GetSize());
}
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());
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());
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());
mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::SetOnlineName(const char * aOnlineFolderName)
{
nsresult rv;
nsCOMPtr<nsIMsgDatabase> db;
nsCOMPtr<nsIDBFolderInfo> 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 <nsIMsgDBHdr> 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<nsIEnumerator> aEnumerator;
nsCOMPtr<nsIRDFResource> 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<nsIImapIncomingServer> 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<nsIMsgFolder> rootFolder;
nsCOMPtr<nsIMsgFolder> 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(
this, &srcKeyArray, messageIds.get(), nsnull,
PR_TRUE, isMove, m_eventQueue, nsnull);
if (!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 <nsITransactionManager> 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 <cnt; i++)
{
nsCOMPtr <nsIMsgDBHdr> msgHdr = do_QueryElementAt(messages, i);
if (msgHdr)
{
msgHdr->GetFlags(&flags);
if (!(flags & MSG_FLAG_IMAP_DELETED))
{
deleteMsgs = PR_TRUE;
break;
}
}
}
}
if (deleteMsgs)
messageFlags |= kImapMsgSeenFlag;
rv = StoreImapFlags(messageFlags, deleteMsgs, srcKeyArray.GetArray(), srcKeyArray.GetSize());
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<nsIMsgFolder> srcFolder;
nsCOMPtr<nsISupports>srcSupport;
PRUint32 count = 0;
rv = messages->Count(&count);
rv = QueryInterface(NS_GET_IID(nsIMsgFolder),
getter_AddRefs(srcFolder));
nsCOMPtr<nsIMsgCopyService> 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<nsIMsgFolder> parent;
nsCOMPtr<nsIMsgFolder> 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<nsIMsgFolder> curFolder;
nsCOMPtr<nsIUrlListener> urlListener;
nsCOMPtr<nsIMsgFolder> trashFolder;
PRUint32 i, 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;
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
rv = folders->Count(&folderCount);
NS_ENSURE_SUCCESS(rv, 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<nsIImapIncomingServer> 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<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv))
prefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash", &confirmDeletion);
}
if (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<nsIDocShell> docShell;
msgWindow->GetRootDocShell(getter_AddRefs(docShell));
nsCOMPtr<nsIPrompt> 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);
else
return rv;
}
// Called by Biff, or when user presses GetMsg button.
NS_IMETHODIMP nsImapMailFolder::GetNewMessages(nsIMsgWindow *aWindow, nsIUrlListener *aListener)
{
nsCOMPtr<nsIMsgFolder> rootFolder;
nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
if(NS_SUCCEEDED(rv) && rootFolder)
{
nsCOMPtr<nsIImapIncomingServer> imapServer;
GetImapIncomingServer(getter_AddRefs(imapServer));
PRBool performingBiff = PR_FALSE;
if (imapServer)
{
nsCOMPtr<nsIMsgIncomingServer> 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<nsIPrefBranch> 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<nsIMsgFolder> inbox;
rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(inbox));
if (inbox)
{
nsCOMPtr<nsIMsgImapMailFolder> 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 <nsISimpleEnumerator> 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 <nsIMsgDBHdr> 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<nsIPrefBranch> 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;
nsCOMPtr<nsIDBFolderInfo> 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);
if (mDatabase->ListAllOfflineDeletes(&existingKeys) > 0)
existingKeys.QuickSort();
}
PRInt32 folderValidity;
aSpec->GetFolder_UIDVALIDITY(&folderValidity);
nsCOMPtr <nsIImapFlagAndUidState> 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() */)
{
nsCOMPtr<nsIMsgDatabase> mailDBFactory;
nsCOMPtr<nsIFileSpec> pathSpec;
rv = GetPath(getter_AddRefs(pathSpec));
if (NS_FAILED(rv)) return rv;
nsFileSpec dbName;
rv = pathSpec->GetFileSpec(&dbName);
if (NS_FAILED(rv)) return rv;
rv = nsComponentManager::CreateInstance(kCImapDB, nsnull,
NS_GET_IID(nsIMsgDatabase),
(void **) getter_AddRefs(mailDBFactory));
if (NS_FAILED(rv)) return rv;
nsCOMPtr <nsIDBFolderInfo> 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 = mailDBFactory->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, 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, 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 (keysToFetch.GetSize() > 0)
{
// We must ensure that the server knows that we are performing biff.
// Otherwise the stand-alone biff won't fire.
nsCOMPtr<nsIMsgIncomingServer> server;
if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
server->SetPerformingBiff(PR_TRUE);
SetNumNewMessages(keysToFetch.GetSize());
}
}
SyncFlags(flagState);
PRInt32 numUnreadFromServer;
aSpec->GetNumUnseenMessages(&numUnreadFromServer);
if (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);
}
}
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 <nsIImapHeaderInfo> headerInfo;
if (!mDatabase)
GetDatabase(nsnull);
nsresult rv = aHdrXferInfo->GetNumHeaders(&numHdrs);
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 (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);
}
return rv;
}
nsresult nsImapMailFolder::SetupHeaderParseStream(PRUint32 aSize,
const char *content_type, nsIMailboxSpec *boxSpec)
{
nsresult rv = NS_ERROR_FAILURE;
if (!mDatabase)
GetDatabase(nsnull);
m_nextMessageByteLength = aSize;
if (!m_msgParser)
{
rv = nsComponentManager::CreateInstance(kParseMailMsgStateCID, nsnull,
NS_GET_IID(nsIMsgParseMailMsgState), (void **) getter_AddRefs(m_msgParser));
}
else
m_msgParser->Clear();
if (m_msgParser)
{
m_msgParser->SetMailDB(mDatabase);
return
m_msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
}
else
{
return NS_ERROR_OUT_OF_MEMORY;
}
return rv;
}
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)
{
nsCOMPtr<nsIMsgDBHdr> 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<nsIMsgIncomingServer> 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
{
rv = m_msgParser->GetAllHeaders(&headers, &headersSize);
if (NS_SUCCEEDED(rv) && headers)
{
if (m_filterList)
{
nsCOMPtr <nsIMsgWindow> msgWindow;
if (aProtocol)
{
nsCOMPtr <nsIImapUrl> aImapUrl;
nsCOMPtr <nsIMsgMailNewsUrl> msgUrl;
rv = aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
if (NS_SUCCEEDED(rv) && aImapUrl)
{
msgUrl = do_QueryInterface(aImapUrl);
if (msgUrl)
msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
}
}
GetMoveCoalescer(); // not sure why we're doing this here.
m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr, this, mDatabase,
headers, headersSize, this, msgWindow);
}
}
}
}
// here we need to tweak flags from uid state..
if (mDatabase && (!m_msgMovedByFilter || ShowDeletedMessages()))
mDatabase->AddNewHdrToDB(newMsgHdr, PR_TRUE);
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));
if (NS_SUCCEEDED(rv) && m_copyState->m_tmpFileSpec)
rv = m_copyState->m_tmpFileSpec->OpenStreamForWriting();
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::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_tmpFileSpec || !m_copyState->m_dataBuffer)
return rv;
PRUint32 readCount;
PRInt32 writeCount;
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 = 0;
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;
end = PL_strchr(start, '\r');
if (!end)
end = PL_strchr(start, '\n');
else if (*(end+1) == nsCRT::LF && linebreak_len == 0)
linebreak_len = 2;
if (linebreak_len == 0) // not initialize yet
linebreak_len = 1;
while (start && end)
{
if (PL_strncasecmp(start, "X-Mozilla-Status:", 17) &&
PL_strncasecmp(start, "X-Mozilla-Status2:", 18) &&
PL_strncmp(start, "From - ", 7))
{
rv = m_copyState->m_tmpFileSpec->Write(start,
end-start,
&writeCount);
rv = m_copyState->m_tmpFileSpec->Write(CRLF, 2, &writeCount);
}
start = end+linebreak_len;
if (start >=
m_copyState->m_dataBuffer+m_copyState->m_leftOver)
{
m_copyState->m_leftOver = 0;
break;
}
end = PL_strchr(start, '\r');
if (!end)
end = PL_strchr(start, '\n');
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::EndCopy(PRBool copySucceeded)
{
nsresult rv = copySucceeded ? NS_OK : NS_ERROR_FAILURE;
if (copySucceeded && m_copyState && m_copyState->m_tmpFileSpec)
{
nsCOMPtr<nsIUrlListener> urlListener;
m_copyState->m_tmpFileSpec->Flush();
m_copyState->m_tmpFileSpec->CloseStream();
nsCOMPtr<nsIImapService> imapService =
do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
rv = QueryInterface(NS_GET_IID(nsIUrlListener),
getter_AddRefs(urlListener));
nsCOMPtr<nsISupports> 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<nsIMsgDBHdr> 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<nsISupportsArray> 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 && *applyMore; actionIndex++)
{
nsCOMPtr<nsIMsgRuleAction> filterAction;
filterActionList->QueryElementAt(actionIndex, NS_GET_IID(nsIMsgRuleAction), getter_AddRefs(filterAction));
if (!filterAction)
continue;
if (NS_SUCCEEDED(filterAction->GetType(&actionType)))
{
if (actionType == nsMsgFilterAction::MoveToFolder)
{
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 <nsIMsgFolder> 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());
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::MarkRead:
{
nsMsgKeyArray keysToFlag;
keysToFlag.Add(msgKey);
StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize());
msgIsNew = PR_FALSE;
}
break;
case nsMsgFilterAction::MarkFlagged:
{
nsMsgKeyArray keysToFlag;
keysToFlag.Add(msgKey);
StoreImapFlags(kImapMsgFlaggedFlag, PR_TRUE, keysToFlag.GetArray(), keysToFlag.GetSize());
}
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());
}
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);
}
}
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<nsIImapService> 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<nsIImapService> 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<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr <nsIURI> 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 <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(resultUrl);
if (mailnewsUrl)
{
nsCOMPtr <nsIUrlListener> folderListener = do_QueryInterface(aDstFolder);
if (folderListener)
mailnewsUrl->RegisterListener(folderListener);
}
}
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::StoreImapFlags(PRInt32 flags, PRBool addFlags, nsMsgKey *keys, PRUint32 numKeys)
{
nsresult rv = NS_OK;
if (!WeAreOffline())
{
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
nsCAutoString msgIds;
AllocateUidStringFromKeys(keys, numKeys, msgIds);
if (addFlags)
{
imapService->AddMessageFlags(m_eventQueue, this, this,
nsnull, msgIds.get(), flags, PR_TRUE);
}
else
{
imapService->SubtractMessageFlags(m_eventQueue, this, this,
nsnull, msgIds.get(), flags,
PR_TRUE);
}
}
}
else
{
GetDatabase(nsnull);
if (mDatabase)
{
PRUint32 total = numKeys;
for (PRUint32 keyIndex=0; keyIndex < total; keyIndex++)
{
nsCOMPtr <nsIMsgOfflineImapOperation> 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<nsIImapService> 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<nsIMsgIncomingServer> 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<nsIImapIncomingServer> 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)
{
if (!m_adminUrl.IsEmpty())
{
nsCOMPtr <nsIDocShell> docShell;
rv = window->GetRootDocShell(getter_AddRefs(docShell));
if (NS_SUCCEEDED(rv) && docShell)
{
nsCOMPtr<nsIURI> 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<nsIImapService> 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<nsIImapService> 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<nsIImapService> 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<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &err));
nsCOMPtr<nsIRDFResource> res;
err = rdf->GetResource(destFolderUri, getter_AddRefs(res));
if (NS_FAILED(err))
return err;
nsCOMPtr<nsIMsgFolder> 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<nsIMsgFolder> parentFolder;
destIFolder->GetParent(getter_AddRefs(parentFolder));
if (parentFolder)
destIFolder->GetCanFileMessages(&canFileMessages);
if (!parentFolder || !canFileMessages)
{
filter->SetEnabled(PR_FALSE);
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)
{
}
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, nsIImapFlagAndUidState *flagState)
{
PRBool showDeletedMessages = ShowDeletedMessages();
int dbIndex=0; // current index into existingKeys
PRInt32 existTotal, numberOfKnownKeys;
PRInt32 messageIndex;
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 != 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);
}
}
}
}
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<nsISupports> 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<nsIImapService> 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 <nsIURI> 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<nsIImapService> 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)
{
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)
{
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 <nsIImapUrl> 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<nsIEventQueue> queue;
// get the Event Queue for this thread...
nsCOMPtr<nsIEventQueueService> pEventQService =
do_GetService(kEventQueueServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
getter_AddRefs(queue));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIImapService> imapService =
do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
rv = imapService->AddMessageFlags(queue, this, nsnull, nsnull,
messageIds,
kImapMsgDeletedFlag,
PR_TRUE);
if (NS_SUCCEEDED(rv))
{
nsMsgKeyArray affectedMessages;
char *keyTokenString = nsCRT::strdup(messageIds);
ParseUidString(keyTokenString, affectedMessages);
if (mDatabase)
mDatabase->DeleteMessages(&affectedMessages,nsnull);
nsCRT::free(keyTokenString);
return rv;
}
}
/* 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<nsIMsgFolder> 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)
{
PRBool messageClassified = PR_TRUE;
nsXPIDLCString::const_iterator b, e;
if (FindInReadable(NS_LITERAL_CSTRING("NonJunk"), keywords.BeginReading(b), keywords.EndReading(e)))
mDatabase->SetStringProperty(uidOfMessage, "junkscore", "0");
// Mac Mail uses "NotJunk"
else if (FindInReadable(NS_LITERAL_CSTRING("NotJunk"), keywords.BeginReading(b), keywords.EndReading(e)))
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)))
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<nsIMsgDBHdr> 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);
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<nsIMsgDBHdr> 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<nsIImapHostSessionList> 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<nsIImapIncomingServer> 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<nsIImapHostSessionList> 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<nsIMsgFolder> 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;
}
void nsImapMailFolder::ParseUidString(char *uidString, nsMsgKeyArray &keys)
{
// This is in the form <id>,<id>, or <id1>:<id2>
char curChar = *uidString;
PRBool isRange = PR_FALSE;
int32 curToken;
int32 saveStartToken=0;
for (char *curCharPtr = uidString; curChar && *curCharPtr;)
{
char *currentKeyToken = curCharPtr;
curChar = *curCharPtr;
while (curChar != ':' && curChar != ',' && curChar != '\0')
curChar = *curCharPtr++;
*(curCharPtr - 1) = '\0';
curToken = atoi(currentKeyToken);
if (isRange)
{
while (saveStartToken < curToken)
keys.Add(saveStartToken++);
}
keys.Add(curToken);
isRange = (curChar == ':');
if (isRange)
saveStartToken = curToken + 1;
}
}
// 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<nsIMsgDBHdr> 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<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsICacheSession> cacheSession;
rv = imapService->GetCacheSession(getter_AddRefs(cacheSession));
if (NS_SUCCEEDED(rv) && cacheSession)
rv = mailurl->SetImageCacheSession(cacheSession);
return rv;
}
NS_IMETHODIMP nsImapMailFolder::GetCurMoveCopyMessageFlags(nsIImapUrl *runningUrl, PRUint32 *aResult)
{
nsCOMPtr <nsISupports> copyState;
runningUrl->GetCopyState(getter_AddRefs(copyState));
if (copyState)
{
nsCOMPtr<nsImapMailCopyState> mailCopyState = do_QueryInterface(copyState);
if (mailCopyState && mailCopyState->m_message)
{
nsMsgLabelValue label;
mailCopyState->m_message->GetFlags(aResult);
mailCopyState->m_message->GetLabel(&label);
if (label != 0)
*aResult |= label << 25;
}
}
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");
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;
if (m_downloadingFolderForOfflineUse)
{
ReleaseSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this));
m_downloadingFolderForOfflineUse = PR_FALSE;
endedOfflineDownload = PR_TRUE;
EndOfflineDownload();
}
nsCOMPtr<nsIMsgMailSession> session =
do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
if (aUrl)
{
nsCOMPtr<nsIMsgWindow> msgWindow;
nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
nsCOMPtr<nsIImapUrl> 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)
{
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<nsIMsgFolder> srcFolder = do_QueryInterface(m_copyState->m_srcSupport, &rv);
if (m_copyState->m_isMove && !m_copyState->m_isCrossServerOp)
{
if (NS_SUCCEEDED(aExitCode))
{
nsCOMPtr<nsIMsgDatabase> srcDB;
if (srcFolder)
rv = srcFolder->GetMsgDatabase(msgWindow,
getter_AddRefs(srcDB));
if (NS_SUCCEEDED(rv) && srcDB)
{
nsCOMPtr<nsImapMoveCopyMsgTxn> msgTxn;
nsMsgKeyArray srcKeyArray;
if (m_copyState->m_allowUndo)
{
msgTxn = do_QueryInterface(m_copyState->m_undoMsgTxn);
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);
}
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<nsITransactionManager> txnMgr;
m_copyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
if (txnMgr)
txnMgr->DoTransaction(m_copyState->m_undoMsgTxn);
}
(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<nsIMsgDatabase> 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<nsIMsgDatabase> 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)
UpdateFolder(msgWindow);
if (m_copyState->m_msgWindow && m_copyState->m_undoMsgTxn)
{
nsCOMPtr<nsITransactionManager> 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 <nsIAtom> 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<nsIMsgFolder> parent;
rv = GetParent(getter_AddRefs(parent));
if (NS_SUCCEEDED(rv) && parent)
{
nsCOMPtr<nsIMsgImapMailFolder> 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 <nsIAtom> folderCreateAtom;
folderCreateAtom = do_GetAtom("FolderCreateFailed");
NotifyFolderEvent(folderCreateAtom);
}
break;
case nsIImapUrl::nsImapSubscribe:
if (NS_SUCCEEDED(aExitCode) && msgWindow)
{
nsXPIDLCString canonicalFolderName;
imapUrl->CreateCanonicalSourceFolderPathString(getter_Copies(canonicalFolderName));
nsCOMPtr <nsIMsgFolder> rootFolder;
nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
if(NS_SUCCEEDED(rv) && rootFolder)
{
nsCOMPtr <nsIMsgImapMailFolder> imapRoot = do_QueryInterface(rootFolder);
if (imapRoot)
{
nsCOMPtr <nsIMsgImapMailFolder> foundFolder;
rv = imapRoot->FindOnlineSubFolder(canonicalFolderName, getter_AddRefs(foundFolder));
if (NS_SUCCEEDED(rv) && foundFolder)
{
nsXPIDLCString uri;
nsCOMPtr <nsIMsgFolder> 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(nsIImapProtocol* aProtocol,
nsIMAPACLRightsInfo* aclRights)
{
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(nsIImapProtocol* aProtocol,
nsMsgKeyArray* aKeyArray,
const char* msgIdString,
nsIImapUrl * aUrl)
{ // CopyMessages() only
nsresult rv = NS_OK;
nsCOMPtr<nsImapMoveCopyMsgTxn> msgTxn;
nsCOMPtr<nsISupports> copyState;
if (aUrl)
aUrl->GetCopyState(getter_AddRefs(copyState));
if (copyState)
{
nsCOMPtr<nsImapMailCopyState> mailCopyState =
do_QueryInterface(copyState, &rv);
if (NS_FAILED(rv)) return rv;
if (mailCopyState->m_undoMsgTxn)
msgTxn = do_QueryInterface(mailCopyState->m_undoMsgTxn, &rv);
}
if (msgTxn)
msgTxn->SetCopyResponseUid(aKeyArray, msgIdString);
return NS_OK;
}
NS_IMETHODIMP
nsImapMailFolder::StartMessage(nsIMsgMailNewsUrl * aUrl)
{
nsCOMPtr<nsIImapUrl> imapUrl (do_QueryInterface(aUrl));
nsCOMPtr<nsISupports> copyState;
NS_ENSURE_TRUE(imapUrl, NS_ERROR_FAILURE);
imapUrl->GetCopyState(getter_AddRefs(copyState));
if (copyState)
{
nsCOMPtr <nsICopyMessageStreamListener> listener = do_QueryInterface(copyState);
if (listener)
listener->StartMessage();
}
return NS_OK;
}
NS_IMETHODIMP
nsImapMailFolder::EndMessage(nsIMsgMailNewsUrl * aUrl, nsMsgKey uidOfMessage)
{
nsCOMPtr<nsIImapUrl> imapUrl (do_QueryInterface(aUrl));
nsCOMPtr<nsISupports> copyState;
NS_ENSURE_TRUE(imapUrl, NS_ERROR_FAILURE);
imapUrl->GetCopyState(getter_AddRefs(copyState));
if (copyState)
{
nsCOMPtr <nsICopyMessageStreamListener> 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 <hit> <hit> ..."
// expect search results in the form of "* SEARCH <hit> <hit> ..."
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 <nsIMsgDBHdr> hitHeader;
rv = mDatabase->GetMsgHdrForKey(hitUid, getter_AddRefs(hitHeader));
if (NS_SUCCEEDED(rv) && hitHeader)
{
nsCOMPtr <nsIMsgSearchSession> searchSession;
nsCOMPtr <nsIMsgSearchAdapter> 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(nsIImapProtocol* aProtocol,
nsMsgKey aKey,
nsIImapUrl * aUrl)
{
nsresult rv = NS_OK;
nsCOMPtr<nsISupports> copyState;
if (aUrl)
aUrl->GetCopyState(getter_AddRefs(copyState));
if (copyState)
{
nsCOMPtr<nsImapMailCopyState> mailCopyState = do_QueryInterface(copyState, &rv);
if (NS_FAILED(rv)) return rv;
if (mailCopyState->m_undoMsgTxn) // CopyMessages()
{
nsCOMPtr<nsImapMoveCopyMsgTxn> msgTxn;
msgTxn = do_QueryInterface(mailCopyState->m_undoMsgTxn, &rv);
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(nsIImapProtocol* aProtocl,
nsCString* messageId,
nsIImapUrl * aUrl)
{
nsresult rv = NS_OK;
nsCOMPtr<nsISupports> copyState;
if (aUrl)
aUrl->GetCopyState(getter_AddRefs(copyState));
if (copyState)
{
nsCOMPtr<nsImapMailCopyState> mailCopyState =
do_QueryInterface(copyState, &rv);
if (NS_FAILED(rv)) return rv;
if (mailCopyState->m_listener)
rv = mailCopyState->m_listener->GetMessageId(messageId);
}
if (NS_SUCCEEDED(rv) && messageId->Length() > 0)
{
if (messageId->First() == '<')
messageId->Cut(0, 1);
if (messageId->Last() == '>')
messageId->SetLength(messageId->Length() -1);
}
return rv;
}
// nsIImapMiscellaneousSink methods
NS_IMETHODIMP
nsImapMailFolder::AddSearchResult(nsIImapProtocol* aProtocol,
const char* searchHitLine)
{
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsImapMailFolder::HeaderFetchCompleted(nsIImapProtocol* aProtocol)
{
nsCOMPtr <nsIMsgWindow> 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);
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.
PRBool autoDownloadNewHeaders = PR_FALSE;
if (mFlags & MSG_FOLDER_FLAG_INBOX)
{
nsCOMPtr<nsIImapIncomingServer> imapServer;
nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
if (NS_SUCCEEDED(rv) && imapServer)
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)
{
// 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 <nsIURI> runningUri;
aProtocol->GetRunningUrl(getter_AddRefs(runningUri));
if (runningUri)
{
nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(runningUri);
if (mailnewsUrl)
mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
}
}
PRBool filtersRun;
CallFilterPlugins(msgWindow, &filtersRun);
if (!filtersRun && m_performingBiff && mDatabase && numNewBiffMsgs > 0)
{
// 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<nsIMsgIncomingServer> 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(nsIImapProtocol* aProtocol,
nsMsgBiffState biffState)
{
SetBiffState(biffState);
return NS_OK;
}
NS_IMETHODIMP
nsImapMailFolder::GetUidValidity(PRInt32 *uidValidity)
{
NS_ENSURE_ARG(uidValidity);
if (m_uidValidity == kUidUnknown)
{
nsCOMPtr<nsIMsgDatabase> db;
nsCOMPtr<nsIDBFolderInfo> 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<nsIStringBundle> 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<nsIImapHostSessionList> 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?
uniOwner.Assign(NS_ConvertASCIItoUCS2(owner.get()));
}
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)
{
nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
nsresult rv = GetDatabase(nsnull);
m_aclFlags = aclFlags;
if (mDatabase)
{
rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
if (NS_SUCCEEDED(rv) && dbFolderInfo)
dbFolderInfo->SetUint32Property("aclFlags", aclFlags);
}
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<nsIDBFolderInfo> dbFolderInfo;
rv = GetDatabase(nsnull);
if (mDatabase)
{
rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
if (NS_SUCCEEDED(rv) && dbFolderInfo)
{
rv = dbFolderInfo->GetUint32Property("aclFlags", aclFlags, 0);
m_aclFlags = *aclFlags;
}
}
}
else
*aclFlags = m_aclFlags;
return rv;
}
nsresult nsImapMailFolder::SetSupportedUserFlags(PRUint32 userFlags)
{
nsCOMPtr<nsIDBFolderInfo> 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<nsIDBFolderInfo> dbFolderInfo;
rv = GetDatabase(nsnull);
if (mDatabase)
{
rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
if (NS_SUCCEEDED(rv) && dbFolderInfo)
{
rv = dbFolderInfo->GetUint32Property("imapFlags", userFlags, 0);
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 += "d";
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 <nsIMsgIncomingServer> 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);
ToLowerCase(ourUserName);
char *rightsWeOwn = PL_strdup(rights);
nsCStringKey hashKey(ourUserName);
if (rightsWeOwn && !ourUserName.IsEmpty())
{
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 (!ourUserName.IsEmpty() &&
(myUserName.Equals(ourUserName) || ourUserName.Equals(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 <nsIMsgIncomingServer> 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);
}
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);
}
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<nsIStringBundle> 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 += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_WRITE_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanIInsertInFolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_INSERT_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanILookupFolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_LOOKUP_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanIStoreSeenInFolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_SEEN_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanIDeleteInFolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_DELETE_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanICreateSubfolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_CREATE_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanIPostToFolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
bundle->GetStringFromID(IMAP_ACL_POST_RIGHT, getter_Copies(curRight));
rights.Append(curRight);
}
if (GetCanIAdministerFolder())
{
if (!rights.IsEmpty()) rights += NS_LITERAL_STRING(", ");
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<nsIImapMockChannel> mockChannel;
aImapUrl->GetMockChannel(getter_AddRefs(mockChannel));
if (mockChannel)
{
nsCOMPtr<nsIProgressEventSink> progressSink;
mockChannel->GetProgressEventSink(getter_AddRefs(progressSink));
if (progressSink)
{
nsCOMPtr<nsIRequest> 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<nsIMsgIncomingServer> server;
nsresult rv = GetServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv) && server)
{
nsCOMPtr<nsIImapServerSink> 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 <nsIImapUrl> 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,
ProgressInfo* aInfo)
{
if (aProtocol)
{
nsCOMPtr <nsIImapUrl> imapUrl;
aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl));
if (imapUrl)
{
nsCOMPtr<nsIImapMockChannel> mockChannel;
imapUrl->GetMockChannel(getter_AddRefs(mockChannel));
if (mockChannel)
{
nsCOMPtr<nsIProgressEventSink> progressSink;
mockChannel->GetProgressEventSink(getter_AddRefs(progressSink));
if (progressSink)
{
nsCOMPtr<nsIRequest> request = do_QueryInterface(mockChannel);
if (!request) return NS_ERROR_FAILURE;
progressSink->OnProgress(request, nsnull, aInfo->currentProgress, aInfo->maxProgress);
if (aInfo->message)
progressSink->OnStatus(request, nsnull, NS_OK, aInfo->message); // 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<nsImapMailCopyState> 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<nsIMsgFolder> 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<nsIMsgLocalMailFolder> 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;
EndOfflineDownload();
if (m_downloadingFolderForOfflineUse)
{
ReleaseSemaphore(NS_STATIC_CAST(nsIMsgImapMailFolder*, this));
m_downloadingFolderForOfflineUse = PR_FALSE;
}
}
if (aUrl)
return aUrl->SetUrlState(isRunning, statusCode);
return statusCode;
}
nsresult
nsImapMailFolder::CreateDirectoryForFolder(nsFileSpec &path) //** dup
{
nsresult rv = NS_OK;
if(!path.IsDirectory())
{
//If the current path isn't a directory, add directory separator
//and test it out.
rv = AddDirectorySeparator(path);
if(NS_FAILED(rv))
return rv;
nsFileSpec tempPath(path.GetNativePathCString(), PR_TRUE); // create incoming directories.
//If that doesn't exist, then we have to create this directory
if(!path.IsDirectory())
{
//If for some reason there's a file with the directory separator
//then we are going to fail.
if(path.Exists())
{
return NS_MSG_COULD_NOT_CREATE_DIRECTORY;
}
//otherwise we need to create a new directory.
else
{
path.CreateDirectory();
//Above doesn't return an error value so let's see if
//it was created.
if(!path.IsDirectory())
return NS_MSG_COULD_NOT_CREATE_DIRECTORY;
}
}
}
return rv;
}
// 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<nsISupports> aSupport(do_QueryInterface(srcFolder, &rv));
if (NS_FAILED(rv)) return rv;
rv = InitCopyState(aSupport, messages, isMove, PR_FALSE, isCrossServerOp, 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<nsIUrlListener> urlListener;
rv = QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener));
rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray);
nsImapMoveCopyMsgTxn* undoMsgTxn = new nsImapMoveCopyMsgTxn(
srcFolder, &srcKeyArray, messageIds.get(), this,
PR_TRUE, isMove, m_eventQueue, urlListener);
if (!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<nsIMsgDBHdr> 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<nsIRDFResource> res;
nsresult rv;
nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
if (NS_FAILED(rv))
return rv;
rv = rdf->GetResource(sourceFolderURI, getter_AddRefs(res));
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIMsgFolder> sourceFolder(do_QueryInterface(res, &rv));
if (NS_SUCCEEDED(rv) && sourceFolder)
{
if (sourceFolder)
{
nsCOMPtr <nsIDBFolderInfo> 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<nsIRDFResource> res;
nsresult rv;
nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
if (NS_FAILED(rv))
return rv;
rv = rdf->GetResource(sourceFolderURI, getter_AddRefs(res));
if (NS_SUCCEEDED(rv))
{
nsCOMPtr<nsIMsgFolder> sourceFolder(do_QueryInterface(res, &rv));
if (NS_SUCCEEDED(rv) && sourceFolder)
{
if (sourceFolder)
{
nsCOMPtr <nsIDBFolderInfo> 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<nsIOutputStream> outputStream;
nsresult rv = GetOfflineStoreOutputStream(getter_AddRefs(outputStream));
nsCOMPtr <nsISeekableStream> seekable;
PRUint32 curStorePos;
seekable = do_QueryInterface(outputStream);
if (seekable)
{
nsMsgKey messageOffset;
PRUint32 messageSize;
origHdr->GetMessageOffset(&messageOffset);
origHdr->GetOfflineMessageSize(&messageSize);
seekable->Tell(&curStorePos);
destHdr->SetMessageOffset(curStorePos);
nsCOMPtr <nsIInputStream> offlineStoreInputStream;
rv = srcFolder->GetOfflineStoreInputStream(getter_AddRefs(offlineStoreInputStream));
if (NS_SUCCEEDED(rv) && offlineStoreInputStream)
{
nsCOMPtr<nsISeekableStream> 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();
}
}
}
}
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 <nsIMsgDatabase> sourceMailDB;
nsCOMPtr <nsIDBFolderInfo> srcDbFolderInfo;
srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(srcDbFolderInfo), getter_AddRefs(sourceMailDB));
PRBool deleteToTrash = PR_FALSE;
PRBool deleteImmediately = PR_FALSE;
PRUint32 srcCount;
messages->Count(&srcCount);
nsCOMPtr<nsIImapIncomingServer> 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 <nsITransactionManager> 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 <nsIDBFolderInfo> 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<nsIMsgDBHdr> 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 <nsIMsgOfflineImapOperation> sourceOp;
rv = sourceMailDB->GetOfflineOpForKey(originalKey, PR_TRUE, getter_AddRefs(sourceOp));
if (NS_SUCCEEDED(rv) && sourceOp)
{
srcFolder->SetFlag(MSG_FOLDER_FLAG_OFFLINEEVENTS);
nsCOMPtr <nsIMsgDatabase> 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 <nsIMsgOfflineImapOperation> 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)
{
sourceOp->SetDestinationFolderURI(folderURI); // offline move
sourceOp->SetOperation(nsIMsgOfflineImapOperation::kMsgMoved);
}
else
sourceOp->AddMessageCopyOperation(folderURI); // offline copy
nsMsgKeyArray srcKeyArray;
nsCOMPtr<nsIUrlListener> 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 <nsIMsgDBHdr> 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 <nsIMsgDBHdr> 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 <nsIMsgOfflineImapOperation> 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<nsIUrlListener> 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<nsIUrlListener> 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<nsISupports> 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<nsIUrlListener> urlListener;
nsCOMPtr<nsISupports> srcSupport;
nsCOMPtr<nsISupports> copySupport;
if (WeAreOffline())
return CopyMessagesOffline(srcFolder, messages, isMove, msgWindow, listener);
nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
srcSupport = do_QueryInterface(srcFolder);
nsCOMPtr <nsIMsgIncomingServer> srcServer;
nsCOMPtr <nsIMsgIncomingServer> 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;
// 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 <nsIMsgDBHdr> msgDBHdr = do_QueryElementAt(messages, i, &rv);
if (mDatabase && msgDBHdr)
{
nsXPIDLCString junkScore, junkScoreOrigin;
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", junkScore.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, 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(
srcFolder, &srcKeyArray, messageIds.get(), this,
PR_TRUE, isMove, m_eventQueue, urlListener);
if (!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;
}
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
{
nsCOMPtr <nsIImapService> imapService = do_GetService (NS_IMAPSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv))
{
nsCOMPtr <nsIUrlListener> 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);
if (!confirmed) return NS_OK;
}
}
rv = imapService->MoveFolder(m_eventQueue,
srcFolder,
this,
urlListener,
msgWindow,
nsnull);
}
}
else
NS_ASSERTION(0,"isMoveFolder is false. Trying to copy to a different server.");
return rv;
}
NS_IMETHODIMP
nsImapMailFolder::CopyFileMessage(nsIFileSpec* fileSpec,
nsIMsgDBHdr* msgToReplace,
PRBool isDraftOrTemplate,
nsIMsgWindow *msgWindow,
nsIMsgCopyServiceListener* listener)
{
nsresult rv = NS_ERROR_NULL_POINTER;
nsMsgKey key = 0xffffffff;
nsCAutoString messageId;
nsCOMPtr<nsIUrlListener> urlListener;
nsCOMPtr<nsISupportsArray> messages;
nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(fileSpec, &rv);
rv = NS_NewISupportsArray(getter_AddRefs(messages));
if (NS_FAILED(rv))
return OnCopyCompleted(srcSupport, rv);
nsCOMPtr<nsIImapService> 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, listener, msgWindow, PR_FALSE);
if (NS_FAILED(rv))
return OnCopyCompleted(srcSupport, rv);
nsCOMPtr<nsISupports> 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<nsICopyMessageStreamListener> copyStreamListener = do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr<nsICopyMessageListener>
copyListener(do_QueryInterface(dstFolder, &rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIMsgFolder>
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<nsIMsgDBHdr> 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<nsIStreamListener>
streamListener(do_QueryInterface(copyStreamListener, &rv));
if(NS_FAILED(rv) || !streamListener)
return NS_ERROR_NO_INTERFACE;
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)
{
}
nsImapMailCopyState::~nsImapMailCopyState()
{
PR_Free(m_dataBuffer);
if (m_msgService && m_message)
{
nsCOMPtr <nsIMsgFolder> 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,
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<nsIMsgDBHdr> 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<nsIMsgDBHdr> 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_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<nsIMsgCopyService> 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.EqualsIgnoreCase("inbox");
if (isInbox)
*matches = mName.Equals(*name, nsCaseInsensitiveStringComparator());
else
*matches = mName.Equals(*name);
return NS_OK;
}
nsresult nsImapMailFolder::CreateBaseMessageURI(const char *aURI)
{
nsresult rv;
rv = nsCreateImapBaseMessageURI(aURI, &mBaseMessageURI);
return rv;
}
NS_IMETHODIMP nsImapMailFolder::GetFolderURL(char **aFolderURL)
{
NS_ENSURE_ARG_POINTER(aFolderURL);
nsCOMPtr<nsIMsgFolder> 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<nsIImapHostSessionList> 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<nsIEnumerator> aEnumerator;
GetSubFolders(getter_AddRefs(aEnumerator));
if (!aEnumerator)
return NS_OK;
nsCOMPtr<nsISupports> aSupport;
nsresult rv = aEnumerator->First();
while (NS_SUCCEEDED(rv))
{
rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport));
nsCOMPtr<nsIMsgImapMailFolder> 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<nsIEnumerator> aEnumerator;
GetSubFolders(getter_AddRefs(aEnumerator));
if (!aEnumerator)
return NS_OK;
nsCOMPtr<nsISupports> aSupport;
rv = aEnumerator->First();
while (NS_SUCCEEDED(rv))
{
rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport));
nsCOMPtr<nsIMsgImapMailFolder> 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::GetFolderVerifiedOnline(PRBool *bVal)
{
if (!bVal)
return NS_ERROR_NULL_POINTER;
*bVal = m_verifiedAsOnlineFolder;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::SetFolderVerifiedOnline(PRBool bVal)
{
m_verifiedAsOnlineFolder = 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::GetFolderQuotaDataIsValid(PRBool *aIsValid)
{
NS_ENSURE_ARG_POINTER(aIsValid);
*aIsValid = m_folderQuotaDataIsValid;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaDataIsValid(PRBool aIsValid)
{
m_folderQuotaDataIsValid = aIsValid;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::GetFolderQuotaRoot(nsACString &aQuotaRoot)
{
aQuotaRoot = m_folderQuotaRoot;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaRoot(const nsACString &aQuotaRoot)
{
m_folderQuotaRoot = aQuotaRoot;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::GetFolderQuotaUsedKB(PRUint32 *aUsedKB)
{
NS_ENSURE_ARG_POINTER(aUsedKB);
*aUsedKB = m_folderQuotaUsedKB;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaUsedKB(PRUint32 aUsedKB)
{
m_folderQuotaUsedKB = aUsedKB;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::GetFolderQuotaMaxKB(PRUint32 *aMaxKB)
{
NS_ENSURE_ARG_POINTER(aMaxKB);
*aMaxKB = m_folderQuotaMaxKB;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaMaxKB(PRUint32 aMaxKB)
{
m_folderQuotaMaxKB = aMaxKB;
return NS_OK;
}
NS_IMETHODIMP nsImapMailFolder::PerformExpand(nsIMsgWindow *aMsgWindow)
{
nsresult rv;
PRBool usingSubscription = PR_FALSE;
nsCOMPtr<nsIImapIncomingServer> 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<nsIImapService> 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<nsIFileSpec> 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<nsIMsgImapMailFolder> 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<nsIMsgDatabase> mailDBFactory;
nsCOMPtr<nsIMsgFolder> child;
nsCOMPtr <nsIMsgImapMailFolder> imapFolder;
rv = nsComponentManager::CreateInstance(kCImapDB, nsnull, NS_GET_IID(nsIMsgDatabase), (void **) getter_AddRefs(mailDBFactory));
if (NS_SUCCEEDED(rv) && mailDBFactory)
{
nsCOMPtr<nsIMsgDatabase> unusedDB;
nsCOMPtr <nsIFileSpec> 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 Open and not OpenFolderDB here, since we don't use the DB.
rv = mailDBFactory->Open(dbFileSpec, PR_TRUE, PR_TRUE, (nsIMsgDatabase **) getter_AddRefs(unusedDB));
if (NS_SUCCEEDED(rv) && unusedDB)
{
//need to set the folder name
nsCOMPtr <nsIDBFolderInfo> 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 = CreateUnicodeStringFromUtf7(proposedDBName.get(), getter_Copies(unicodeName));
if (NS_SUCCEEDED(rv) && unicodeName)
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<nsIMsgFolder> msgParent;
msgFolder->GetParentMsgFolder(getter_AddRefs(msgParent));
msgFolder->SetParent(nsnull);
msgParent->PropagateDelete(msgFolder,PR_FALSE, nsnull);
// Reset online status now that the folder is renamed.
nsCOMPtr <nsIMsgImapMailFolder> oldImapFolder = do_QueryInterface(msgFolder);
if (oldImapFolder)
oldImapFolder->SetVerifiedAsOnlineFolder(PR_FALSE);
nsCOMPtr<nsISupports> childSupports(do_QueryInterface(child));
nsCOMPtr<nsISupports> parentSupports;
rv = QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(parentSupports));
if(childSupports && NS_SUCCEEDED(rv))
{
NotifyItemAdded(parentSupports, childSupports, "folderView");
}
}
}
return rv;
}
NS_IMETHODIMP nsImapMailFolder::RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder)
{
nsresult rv = NS_OK;
m_initialized = PR_TRUE;
nsCOMPtr<nsIEnumerator> aEnumerator;
oldFolder->GetSubFolders(getter_AddRefs(aEnumerator));
nsCOMPtr<nsISupports> aSupport;
rv = aEnumerator->First();
while (NS_SUCCEEDED(rv))
{
rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport));
nsCOMPtr<nsIMsgFolder>msgFolder = do_QueryInterface(aSupport);
nsCOMPtr<nsIMsgImapMailFolder> 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<nsIFileSpec> 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<nsIFileSpec> 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<nsIFileSpec> 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<nsIFileSpec> dbFileSpec;
NS_NewFileSpecWithSpec(newPath, getter_AddRefs(dbFileSpec));
nsCOMPtr<nsIMsgFolder> child;
nsXPIDLString folderName;
rv = msgFolder->GetName(getter_Copies(folderName));
if (folderName.IsEmpty() || NS_FAILED(rv)) return rv;
nsXPIDLCString utf7LeafName;
utf7LeafName.Adopt(CreateUtf7ConvertedStringFromUnicode(folderName.get()));
nsAutoString unicodeLeafName;
unicodeLeafName.AssignWithConversion(utf7LeafName.get());
rv = AddSubfolderWithPath(&unicodeLeafName, dbFileSpec, getter_AddRefs(child));
if (!child || NS_FAILED(rv)) return rv;
child->SetName(folderName);
nsCOMPtr <nsIMsgImapMailFolder> 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<nsIMsgIncomingServer> 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;
nsCOMPtr<nsIImapService> 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);
}
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();
}
return NS_OK; // must not be any coalesced operations
}
NS_IMETHODIMP
nsImapMailFolder::OnMessageClassified(const char *aMsgURI, nsMsgJunkStatus aClassification)
{
nsXPIDLCString spamFolderURI;
nsCOMPtr <nsIMsgIncomingServer> server;
nsresult rv = GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsIMsgDBHdr> 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<nsISpamSettings> spamSettings;
rv = GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
NS_ENSURE_SUCCESS(rv, rv);
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<nsIMsgFolder> 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)
{
PlaybackCoalescedOperations();
// If we are performing biff for this folder, tell the server object
if (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<nsIMsgIncomingServer> 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 <nsIMsgFilterList> filterList;
nsresult rv = GetFilterList(nsnull, getter_AddRefs(filterList));
NS_ENSURE_SUCCESS(rv,rv);
rv = filterList->GetShouldDownloadAllHeaders(aResult);
if (*aResult)
return rv;
}
nsCOMPtr <nsIMsgFilterPlugin> filterPlugin;
nsCOMPtr<nsIMsgIncomingServer> 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<nsIMsgIncomingServer> server;
nsCOMPtr<nsIImapIncomingServer> 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;
}
}
}