mailnews/imap/src/nsImapMailFolder.cpp
author Mark Banner <bugzilla@standard8.plus.com>
Fri, 30 Dec 2011 12:07:39 +0000
changeset 10318 f9d611e3d0a562cedb52cdafb65dd4f8f5af3b23
parent 10291 097bc36f180d6abb8cb38b43f667fdc19d1d3e8e
child 10390 25d62c5e3121d290269118ad0aba11f3c40c652c
permissions -rw-r--r--
Bug 695256 - Switch from PRBool to bool and replace PR_TRUE/PR_FALSE with true/false in comm-central. r=bienvenu

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Pierre Phaneuf <pp@ludusdesign.com>
 *   Seth Spitzer <sspitzer@netscape.com>
 *   Lorenzo Colitti <lorenzo@colitti.com>
 *   Karsten Düsterloh <mnyromyr@tprac.de>
 *   Siddharth Agarwal <sid1337@gmail.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifdef MOZ_LOGGING
// sorry, this has to be before the pre-compiled header
#define FORCE_PR_LOG /* Allow logging in the release build */
#endif
#include "msgCore.h"
#include "prmem.h"
#include "nsMsgImapCID.h"
#include "nsImapMailFolder.h"
#include "nsIEnumerator.h"
#include "nsILocalFile.h"
#include "nsIFolderListener.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsIRDFService.h"
#include "nsRDFCID.h"
#include "nsMsgDBCID.h"
#include "nsMsgFolderFlags.h"
#include "nsImapFlagAndUidState.h"
#include "nsISeekableStream.h"
#include "nsThreadUtils.h"
#include "nsIImapUrl.h"
#include "nsImapUtils.h"
#include "nsMsgUtils.h"
#include "nsIMsgMailSession.h"
#include "nsMsgKeyArray.h"
#include "nsMsgBaseCID.h"
#include "nsMsgLocalCID.h"
#include "nsImapUndoTxn.h"
#include "nsIIMAPHostSessionList.h"
#include "nsIMsgCopyService.h"
#include "nsICopyMsgStreamListener.h"
#include "nsImapStringBundle.h"
#include "nsIMsgFolderCacheElement.h"
#include "nsTextFormatter.h"
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsMsgI18N.h"
#include "nsICacheSession.h"
#include "nsIDOMWindow.h"
#include "nsIMsgFilter.h"
#include "nsIMsgFilterService.h"
#include "nsIMsgSearchCustomTerm.h"
#include "nsImapMoveCoalescer.h"
#include "nsIPrompt.h"
#include "nsIPromptService.h"
#include "nsIDocShell.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.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 "nsIMsgFolderCompactor.h"
#include "nsMsgMessageFlags.h"
#include "nsIMimeHeaders.h"
#include "nsIMsgMdnGenerator.h"
#include "nsISpamSettings.h"
#include <time.h>
#include "nsIMsgMailNewsUrl.h"
#include "nsEmbedCID.h"
#include "nsIMsgComposeService.h"
#include "nsMsgCompCID.h"
#include "nsICacheEntryDescriptor.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIMsgIdentity.h"
#include "nsIMsgFolderNotificationService.h"
#include "nsNativeCharsetUtils.h"
#include "nsIExternalProtocolService.h"
#include "nsCExternalHandlerService.h"
#include "prprf.h"
#include "nsIMutableArray.h"
#include "nsArrayUtils.h"
#include "nsArrayEnumerator.h"
#include "nsAutoSyncManager.h"
#include "nsIMsgFilterCustomAction.h"
#include "nsMsgReadStateTxn.h"
#include "nsIStringEnumerator.h"
#include "nsIMsgStatusFeedback.h"
#include "nsAlgorithm.h"
#include "nsMsgLineBuffer.h"

static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kParseMailMsgStateCID, NS_PARSEMAILMSGSTATE_CID);
static NS_DEFINE_CID(kCImapHostSessionList, NS_IIMAPHOSTSESSIONLIST_CID);

extern PRLogModuleInfo *gAutoSyncLog;
extern PRLogModuleInfo* IMAP;

#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;
  bool isDir;

  rv = srcDir->IsDirectory(&isDir);
  if (NS_FAILED(rv)) return rv;
  if (!isDir) return NS_ERROR_INVALID_ARG;

  bool exists;
  rv = destDir->Exists(&exists);
  if (NS_SUCCEEDED(rv) && !exists)
    rv = destDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
  if (NS_FAILED(rv)) return rv;

  bool hasMore = 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, EmptyString());
      }

    }
    rv = dirIterator->HasMoreElements(&hasMore);
    if (NS_FAILED(rv)) return rv;
  }

  return rv;
}

nsImapMailFolder::nsImapMailFolder() :
    m_initialized(false),m_haveDiscoveredAllFolders(false),
    m_curMsgUid(0), m_nextMessageByteLength(0),
    m_urlRunning(false),
    m_verifiedAsOnlineFolder(false),
    m_explicitlyVerify(false),
    m_folderIsNamespace(false),
    m_folderNeedsSubscribing(false),
    m_folderNeedsAdded(false),
    m_folderNeedsACLListed(true),
    m_performingBiff(false),
    m_folderQuotaCommandIssued(false),
    m_folderQuotaDataIsValid(false),
    m_updatingFolder(false),
    m_downloadingFolderForOfflineUse(false),
    m_folderQuotaUsedKB(0),
    m_folderQuotaMaxKB(0),
    m_compactingOfflineStore(false),
    m_expunging(false),
    m_applyIncomingFilters(false),
    m_filterListRequiresBody(false)
{
  MOZ_COUNT_CTOR(nsImapMailFolder); // double count these for now.

  m_thread = do_GetCurrentThread();
  m_moveCoalescer = nsnull;
  m_boxFlags = 0;
  m_uidValidity = kUidUnknown;
  m_numServerRecentMessages = 0;
  m_numServerUnseenMessages = 0;
  m_numServerTotalMessages = 0;
  m_nextUID = nsMsgKey_None;
  m_hierarchyDelimiter = kOnlineHierarchySeparatorUnknown;
  m_folderACL = nsnull;
  m_aclFlags = 0;
  m_supportedUserFlags = 0;
  m_namespace = nsnull;
  m_pendingPlaybackReq = nsnull;
}

nsImapMailFolder::~nsImapMailFolder()
{
  MOZ_COUNT_DTOR(nsImapMailFolder);

  NS_IF_RELEASE(m_moveCoalescer);
  delete m_folderACL;
    
  // cleanup any pending request
  delete m_pendingPlaybackReq;
}

NS_IMPL_ADDREF_INHERITED(nsImapMailFolder, nsMsgDBFolder)
NS_IMPL_RELEASE_INHERITED(nsImapMailFolder, nsMsgDBFolder)
NS_IMPL_QUERY_HEAD(nsImapMailFolder)
    NS_IMPL_QUERY_BODY(nsIMsgImapMailFolder)
    NS_IMPL_QUERY_BODY(nsICopyMessageListener)
    NS_IMPL_QUERY_BODY(nsIImapMailFolderSink)
    NS_IMPL_QUERY_BODY(nsIImapMessageSink)
    NS_IMPL_QUERY_BODY(nsIUrlListener)
    NS_IMPL_QUERY_BODY(nsIMsgFilterHitNotify)
NS_IMPL_QUERY_TAIL_INHERITING(nsMsgDBFolder)

nsresult nsImapMailFolder::AddDirectorySeparator(nsILocalFile *path)
{
  if (mURI.Equals(kImapRootURI))
  {
    // don't concat the full separator with .sbd
  }
  else
  {
    // see if there's a dir with the same name ending with .sbd
    nsAutoString leafName;
    path->GetLeafName(leafName);
    leafName.Append(NS_LITERAL_STRING(FOLDER_SUFFIX));
    path->SetLeafName(leafName);
  }

  return NS_OK;
}

static bool
nsShouldIgnoreFile(nsString& name)
{
  PRInt32 len = name.Length();
  if (len > 4 && name.RFind(SUMMARY_SUFFIX, true) == len - 4)
  {
    name.SetLength(len-4); // truncate the string
    return false;
  }
  return true;
}

nsresult nsImapMailFolder::CreateChildFromURI(const nsCString &uri, nsIMsgFolder **folder)
{
  nsImapMailFolder *newFolder = new nsImapMailFolder;
  if (!newFolder)
    return NS_ERROR_OUT_OF_MEMORY;
  newFolder->Init(uri.get());
  NS_ADDREF(*folder = newFolder);
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::AddSubfolder(const nsAString& aName, nsIMsgFolder** aChild)
{
  NS_ENSURE_ARG_POINTER(aChild);

  PRInt32 flags = 0;
  nsresult rv;
  nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
  NS_ENSURE_SUCCESS(rv,rv);

  nsCAutoString uri(mURI);
  uri.Append('/');

  // If AddSubFolder starts getting called for folders other than virtual folders,
  // we'll have to do convert those names to modified utf-7. For now, the account manager code
  // that loads the virtual folders for each account, expects utf8 not modified utf-7.
  nsCAutoString escapedName;
  rv = NS_MsgEscapeEncodeURLPath(aName, escapedName);
  NS_ENSURE_SUCCESS(rv, rv);

  uri += escapedName.get();

  nsCOMPtr <nsIMsgFolder> msgFolder;
  rv = GetChildWithURI(uri, false/*deep*/, true /*case Insensitive*/, getter_AddRefs(msgFolder));
  if (NS_SUCCEEDED(rv) && msgFolder)
    return NS_MSG_FOLDER_EXISTS;

  nsCOMPtr<nsIRDFResource> res;
  rv = rdf->GetResource(uri, getter_AddRefs(res));
  if (NS_FAILED(rv))
    return rv;

  nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(res, &rv));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr <nsILocalFile> path;
  rv = CreateDirectoryForFolder(getter_AddRefs(path));
  NS_ENSURE_SUCCESS(rv, rv);

  folder->GetFlags((PRUint32 *)&flags);

  flags |= nsMsgFolderFlags::Mail;

  nsCOMPtr<nsIImapIncomingServer> imapServer;
  GetImapIncomingServer(getter_AddRefs(imapServer));
  if (imapServer)
  {
    bool setNewFoldersForOffline = false;
    rv = imapServer->GetOfflineDownload(&setNewFoldersForOffline);
    if (NS_SUCCEEDED(rv) && setNewFoldersForOffline)
      flags |= nsMsgFolderFlags::Offline;
  }

  folder->SetParent(this);

  folder->SetFlags(flags);

  mSubFolders.AppendObject(folder);
  folder.swap(*aChild);

  nsCOMPtr <nsIMsgImapMailFolder> imapChild = do_QueryInterface(*aChild);
  if (imapChild)
  {
    imapChild->SetOnlineName(NS_LossyConvertUTF16toASCII(aName));
    imapChild->SetHierarchyDelimiter(m_hierarchyDelimiter);
  }
  NotifyItemAdded(*aChild);
  return rv;
}

nsresult nsImapMailFolder::AddSubfolderWithPath(nsAString& name, nsILocalFile *dbPath,
                                             nsIMsgFolder **child, bool brandNew)
{
  NS_ENSURE_ARG_POINTER(child);

  nsresult rv;
  nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);
  PRInt32 flags = 0;

  nsCAutoString uri(mURI);
  uri.Append('/');
  AppendUTF16toUTF8(name, uri);

  bool isServer;
  rv = GetIsServer(&isServer);
  NS_ENSURE_SUCCESS(rv, rv);

  bool isInbox = isServer && name.LowerCaseEqualsLiteral("inbox");

  //will make sure mSubFolders does not have duplicates because of bogus msf files.
  nsCOMPtr <nsIMsgFolder> msgFolder;
  rv = GetChildWithURI(uri, false/*deep*/, isInbox /*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));
  NS_ENSURE_SUCCESS(rv, rv);

  folder->SetFilePath(dbPath);
  nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  folder->GetFlags((PRUint32 *)&flags);
  folder->SetParent(this);
  flags |= nsMsgFolderFlags::Mail;

  PRInt32 pFlags;
  GetFlags ((PRUint32 *) &pFlags);
  bool isParentInbox = pFlags & nsMsgFolderFlags::Inbox;

  nsCOMPtr<nsIImapIncomingServer> imapServer;
  rv = GetImapIncomingServer(getter_AddRefs(imapServer));
  NS_ENSURE_SUCCESS(rv, rv);

  //Only set these if these are top level children or parent is inbox
  if (isInbox)
    flags |= nsMsgFolderFlags::Inbox;
  else if (isServer || isParentInbox)
  {
    nsMsgImapDeleteModel deleteModel;
    imapServer->GetDeleteModel(&deleteModel);
    if (deleteModel == nsMsgImapDeleteModels::MoveToTrash)
    {
      nsAutoString trashName;
      GetTrashFolderName(trashName);
      if (name.Equals(trashName))
        flags |= nsMsgFolderFlags::Trash;
    }
  }

  //special case:
  // Make the folder offline if it is newly created and offline_download pref is true.
  if (brandNew)
  {
    bool setNewFoldersForOffline = false;
    rv = imapServer->GetOfflineDownload(&setNewFoldersForOffline);
    if (NS_SUCCEEDED(rv) && setNewFoldersForOffline)
      flags |= nsMsgFolderFlags::Offline;
  }

  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;

  if (folder)
    mSubFolders.AppendObject(folder);
  folder.swap(*child);
  return rv;
}

nsresult nsImapMailFolder::CreateSubFolders(nsILocalFile *path)
{
  nsresult rv = NS_OK;
  nsAutoString currentFolderNameStr;    // online name
  nsAutoString currentFolderDBNameStr;  // possibly munged name
  nsCOMPtr<nsIMsgFolder> child;
  nsCOMPtr<nsIMsgIncomingServer> server;
  rv = GetServer(getter_AddRefs(server));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr <nsISimpleEnumerator> children;
  rv = path->GetDirectoryEntries(getter_AddRefs(children));
  bool more = false;
  if (children)
    children->HasMoreElements(&more);
  nsCOMPtr<nsIFile> dirEntry;

  while (more)
  {
    rv = children->GetNext((nsISupports**) getter_AddRefs(dirEntry));
    if (NS_FAILED(rv))
      break;
    rv = children->HasMoreElements(&more);
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr <nsILocalFile> currentFolderPath = do_QueryInterface(dirEntry);
    currentFolderPath->GetLeafName(currentFolderNameStr);
    if (nsShouldIgnoreFile(currentFolderNameStr))
      continue;

    // OK, here we need to get the online name from the folder cache if we can.
    // If we can, use that to create the sub-folder
    nsCOMPtr <nsIMsgFolderCacheElement> cacheElement;
    nsCOMPtr <nsILocalFile> curFolder = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr <nsILocalFile> dbFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    dbFile->InitWithFile(currentFolderPath);
    curFolder->InitWithFile(currentFolderPath);
    // don't strip off the .msf in currentFolderPath.
    currentFolderPath->SetLeafName(currentFolderNameStr);
    currentFolderDBNameStr = currentFolderNameStr;
    nsAutoString utf7LeafName = currentFolderNameStr;

    if (curFolder)
    {
      rv = GetFolderCacheElemFromFile(dbFile, getter_AddRefs(cacheElement));
      if (NS_SUCCEEDED(rv) && cacheElement)
      {
        nsCString onlineFullUtf7Name;

        PRUint32 folderFlags;
        rv = cacheElement->GetInt32Property("flags", (PRInt32 *) &folderFlags);
        if (NS_SUCCEEDED(rv) && folderFlags & nsMsgFolderFlags::Virtual) //ignore virtual folders
          continue;
        PRInt32 hierarchyDelimiter;
        rv = cacheElement->GetInt32Property("hierDelim", &hierarchyDelimiter);
        if (NS_SUCCEEDED(rv) && hierarchyDelimiter == kOnlineHierarchySeparatorUnknown)
        {
          currentFolderPath->Remove(false);
          continue; // blow away .msf files for folders with unknown delimiter.
        }
        rv = cacheElement->GetStringProperty("onlineName", onlineFullUtf7Name);
        if (NS_SUCCEEDED(rv) && !onlineFullUtf7Name.IsEmpty())
        {
          CopyMUTF7toUTF16(onlineFullUtf7Name, currentFolderNameStr);
          char delimiter = 0;
          GetHierarchyDelimiter(&delimiter);
          PRInt32 leafPos = currentFolderNameStr.RFindChar(delimiter);
          if (leafPos > 0)
            currentFolderNameStr.Cut(0, leafPos + 1);

          // take the utf7 full online name, and determine the utf7 leaf name
          CopyASCIItoUTF16(onlineFullUtf7Name, utf7LeafName);
          leafPos = utf7LeafName.RFindChar(delimiter);
          if (leafPos > 0)
            utf7LeafName.Cut(0, leafPos + 1);
        }
      }
    }
      // make the imap folder remember the file spec it was created with.
    nsCOMPtr <nsILocalFile> msfFilePath = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    msfFilePath->InitWithFile(currentFolderPath);
    if (NS_SUCCEEDED(rv) && msfFilePath)
    {
      // leaf name is the db name w/o .msf (nsShouldIgnoreFile strips it off)
      // so this trims the .msf off the file spec.
      msfFilePath->SetLeafName(currentFolderDBNameStr);
    }
    // use the utf7 name as the uri for the folder.
    AddSubfolderWithPath(utf7LeafName, msfFilePath, 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);
      child->SetMsgDatabase(nsnull);
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::GetSubFolders(nsISimpleEnumerator **aResult)
{
  bool isServer;
  nsresult rv = GetIsServer(&isServer);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!m_initialized)
  {
    nsCOMPtr<nsILocalFile> pathFile;
    rv = GetFilePath(getter_AddRefs(pathFile));
    if (NS_FAILED(rv)) return rv;

    // host directory does not need .sbd tacked on
    if (!isServer)
    {
      rv = AddDirectorySeparator(pathFile);
      if(NS_FAILED(rv)) return rv;
    }

    m_initialized = 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 = nsMsgFolderFlags::Mail;
    bool isDirectory = false;
    pathFile->IsDirectory(&isDirectory);
    if (isDirectory)
    {
        newFlags |= (nsMsgFolderFlags::Directory | nsMsgFolderFlags::Elided);
        if (!mIsServer)
          SetFlag(newFlags);
        rv = CreateSubFolders(pathFile);
    }
    if (isServer)
    {
      nsCOMPtr <nsIMsgFolder> inboxFolder;

      GetFolderWithFlags(nsMsgFolderFlags::Inbox, getter_AddRefs(inboxFolder));
      if (!inboxFolder)
      {
        // create an inbox if we don't have one.
        CreateClientSubfolderInfo(NS_LITERAL_CSTRING("INBOX"), kOnlineHierarchySeparatorUnknown, 0, true);
      }
    }

    PRInt32 count = mSubFolders.Count();
    for (PRInt32 i = 0; i < count; i++)
      mSubFolders[i]->GetSubFolders(nsnull);

    UpdateSummaryTotals(false);
    if (NS_FAILED(rv)) return rv;
  }

  return aResult ? NS_NewArrayEnumerator(aResult, mSubFolders) : NS_ERROR_NULL_POINTER;
}

//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()
{
  nsresult rv = NS_OK;
  if (!mDatabase)
  {
    nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    // Create the database, blowing it away if it needs to be rebuilt
    rv = msgDBService->OpenFolderDB(this, false, getter_AddRefs(mDatabase));
    if (NS_FAILED(rv))
      rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));

    NS_ENSURE_SUCCESS(rv, rv);

    // UpdateNewMessages/UpdateSummaryTotals can null mDatabase, so we save a local copy
    nsCOMPtr<nsIMsgDatabase> database(mDatabase);
    UpdateNewMessages();
    if(mAddListener)
      database->AddListener(this);
    UpdateSummaryTotals(true);
    mDatabase = database;
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::UpdateFolder(nsIMsgWindow * inMsgWindow)
{
  return UpdateFolderWithListener(inMsgWindow, nsnull);
}

NS_IMETHODIMP nsImapMailFolder::UpdateFolderWithListener(nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener)
{
  nsresult rv;
  bool selectFolder = false;

  // If this is the inbox, filters will be applied. Otherwise, we test the
  // inherited folder property "applyIncomingFilters" (which defaults to empty).
  // If this inherited property has the string value "true", we will apply
  // filters even if this is not the inbox folder.
  nsCString applyIncomingFilters;
  GetInheritedStringProperty("applyIncomingFilters", applyIncomingFilters);
  m_applyIncomingFilters = applyIncomingFilters.EqualsLiteral("true");

  if (mFlags & nsMsgFolderFlags::Inbox || m_applyIncomingFilters)
  {
    if (!m_filterList)
      rv = GetFilterList(aMsgWindow, getter_AddRefs(m_filterList));
    // if there's no msg window, but someone is updating the inbox, we're
    // doing something biff-like, and may download headers, so make biff notify.
    if (!aMsgWindow && mFlags & nsMsgFolderFlags::Inbox)
      SetPerformingBiff(true);
  }

  if (m_filterList)
  {
    nsCOMPtr<nsIMsgIncomingServer> server;
    rv = GetServer(getter_AddRefs(server));
    NS_ENSURE_SUCCESS(rv, rv);

    bool canFileMessagesOnServer = true;
    rv = server->GetCanFileMessagesOnServer(&canFileMessagesOnServer);
    // the mdn filter is for filing return receipts into the sent folder
    // some servers (like AOL mail servers)
    // can't file to the sent folder, so we don't add the filter for those servers
    if (canFileMessagesOnServer)
    {
      rv = server->ConfigureTemporaryFilters(m_filterList);
      NS_ENSURE_SUCCESS(rv, rv);
    }

    // If a body filter is enabled for an offline folder, delay the filter
    // application until after message has been downloaded.
    m_filterListRequiresBody = false;

    if (mFlags & nsMsgFolderFlags::Offline)
    {
      nsCOMPtr<nsIMsgFilterService> filterService =
        do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv);
      PRUint32 filterCount = 0;
      m_filterList->GetFilterCount(&filterCount);
      for (PRUint32 index = 0;
           index < filterCount && !m_filterListRequiresBody;
           ++index)
      {
        nsCOMPtr<nsIMsgFilter> filter;
        m_filterList->GetFilterAt(index, getter_AddRefs(filter));
        if (!filter)
          continue;
        nsMsgFilterTypeType filterType;
        filter->GetFilterType(&filterType);
        if (!(filterType & nsMsgFilterType::Incoming))
          continue;
        bool enabled = false;
        filter->GetEnabled(&enabled);
        if (!enabled)
          continue;
        nsCOMPtr<nsISupportsArray> searchTerms;
        PRUint32 numSearchTerms = 0;
        filter->GetSearchTerms(getter_AddRefs(searchTerms));
        if (searchTerms)
          searchTerms->Count(&numSearchTerms);
        for (PRUint32 termIndex = 0;
             termIndex < numSearchTerms && !m_filterListRequiresBody;
             termIndex++)
        {
          nsCOMPtr<nsIMsgSearchTerm> term;
          rv = searchTerms->QueryElementAt(termIndex,
                                           NS_GET_IID(nsIMsgSearchTerm),
                                           getter_AddRefs(term));
          nsMsgSearchAttribValue attrib;
          rv = term->GetAttrib(&attrib);
          NS_ENSURE_SUCCESS(rv, rv);
          if (attrib == nsMsgSearchAttrib::Body)
            m_filterListRequiresBody = true;
          else if (attrib == nsMsgSearchAttrib::Custom)
          {
            nsCAutoString customId;
            rv = term->GetCustomId(customId);
            nsCOMPtr<nsIMsgSearchCustomTerm> customTerm;
            if (NS_SUCCEEDED(rv) && filterService)
              rv = filterService->GetCustomTerm(customId,
                                                getter_AddRefs(customTerm));
            bool needsBody = false;
            if (NS_SUCCEEDED(rv) && customTerm)
              rv = customTerm->GetNeedsBody(&needsBody);
            if (NS_SUCCEEDED(rv) && needsBody)
              m_filterListRequiresBody = true;
          }
        }

        // Also check if filter actions need the body, as this
        //  is supported in custom actions.
        nsCOMPtr<nsISupportsArray> actionList;
        filter->GetActionList(getter_AddRefs(actionList));
        PRUint32 numActions = 0;
        if (actionList)
          actionList->Count(&numActions);
        for (PRUint32 actionIndex = 0;
             actionIndex < numActions && !m_filterListRequiresBody;
             actionIndex++)
        {
          nsCOMPtr<nsIMsgRuleAction> action(do_QueryElementAt(actionList,
                                                              actionIndex,
                                                              &rv));
          if (NS_FAILED(rv) || !action)
            continue;
          nsCOMPtr<nsIMsgFilterCustomAction> customAction;
          rv = action->GetCustomAction(getter_AddRefs(customAction));
          if (NS_FAILED(rv) || !customAction)
            continue;
          bool needsBody = false;
          customAction->GetNeedsBody(&needsBody);
          if (needsBody)
            m_filterListRequiresBody = true;
        }
      }
    }
  }

  selectFolder = true;

  bool isServer;
  rv = GetIsServer(&isServer);
  if (NS_SUCCEEDED(rv) && isServer)
  {
    if (!m_haveDiscoveredAllFolders)
    {
      bool hasSubFolders = false;
      GetHasSubFolders(&hasSubFolders);
      if (!hasSubFolders)
      {
        rv = CreateClientSubfolderInfo(NS_LITERAL_CSTRING("Inbox"), kOnlineHierarchySeparatorUnknown,0, false);
        NS_ENSURE_SUCCESS(rv, rv);
      }
      m_haveDiscoveredAllFolders = true;
    }
    selectFolder = false;
  }
  rv = GetDatabase();
  if (NS_FAILED(rv))
  {
    ThrowAlertMsg("errorGettingDB", aMsgWindow);
    return rv;
  }
  bool canOpenThisFolder = true;
  GetCanOpenFolder(&canOpenThisFolder);

  bool hasOfflineEvents = false;
  GetFlag(nsMsgFolderFlags::OfflineEvents, &hasOfflineEvents);

  if (!WeAreOffline())
  {
    if (hasOfflineEvents)
    {
      // hold a reference to the offline sync object. If ProcessNextOperation
      // runs a url, a reference will be added to it. Otherwise, it will get
      // destroyed when the refptr goes out of scope.
      nsRefPtr<nsImapOfflineSync> goOnline = new nsImapOfflineSync(aMsgWindow, this, this);
      if (goOnline)
      {
        m_urlListener = aUrlListener;
        return goOnline->ProcessNextOperation();
      }
    }
  }

  // Check it we're password protecting the local store.
  if (!PromptForMasterPasswordIfNecessary())
    return NS_ERROR_FAILURE;

  if (!canOpenThisFolder)
    selectFolder = false;
  // don't run select if we can't select the folder...
  if (NS_SUCCEEDED(rv) && !m_urlRunning && selectFolder)
  {
    nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr <nsIURI> url;
    rv = imapService->SelectFolder(m_thread, this, m_urlListener, aMsgWindow, getter_AddRefs(url));
    if (NS_SUCCEEDED(rv))
    {
      m_urlRunning = true;
      m_updatingFolder = true;
    }
    if (url)
    {
      nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(url, &rv);
      NS_ENSURE_SUCCESS(rv, rv);
      mailnewsUrl->RegisterListener(this);
      m_urlListener = aUrlListener;
    }
    switch (rv)
    {
      case NS_MSG_ERROR_OFFLINE:
        if (aMsgWindow)
          AutoCompact(aMsgWindow);
        // note fall through to next case.
      case NS_BINDING_ABORTED:
        rv = NS_OK;
        NotifyFolderEvent(mFolderLoadedAtom);
        break;
      default:
        break;
    }
  }
  else if (NS_SUCCEEDED(rv))  // tell the front end that the folder is loaded if we're not going to
  {                           // actually run a url.
    if (!m_updatingFolder)    // if we're already running an update url, we'll let that one send the folder loaded
      NotifyFolderEvent(mFolderLoadedAtom);
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::GetMessages(nsISimpleEnumerator* *result)
{
  NS_ENSURE_ARG_POINTER(result);
  if (!mDatabase)
    GetDatabase();
  if (mDatabase)
    return mDatabase->EnumerateMessages(result);
  return NS_ERROR_UNEXPECTED;
}

NS_IMETHODIMP nsImapMailFolder::CreateSubfolder(const nsAString& folderName, nsIMsgWindow *msgWindow)
{
  NS_ENSURE_TRUE(!folderName.IsEmpty(), NS_ERROR_FAILURE);
  nsresult rv;
  nsAutoString trashName;
  GetTrashFolderName(trashName);
  if ( folderName.Equals(trashName))   // Trash , a special folder
  {
    ThrowAlertMsg("folderExists", msgWindow);
    return NS_MSG_FOLDER_EXISTS;
  }
  else if (mIsServer && folderName.LowerCaseEqualsLiteral("inbox"))  // 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);

  return imapService->CreateFolder(m_thread, this, folderName, this, nsnull);
}

NS_IMETHODIMP nsImapMailFolder::CreateClientSubfolderInfo(const nsACString& folderName,
                                                          char hierarchyDelimiter,
                                                          PRInt32 flags,
                                                          bool suppressNotification)
{
  nsresult rv = NS_OK;

  //Get a directory based on our current path.
  nsCOMPtr <nsILocalFile> path;
  rv = CreateDirectoryForFolder(getter_AddRefs(path));
  if(NS_FAILED(rv))
    return rv;

  NS_ConvertASCIItoUTF16 leafName(folderName);
  nsAutoString folderNameStr;
  nsAutoString parentName = leafName;
  // use RFind, because folder can start with a delimiter and
  // not be a leaf folder.
  PRInt32 folderStart = leafName.RFindChar('/');
  if (folderStart > 0)
  {
    nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsIRDFResource> res;
    nsCOMPtr<nsIMsgImapMailFolder> parentFolder;
    nsCAutoString uri (mURI);
    leafName.Assign(Substring(parentName, folderStart + 1));
    parentName.SetLength(folderStart);

    rv = CreateDirectoryForFolder(getter_AddRefs(path));
    if (NS_FAILED(rv))
      return rv;
    uri.Append('/');
    uri.Append(NS_LossyConvertUTF16toASCII(parentName));
    rv = rdf->GetResource(uri, getter_AddRefs(res));
    if (NS_FAILED(rv))
      return rv;
    parentFolder = do_QueryInterface(res, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCAutoString leafnameC;
    LossyCopyUTF16toASCII(leafName, leafnameC);
    return parentFolder->CreateClientSubfolderInfo(leafnameC, hierarchyDelimiter,flags, suppressNotification);
  }

  // if we get here, it's really a leaf, and "this" is the parent.
  folderNameStr = leafName;

  // Create an empty database for this mail folder, set its name from the user
  nsCOMPtr<nsIMsgDatabase> mailDBFactory;
  nsCOMPtr<nsIMsgFolder> child;

  nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIMsgDatabase> unusedDB;
  nsCOMPtr <nsILocalFile> dbFile;

  // warning, path will be changed
  rv = CreateFileForDB(folderNameStr, path, getter_AddRefs(dbFile));
  NS_ENSURE_SUCCESS(rv,rv);

  //Now let's create the actual new folder
  rv = AddSubfolderWithPath(folderNameStr, dbFile, getter_AddRefs(child), true);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = msgDBService->OpenMailDBFromFile(dbFile, child, true, true,
                                        getter_AddRefs(unusedDB));
  if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
    rv = NS_OK;

  if (NS_SUCCEEDED(rv) && unusedDB)
  {
  //need to set the folder name
    nsCOMPtr <nsIDBFolderInfo> folderInfo;
    rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
    nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(child, &rv);
    if (NS_SUCCEEDED(rv))
    {
      nsCAutoString onlineName(m_onlineFolderName);
      if (!onlineName.IsEmpty())
        onlineName.Append(hierarchyDelimiter);
      onlineName.Append(NS_LossyConvertUTF16toASCII(folderNameStr));
      imapFolder->SetVerifiedAsOnlineFolder(true);
      imapFolder->SetOnlineName(onlineName);
      imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
      imapFolder->SetBoxFlags(flags);

      child->SetFlag(nsMsgFolderFlags::Elided);
      nsString unicodeName;
      rv = CopyMUTF7toUTF16(nsCString(folderName), unicodeName);
      if (NS_SUCCEEDED(rv))
        child->SetPrettyName(unicodeName);

      // store the online name as the mailbox name in the db folder info
      // I don't think anyone uses the mailbox name, so we'll use it
      // to restore the online name when blowing away an imap db.
      if (folderInfo)
        folderInfo->SetMailboxName(NS_ConvertASCIItoUTF16(onlineName));
    }

    unusedDB->SetSummaryValid(true);
    unusedDB->Commit(nsMsgDBCommitType::kLargeCommit);
    unusedDB->Close(true);
    // don't want to hold onto this newly created db.
    child->SetMsgDatabase(nsnull);
  }

  if (!suppressNotification)
  {
    nsCOMPtr <nsIAtom> folderCreateAtom;
    if(NS_SUCCEEDED(rv) && child)
    {
      NotifyItemAdded(child);
      folderCreateAtom = MsgGetAtom("FolderCreateCompleted");
      child->NotifyFolderEvent(folderCreateAtom);
      nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
      if (notifier)
        notifier->NotifyFolderAdded(child);
    }
    else
    {
      folderCreateAtom = MsgGetAtom("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);
  return imapService->ListFolder(m_thread, this, this, nsnull);
}

NS_IMETHODIMP nsImapMailFolder::RemoveSubFolder (nsIMsgFolder *which)
{
  nsresult rv;
  nsCOMPtr<nsIMutableArray> folders(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
  NS_ENSURE_TRUE(folders, rv);
  nsCOMPtr<nsISupports> folderSupport = do_QueryInterface(which, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  folders->AppendElement(folderSupport, false);
  rv = nsMsgDBFolder::DeleteSubFolders(folders, nsnull);
  which->Delete();
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::CreateStorageIfMissing(nsIUrlListener* urlListener)
{
  nsresult rv = NS_OK;
  nsCOMPtr <nsIMsgFolder> msgParent;
  GetParent(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.SetLength(leafPos);
      // get the corresponding RDF resource
      // RDF will create the folder resource if it doesn't already exist
      nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
      NS_ENSURE_SUCCESS(rv, rv);
      nsCOMPtr<nsIRDFResource> resource;
      rv = rdf->GetResource(parentName, getter_AddRefs(resource));
      if (NS_FAILED(rv)) return rv;
      msgParent = do_QueryInterface(resource, &rv);
    }
  }
  if (msgParent)
  {
    nsString folderName;
    GetName(folderName);
    nsresult rv;
    nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr <nsIURI> uri;
    imapService->EnsureFolderExists(m_thread, msgParent, folderName, urlListener, getter_AddRefs(uri));
  }
  return rv;
}


NS_IMETHODIMP nsImapMailFolder::GetVerifiedAsOnlineFolder(bool *aVerifiedAsOnlineFolder)
{
  NS_ENSURE_ARG_POINTER(aVerifiedAsOnlineFolder);
  *aVerifiedAsOnlineFolder = m_verifiedAsOnlineFolder;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::SetVerifiedAsOnlineFolder(bool aVerifiedAsOnlineFolder)
{
  m_verifiedAsOnlineFolder = aVerifiedAsOnlineFolder;
  // mark ancestors as verified as well
  if (aVerifiedAsOnlineFolder)
  {
    nsCOMPtr<nsIMsgFolder> parent;
    do
    {
      GetParent(getter_AddRefs(parent));
      if (parent)
      {
        nsCOMPtr<nsIMsgImapMailFolder> imapParent = do_QueryInterface(parent);
        if (imapParent)
        {
          bool verifiedOnline;
          imapParent->GetVerifiedAsOnlineFolder(&verifiedOnline);
          if (verifiedOnline)
            break;
          imapParent->SetVerifiedAsOnlineFolder(true);
        }
      }
    }
    while (parent);
  }
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::GetOnlineDelimiter(char** onlineDelimiter)
{
  NS_ENSURE_ARG_POINTER(onlineDelimiter);
  char delimiter[2] = {0, 0};
  GetHierarchyDelimiter(delimiter);
  *onlineDelimiter = NS_strdup(delimiter);
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::SetHierarchyDelimiter(char aHierarchyDelimiter)
{
  m_hierarchyDelimiter = aHierarchyDelimiter;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::GetHierarchyDelimiter(char *aHierarchyDelimiter)
{
  NS_ENSURE_ARG_POINTER(aHierarchyDelimiter);
  if (mIsServer)
  {
    // if it's the root folder, we don't know the delimiter. So look at the
    // first child.
    PRInt32 count = mSubFolders.Count();
    if (count > 0)
    {
      nsCOMPtr<nsIMsgImapMailFolder> childFolder(do_QueryInterface(mSubFolders[0]));
      if (childFolder)
      {
        nsresult rv = childFolder->GetHierarchyDelimiter(aHierarchyDelimiter);
        // some code uses m_hierarchyDelimiter directly, so we should set it.
        m_hierarchyDelimiter = *aHierarchyDelimiter;
        return rv;
      }
    }
  }
  ReadDBFolderInfo(false); // update cache first.
  *aHierarchyDelimiter = m_hierarchyDelimiter;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::SetBoxFlags(PRInt32 aBoxFlags)
{
  ReadDBFolderInfo(false);

  m_boxFlags = aBoxFlags;
  PRUint32 newFlags = mFlags;

  newFlags |= nsMsgFolderFlags::ImapBox;

  if (m_boxFlags & kNoinferiors)
    newFlags |= nsMsgFolderFlags::ImapNoinferiors;
  else
    newFlags &= ~nsMsgFolderFlags::ImapNoinferiors;
  if (m_boxFlags & kNoselect)
    newFlags |= nsMsgFolderFlags::ImapNoselect;
  else
    newFlags &= ~nsMsgFolderFlags::ImapNoselect;
  if (m_boxFlags & kPublicMailbox)
    newFlags |= nsMsgFolderFlags::ImapPublic;
  else
    newFlags &= ~nsMsgFolderFlags::ImapPublic;
  if (m_boxFlags & kOtherUsersMailbox)
    newFlags |= nsMsgFolderFlags::ImapOtherUser;
  else
    newFlags &= ~nsMsgFolderFlags::ImapOtherUser;
  if (m_boxFlags & kPersonalMailbox)
    newFlags |= nsMsgFolderFlags::ImapPersonal;
  else
    newFlags &= ~nsMsgFolderFlags::ImapPersonal;

  // The following are all flags returned by XLIST.
  // nsImapIncomingServer::DiscoveryDone checks for these folders.
  if (m_boxFlags & kImapDrafts)
    newFlags |= nsMsgFolderFlags::Drafts;

  if (m_boxFlags & kImapSpam)
    newFlags |= nsMsgFolderFlags::Junk;

  if (m_boxFlags & kImapSent)
    newFlags |= nsMsgFolderFlags::SentMail;

  if (m_boxFlags & kImapInbox)
    newFlags |= nsMsgFolderFlags::Inbox;

  if (m_boxFlags & kImapXListTrash)
  {
    nsCOMPtr<nsIImapIncomingServer> imapServer;
    nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash;
    (void) GetImapIncomingServer(getter_AddRefs(imapServer));
    if (imapServer)
      imapServer->GetDeleteModel(&deleteModel);
    if (deleteModel == nsMsgImapDeleteModels::MoveToTrash)
      newFlags |= nsMsgFolderFlags::Trash;
  }
  // Treat the GMail all mail folder as the archive folder.
  if (m_boxFlags & kImapAllMail)
    newFlags |= nsMsgFolderFlags::Archive;

  SetFlags(newFlags);
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::GetBoxFlags(PRInt32 *aBoxFlags)
{
  NS_ENSURE_ARG_POINTER(aBoxFlags);
  *aBoxFlags = m_boxFlags;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::GetExplicitlyVerify(bool *aExplicitlyVerify)
{
  NS_ENSURE_ARG_POINTER(aExplicitlyVerify);
  *aExplicitlyVerify = m_explicitlyVerify;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::SetExplicitlyVerify(bool aExplicitlyVerify)
{
  m_explicitlyVerify = aExplicitlyVerify;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::GetNoSelect(bool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  return GetFlag(nsMsgFolderFlags::ImapNoselect, aResult);
}

NS_IMETHODIMP nsImapMailFolder::ApplyRetentionSettings()
{
  PRInt32 numDaysToKeepOfflineMsgs = -1;

  // Check if we've limited the offline storage by age.
  nsCOMPtr<nsIImapIncomingServer> imapServer;
  nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
  NS_ENSURE_SUCCESS(rv, rv);
  imapServer->GetAutoSyncMaxAgeDays(&numDaysToKeepOfflineMsgs);

  nsCOMPtr<nsIMsgDatabase> holdDBOpen;
  if (numDaysToKeepOfflineMsgs > 0)
  {
    bool dbWasCached = mDatabase != nsnull;
    rv = GetDatabase();
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr <nsISimpleEnumerator> hdrs;
    rv = mDatabase->EnumerateMessages(getter_AddRefs(hdrs));
    NS_ENSURE_SUCCESS(rv, rv);
    bool hasMore = false;

    PRTime cutOffDay =
      MsgConvertAgeInDaysToCutoffDate(numDaysToKeepOfflineMsgs);

    nsCOMPtr <nsIMsgDBHdr> pHeader;
    // so now cutOffDay is the PRTime cut-off point. Any offline msg with 
    // a date less than that will get marked for pending removal.
    while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && hasMore)
    {
      rv = hdrs->GetNext(getter_AddRefs(pHeader));
      NS_ENSURE_SUCCESS(rv, rv);
      PRUint32 msgFlags;
      PRTime msgDate;
      pHeader->GetFlags(&msgFlags);
      if (msgFlags & nsMsgMessageFlags::Offline)
      {
        pHeader->GetDate(&msgDate);
        MarkPendingRemoval(pHeader, msgDate < cutOffDay);
        // I'm horribly tempted to break out of the loop if we've found
        // a message after the cut-off date, because messages will most likely
        // be in date order in the db, but there are always edge cases.
      }
    }
    if (!dbWasCached)
    {
      holdDBOpen = mDatabase;
      mDatabase = nsnull;
    }
  }
  return nsMsgDBFolder::ApplyRetentionSettings();
}

/**
 * The listener will get called when both the online expunge and the offline
 * store compaction are finished (if the latter is needed).
 */
NS_IMETHODIMP nsImapMailFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
{
  nsresult rv = GetDatabase();
  // now's a good time to apply the retention settings. If we do delete any
  // messages, the expunge is going to have to wait until the delete to
  // finish before it can run, but the multiple-connection protection code
  // should handle that.
  if (mDatabase)
    ApplyRetentionSettings();

  m_urlListener = aListener;
  // We should be able to compact the offline store now that this should
  // just be called by the UI.
  if (aMsgWindow && (mFlags & nsMsgFolderFlags::Offline))
  {
    m_compactingOfflineStore = true;
    CompactOfflineStore(aMsgWindow, this);
  }
  if (WeAreOffline())
    return NS_OK;
  m_expunging = true;
  return Expunge(this, aMsgWindow);
}

NS_IMETHODIMP
nsImapMailFolder::NotifyCompactCompleted()
{
  if (!m_expunging && m_urlListener)
  {
    m_urlListener->OnStopRunningUrl(nsnull, NS_OK);
    m_urlListener = nsnull;
  }
  m_compactingOfflineStore = false;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::MarkPendingRemoval(nsIMsgDBHdr *aHdr, bool aMark)
{
  NS_ENSURE_ARG_POINTER(aHdr);
  PRUint32 offlineMessageSize;
  aHdr->GetOfflineMessageSize(&offlineMessageSize);
  aHdr->SetStringProperty("pendingRemoval", aMark ? "1" : "");
  if (!aMark)
    return NS_OK;
  nsresult rv = GetDatabase();
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
  rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
  NS_ENSURE_SUCCESS(rv, rv);
  return dbFolderInfo->ChangeExpungedBytes(offlineMessageSize);
}

NS_IMETHODIMP nsImapMailFolder::Expunge(nsIUrlListener *aListener,
                                        nsIMsgWindow *aMsgWindow)
{
  nsresult rv;
  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  return imapService->Expunge(m_thread, this, aListener, aMsgWindow, nsnull);
}

NS_IMETHODIMP nsImapMailFolder::CompactAll(nsIUrlListener *aListener,
                                               nsIMsgWindow *aMsgWindow,
                                               bool aCompactOfflineAlso)
{
  nsresult rv;
  nsCOMPtr<nsIMutableArray> folderArray, offlineFolderArray;

  nsCOMPtr<nsIMsgFolder> rootFolder;
  nsCOMPtr<nsISupportsArray> allDescendents;
  rv = GetRootFolder(getter_AddRefs(rootFolder));
  if (NS_SUCCEEDED(rv) && rootFolder)
  {
    allDescendents = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
    NS_ENSURE_TRUE(allDescendents, rv);
    rootFolder->ListDescendents(allDescendents);
    PRUint32 cnt = 0;
    rv = allDescendents->Count(&cnt);
    NS_ENSURE_SUCCESS(rv, rv);
    folderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
    NS_ENSURE_TRUE(folderArray, rv);
    if (aCompactOfflineAlso)
    {
      offlineFolderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
      NS_ENSURE_TRUE(offlineFolderArray, rv);
    }
    for (PRUint32 i = 0; i < cnt; i++)
    {
      nsCOMPtr<nsISupports> supports = dont_AddRef(allDescendents->ElementAt(i));
      nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
      NS_ENSURE_SUCCESS(rv, rv);
      PRUint32 folderFlags;
      folder->GetFlags(&folderFlags);
      if (! (folderFlags & (nsMsgFolderFlags::Virtual | nsMsgFolderFlags::ImapNoselect)))
      {
        rv = folderArray->AppendElement(supports, false);
        if (aCompactOfflineAlso)
          offlineFolderArray->AppendElement(supports, false);
      }
    }
    rv = folderArray->GetLength(&cnt);
    NS_ENSURE_SUCCESS(rv, rv);
    if (cnt == 0)
      return NotifyCompactCompleted();
  }
  nsCOMPtr <nsIMsgFolderCompactor> folderCompactor =
    do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  return folderCompactor->CompactFolders(folderArray, offlineFolderArray,
                                         aListener, aMsgWindow);
}

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_thread, this, aListener, getter_AddRefs(uri));
  if (uri && !aMsgWindow)
  {
    nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(uri, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    // if no msg window, we won't put up error messages (this is almost certainly a biff-inspired status)
    mailNewsUrl->SetSuppressErrorMsgs(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);
    NS_ENSURE_SUCCESS(rv, rv);
    // 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.
    bool empytingOnExit = false;
    accountManager->GetEmptyTrashInProgress(&empytingOnExit);
    if (empytingOnExit)
    {
      nsCOMPtr<nsIImapIncomingServer> imapServer;
      rv = GetImapIncomingServer(getter_AddRefs(imapServer));
      if (imapServer)
      {
        bool isAOLServer = 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(getter_AddRefs(trashDB));
      if (trashDB)
      {
        nsMsgKey fakeKey;
        trashDB->GetNextFakeOfflineMsgKey(&fakeKey);

        nsCOMPtr <nsIMsgOfflineImapOperation> op;
        rv = trashDB->GetOfflineOpForKey(fakeKey, true, getter_AddRefs(op));
        trashFolder->SetFlag(nsMsgFolderFlags::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);
    NS_ENSURE_SUCCESS(rv, rv);
    bool hasSubfolders = false;
    rv = trashFolder->GetHasSubFolders(&hasSubfolders);
    if (hasSubfolders)
    {
      bool confirmDeletion;
      nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
      NS_ENSURE_SUCCESS(rv, rv);

      prefBranch->GetBoolPref("mail.imap.confirm_emptyTrashFolderDeletion", &confirmDeletion);

      if (confirmDeletion)
      {
        nsCOMPtr<nsISimpleEnumerator> enumerator;
        rv = trashFolder->GetSubFolders(getter_AddRefs(enumerator));
        NS_ENSURE_SUCCESS(rv, rv);

        nsCOMPtr<nsIPromptService> promptService(do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv));
        NS_ENSURE_SUCCESS(rv, rv);

        nsCOMPtr<nsIDOMWindow> parentWindow;
        if (aMsgWindow)
        {
          nsCOMPtr<nsIDocShell> docShell;
          (void) aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
          parentWindow = do_GetInterface(docShell);
        }

        nsCOMPtr<nsIStringBundle> bundle;
        rv = IMAPGetStringBundle(getter_AddRefs(bundle));
        NS_ENSURE_SUCCESS(rv, rv);

        bool hasMore;
        while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
        {
          PRInt32 dlgResult = -1;
          nsCOMPtr<nsISupports> item;
          rv = enumerator->GetNext(getter_AddRefs(item));
          if (NS_FAILED(rv))
            continue;

          if (confirmDeletion)
          {
            nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(item, &rv));
            if (NS_FAILED(rv))
              continue;
            nsString confirmText;
            nsString folderName;
            folder->GetName(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.
            bool dummyValue = false;
            rv = promptService->ConfirmEx(parentWindow, nsnull, confirmText.get(),
                                        (nsIPromptService::BUTTON_TITLE_OK * nsIPromptService::BUTTON_POS_0) +
                                        (nsIPromptService::BUTTON_TITLE_CANCEL * nsIPromptService::BUTTON_POS_1),
                                          nsnull, nsnull, nsnull, nsnull, &dummyValue, &dlgResult);
          }
          if (NS_SUCCEEDED(rv) && dlgResult == 1)
            return NS_BINDING_ABORTED;
        }
      }
    }
    if (aListener)
      rv = imapService->DeleteAllMessages(m_thread, trashFolder, aListener, nsnull);
    else
    {
      nsCOMPtr<nsIUrlListener> urlListener = do_QueryInterface(trashFolder);
      rv = imapService->DeleteAllMessages(m_thread, 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<nsISimpleEnumerator> enumerator;
      nsCOMPtr<nsISupports> item;
      nsCOMArray<nsIMsgFolder> array;

      rv = trashFolder->GetSubFolders(getter_AddRefs(enumerator));
      NS_ENSURE_SUCCESS(rv, rv);

      bool hasMore;
      while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
      {
        rv = enumerator->GetNext(getter_AddRefs(item));
        if (NS_SUCCEEDED(rv))
        {
          nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(item, &rv));
          if (NS_SUCCEEDED(rv))
            array.AppendObject(folder);
        }
      }
      for (PRInt32 i = array.Count() - 1; i >= 0; i--)
      {
        trashFolder->PropagateDelete(array[i], true, aMsgWindow);
        // Remove the object, presumably to free it up before we delete the next.
        array.RemoveObjectAt(i);
      }
    }

    // The trash folder has effectively been deleted
    nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
    if (notifier)
      notifier->NotifyFolderDeleted(trashFolder);

    return NS_OK;
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::Delete()
{
  nsresult rv;
  if (!mDatabase)
  {
    // Check if anyone has this db open. If so, do a force closed.
    nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    msgDBService->CachedDBForFolder(this, getter_AddRefs(mDatabase));
  }
  if (mDatabase)
  {
    mDatabase->ForceClosed();
    mDatabase = nsnull;
  }

  nsCOMPtr<nsILocalFile> path;
  rv = GetFilePath(getter_AddRefs(path));
  if (NS_SUCCEEDED(rv))
  {
    nsCOMPtr<nsILocalFile> summaryLocation;
    rv = GetSummaryFileLocation(path, getter_AddRefs(summaryLocation));
    if (NS_SUCCEEDED(rv))
    {
      bool exists = false;
      rv = summaryLocation->Exists(&exists);
      if (NS_SUCCEEDED(rv) && exists)
      {
        rv = summaryLocation->Remove(false);
        if (NS_FAILED(rv))
          NS_WARNING("failed to remove imap summary file");
      }
    }
  }
  if (mPath)
    mPath->Remove(false);
  // should notify nsIMsgFolderListeners about the folder getting deleted...
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::Rename (const nsAString& newName, nsIMsgWindow *msgWindow)
{
  if (mFlags & nsMsgFolderFlags::Virtual)
    return nsMsgDBFolder::Rename(newName, msgWindow);
  nsresult rv;
  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
        };
        nsString alertString;
        rv = bundle->FormatStringFromID(IMAP_SPECIAL_CHAR,
                                      formatStrings, 1,
                                      getter_Copies(alertString));
        nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
        if (dialog && !alertString.IsEmpty())
          dialog->Alert(nsnull, alertString.get());
      }
    }
    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_thread, this, newName, this, msgWindow, nsnull);
}

NS_IMETHODIMP nsImapMailFolder::RecursiveCloseActiveConnections(nsIImapIncomingServer *incomingImapServer)
{
  NS_ENSURE_ARG(incomingImapServer);

  nsCOMPtr<nsIMsgImapMailFolder> folder;
  PRInt32 count = mSubFolders.Count();
  for (PRInt32 i = 0; i < count; i++)
  {
    folder = do_QueryInterface(mSubFolders[i]);
    if (folder)
      folder->RecursiveCloseActiveConnections(incomingImapServer);

    incomingImapServer->CloseConnectionForFolder(mSubFolders[i]);
  }
  return NS_OK;
}

// this is called *after* we've done the rename on the server.
NS_IMETHODIMP nsImapMailFolder::PrepareToRename()
{
  nsCOMPtr<nsIMsgImapMailFolder> folder;
  PRInt32 count = mSubFolders.Count();
  for (PRInt32 i = 0; i < count; i++)
  {
    folder = do_QueryInterface(mSubFolders[i]);
    if (folder)
      folder->PrepareToRename();
  }

  SetOnlineName(EmptyCString());
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::RenameLocal(const nsACString& newName, nsIMsgFolder *parent)
{
  // XXX Here it's assumed that IMAP folder names are stored locally
  // in modified UTF-7 (ASCII-only) as is stored remotely.  If we ever change
  // this, we have to work with nsString instead of nsCString
  // (ref. bug 264071)
  nsCAutoString leafname(newName);
  nsCAutoString parentName;
  // newName always in the canonical form "greatparent/parentname/leafname"
  PRInt32 leafpos = leafname.RFindChar('/');
  if (leafpos >0)
      leafname.Cut(0, leafpos+1);
  m_msgParser = nsnull;
  PrepareToRename();
  CloseAndBackupFolderDB(leafname);

  nsresult rv = NS_OK;
  nsCOMPtr<nsILocalFile> oldPathFile;
  rv = GetFilePath(getter_AddRefs(oldPathFile));
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsILocalFile> parentPathFile;
  rv = parent->GetFilePath(getter_AddRefs(parentPathFile));
  NS_ENSURE_SUCCESS(rv,rv);

  bool isDirectory = false;
  parentPathFile->IsDirectory(&isDirectory);
  if (!isDirectory)
  AddDirectorySeparator(parentPathFile);

  nsCOMPtr <nsILocalFile> dirFile;

  PRInt32 count = mSubFolders.Count();
  if (count > 0)
    rv = CreateDirectoryForFolder(getter_AddRefs(dirFile));

  nsCOMPtr <nsILocalFile> oldSummaryFile;
  rv = GetSummaryFileLocation(oldPathFile, getter_AddRefs(oldSummaryFile));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCAutoString newNameStr;
  oldSummaryFile->Remove(false);
  if (count > 0)
  {
    newNameStr = leafname;
    NS_MsgHashIfNecessary(newNameStr);
    newNameStr += ".sbd";
    nsCAutoString leafName;
    dirFile->GetNativeLeafName(leafName);
    if (!leafName.Equals(newNameStr))
      return dirFile->MoveToNative(nsnull, newNameStr);      // in case of rename operation leaf names will differ

    parentPathFile->AppendNative(newNameStr);    //only for move we need to progress further in case the parent differs
    bool isDirectory = false;
    parentPathFile->IsDirectory(&isDirectory);
    if (!isDirectory)
      parentPathFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
    else
      NS_ERROR("Directory already exists.");
    rv = RecursiveCopy(dirFile, parentPathFile);
    NS_ENSURE_SUCCESS(rv,rv);
    dirFile->Remove(true);                         // moving folders
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::GetPrettyName(nsAString& prettyName)
{
  return GetName(prettyName);
}

NS_IMETHODIMP nsImapMailFolder::UpdateSummaryTotals(bool force)
{
  // bug 72871 inserted the mIsServer check for IMAP
  return mIsServer? NS_OK : nsMsgDBFolder::UpdateSummaryTotals(force);
}

NS_IMETHODIMP nsImapMailFolder::GetDeletable (bool *deletable)
{
  NS_ENSURE_ARG_POINTER(deletable);

  bool isServer;
  GetIsServer(&isServer);

  *deletable = !(isServer || (mFlags & (nsMsgFolderFlags::Inbox |
    nsMsgFolderFlags::Drafts | nsMsgFolderFlags::Templates |
    nsMsgFolderFlags::SentMail | nsMsgFolderFlags::Archive |
    nsMsgFolderFlags::Junk | nsMsgFolderFlags::Trash)));
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::GetRequiresCleanup(bool *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(bool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = !(mFlags & (nsMsgFolderFlags::ImapNoinferiors | nsMsgFolderFlags::Virtual));

  bool isServer = false;
  GetIsServer(&isServer);
  if (!isServer)
  {
    nsCOMPtr<nsIImapIncomingServer> imapServer;
    nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
    bool dualUseFolders = true;
    if (NS_SUCCEEDED(rv) && imapServer)
      imapServer->GetDualUseFolders(&dualUseFolders);
    if (!dualUseFolders && *aResult)
      *aResult = (mFlags & nsMsgFolderFlags::ImapNoselect);
  }
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::GetCanSubscribe(bool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = false;

  bool isImapServer = 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(nsACString& serverKey)
{
  // look for matching imap folders, then pop folders
  nsCOMPtr<nsIMsgIncomingServer> server;
  nsresult rv = GetServer(getter_AddRefs(server));
  if (NS_SUCCEEDED(rv))
    rv = server->GetKey(serverKey);
  return rv;
}

NS_IMETHODIMP
nsImapMailFolder::GetImapIncomingServer(nsIImapIncomingServer **aImapIncomingServer)
{
  NS_ENSURE_ARG(aImapIncomingServer);
  nsCOMPtr<nsIMsgIncomingServer> server;
  if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
  {
    nsCOMPtr <nsIImapIncomingServer> incomingServer = do_QueryInterface(server);
    incomingServer.swap(*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)
  {
    nsMsgKey msgKey;
    aMessage->GetMessageKey(&msgKey);

    if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Replied)
      StoreImapFlags(kImapMsgAnsweredFlag, true, &msgKey, 1, nsnull);
    else if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Forwarded)
      StoreImapFlags(kImapMsgForwardedFlag, true, &msgKey, 1, nsnull);
  }
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::MarkMessagesRead(nsIArray *messages, bool 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;
    nsTArray<nsMsgKey> keysToMarkRead;
    rv = BuildIdsAndKeyArray(messages, messageIds, keysToMarkRead);
    NS_ENSURE_SUCCESS(rv, rv);

    StoreImapFlags(kImapMsgSeenFlag, markRead, keysToMarkRead.Elements(), keysToMarkRead.Length(), nsnull);
    rv = GetDatabase();
    if (NS_SUCCEEDED(rv))
      mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
  }
  return rv;
}

NS_IMETHODIMP
nsImapMailFolder::SetLabelForMessages(nsIArray *aMessages, nsMsgLabelValue aLabel)
{
  NS_ENSURE_ARG(aMessages);

  nsresult rv = nsMsgDBFolder::SetLabelForMessages(aMessages, aLabel);
  if (NS_SUCCEEDED(rv))
  {
    nsCAutoString messageIds;
    nsTArray<nsMsgKey> keysToLabel;
    nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keysToLabel);
    NS_ENSURE_SUCCESS(rv, rv);
    StoreImapFlags((aLabel << 9), true, keysToLabel.Elements(), keysToLabel.Length(), nsnull);
    rv = GetDatabase();
    if (NS_SUCCEEDED(rv))
      mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
  }
  return rv;
}

NS_IMETHODIMP
nsImapMailFolder::MarkAllMessagesRead(nsIMsgWindow *aMsgWindow)
{
  nsresult rv = GetDatabase();
  if(NS_SUCCEEDED(rv))
  {
    nsMsgKey *thoseMarked;
    PRUint32 numMarked;
    EnableNotifications(allMessageCountNotifications, false, true /*dbBatching*/);
    rv = mDatabase->MarkAllRead(&numMarked, &thoseMarked);
    EnableNotifications(allMessageCountNotifications, true, true /*dbBatching*/);
    if (NS_SUCCEEDED(rv))
    {
      rv = StoreImapFlags(kImapMsgSeenFlag, true, thoseMarked,
                          numMarked, nsnull);
      mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);

      // Setup a undo-state
      if (aMsgWindow)
        rv = AddMarkAllReadUndoAction(aMsgWindow, thoseMarked, numMarked);
      nsMemory::Free(thoseMarked);
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::MarkThreadRead(nsIMsgThread *thread)
{
  nsresult rv = GetDatabase();
  if(NS_SUCCEEDED(rv))
  {
    nsMsgKey *keys;
    PRUint32 numKeys;
    rv = mDatabase->MarkThreadRead(thread, nsnull, &numKeys, &keys);
    if (NS_SUCCEEDED(rv))
    {
      rv = StoreImapFlags(kImapMsgSeenFlag, true, keys, numKeys, nsnull);
      mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
      nsMemory::Free(keys);
    }
  }
  return rv;
}


NS_IMETHODIMP nsImapMailFolder::ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element)
{
  nsresult rv = nsMsgDBFolder::ReadFromFolderCacheElem(element);
  PRInt32 hierarchyDelimiter = kOnlineHierarchySeparatorUnknown;
  nsCString onlineName;

  element->GetInt32Property("boxFlags", &m_boxFlags);
  if (NS_SUCCEEDED(element->GetInt32Property("hierDelim", &hierarchyDelimiter))
      && hierarchyDelimiter != kOnlineHierarchySeparatorUnknown)
    m_hierarchyDelimiter = (char) hierarchyDelimiter;
  rv = element->GetStringProperty("onlineName", onlineName);
  if (NS_SUCCEEDED(rv) && !onlineName.IsEmpty())
    m_onlineFolderName.Assign(onlineName);

  m_aclFlags = -1; // init to invalid value.
  element->GetInt32Property("aclFlags", (PRInt32 *) &m_aclFlags);
  element->GetInt32Property("serverTotal", &m_numServerTotalMessages);
  element->GetInt32Property("serverUnseen", &m_numServerUnseenMessages);
  element->GetInt32Property("serverRecent", &m_numServerRecentMessages);
  element->GetInt32Property("nextUID", &m_nextUID);
  PRInt32 lastSyncTimeInSec;
  if ( NS_FAILED(element->GetInt32Property("lastSyncTimeInSec", (PRInt32 *) &lastSyncTimeInSec)) )
    lastSyncTimeInSec = 0U;

  // make sure that auto-sync state object is created
  InitAutoSyncState();
  m_autoSyncStateObj->SetLastSyncTimeInSec(lastSyncTimeInSec);
  
  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);
  element->SetInt32Property("aclFlags", (PRInt32) m_aclFlags);
  element->SetInt32Property("serverTotal", m_numServerTotalMessages);
  element->SetInt32Property("serverUnseen", m_numServerUnseenMessages);
  element->SetInt32Property("serverRecent", m_numServerRecentMessages);
  if (m_nextUID != nsMsgKey_None)
    element->SetInt32Property("nextUID", m_nextUID);

  // store folder's last sync time
  if (m_autoSyncStateObj)
  {
    PRTime lastSyncTime;
    m_autoSyncStateObj->GetLastSyncTime(&lastSyncTime);
    // store in sec
    element->SetInt32Property("lastSyncTimeInSec", (PRInt32) (lastSyncTime / PR_USEC_PER_SEC));
  }
   
  return rv;
}

NS_IMETHODIMP
nsImapMailFolder::MarkMessagesFlagged(nsIArray *messages, bool 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;
    nsTArray<nsMsgKey> keysToMarkFlagged;
    rv = BuildIdsAndKeyArray(messages, messageIds, keysToMarkFlagged);
    if (NS_FAILED(rv)) return rv;
    rv = StoreImapFlags(kImapMsgFlaggedFlag, markFlagged,  keysToMarkFlagged.Elements(),
                        keysToMarkFlagged.Length(), nsnull);
    mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::SetOnlineName(const nsACString& 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;
    CopyASCIItoUTF16(aOnlineFolderName, onlineName);
    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(nsACString& aOnlineFolderName)
{
  ReadDBFolderInfo(false); // update cache first.
  aOnlineFolderName = m_onlineFolderName;
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **db)
{
  NS_ENSURE_ARG_POINTER (folderInfo);
  NS_ENSURE_ARG_POINTER (db);

  nsresult rv = GetDatabase();
  if (NS_FAILED(rv))
    return rv;

  NS_ADDREF(*db = mDatabase);

  rv = (*db)->GetDBFolderInfo(folderInfo);
  if (NS_FAILED(rv))
    return rv; //GetDBFolderInfo can't return NS_OK if !folderInfo

  nsCString onlineName;
  rv = (*folderInfo)->GetCharProperty("onlineName", onlineName);
  if (NS_FAILED(rv))
    return rv;

  if (!onlineName.IsEmpty())
    m_onlineFolderName.Assign(onlineName);
  else
  {
    nsAutoString autoOnlineName;
    (*folderInfo)->GetMailboxName(autoOnlineName);
    if (autoOnlineName.IsEmpty())
    {
      nsCString uri;
      rv = GetURI(uri);
      NS_ENSURE_SUCCESS(rv, rv);

      nsCString hostname;
      rv = GetHostname(hostname);
      NS_ENSURE_SUCCESS(rv, rv);

      nsCString onlineCName;
      rv = nsImapURI2FullName(kImapRootURI, hostname.get(), uri.get(), getter_Copies(onlineCName));
      if (m_hierarchyDelimiter != '/')
        MsgReplaceChar(onlineCName, '/', m_hierarchyDelimiter);
      m_onlineFolderName.Assign(onlineCName);
      CopyASCIItoUTF16(onlineCName, autoOnlineName);
    }
    (*folderInfo)->SetProperty("onlineName", autoOnlineName);
  }
  return rv;
}

/* static */ nsresult
nsImapMailFolder::BuildIdsAndKeyArray(nsIArray* messages,
                                      nsCString& msgIds,
                                      nsTArray<nsMsgKey>& keyArray)
{
  NS_ENSURE_ARG_POINTER(messages);
  nsresult rv;
  PRUint32 count = 0;
  PRUint32 i;
  rv = messages->GetLength(&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.AppendElement(key);
  }
  return AllocateUidStringFromKeys(keyArray.Elements(), keyArray.Length(), msgIds);
}

static int 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;
    bool lastKey = (nextKey == 0xFFFFFFFF);

    if (lastKey)
      curSequenceEnd = curKey;
    if (nextKey == (PRUint32) curSequenceEnd + 1 && !lastKey)
    {
      curSequenceEnd = nextKey;
      continue;
    }
    else if (curSequenceEnd > startSequence)
    {
      AppendUid(msgIds, startSequence);
      msgIds += ':';
      AppendUid(msgIds,curSequenceEnd);
      if (!lastKey)
        msgIds += ',';
      startSequence = nextKey;
      curSequenceEnd = startSequence;
    }
    else
    {
      startSequence = nextKey;
      curSequenceEnd = startSequence;
      AppendUid(msgIds, keys[keyIndex]);
      if (!lastKey)
        msgIds += ',';
    }
  }
  return rv;
}

nsresult nsImapMailFolder::MarkMessagesImapDeleted(nsTArray<nsMsgKey> *keyArray, bool deleted, nsIMsgDatabase *db)
{
  for (PRUint32 kindex = 0; kindex < keyArray->Length(); kindex++)
  {
    nsMsgKey key = keyArray->ElementAt(kindex);
    db->MarkImapDeleted(key, deleted, nsnull);
  }
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::DeleteMessages(nsIArray *messages,
                                               nsIMsgWindow *msgWindow,
                                               bool deleteStorage, bool isMove,
                                               nsIMsgCopyServiceListener* listener,
                                               bool allowUndo)
{
  // *** jt - assuming delete is move to the trash folder for now
  nsCOMPtr<nsIRDFResource> res;
  nsCAutoString uri;
  bool deleteImmediatelyNoTrash = false;
  nsCAutoString messageIds;
  nsTArray<nsMsgKey> srcKeyArray;
  bool deleteMsgs = true;  //used for toggling delete status - default is true
  nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash;
  imapMessageFlagsType messageFlags = kImapMsgDeletedFlag;

  nsCOMPtr<nsIImapIncomingServer> imapServer;
  nsresult rv = GetFlag(nsMsgFolderFlags::Trash, &deleteImmediatelyNoTrash);
  rv = GetImapIncomingServer(getter_AddRefs(imapServer));

  if (NS_SUCCEEDED(rv) && imapServer)
  {
    imapServer->GetDeleteModel(&deleteModel);
    if (deleteModel != nsMsgImapDeleteModels::MoveToTrash || deleteStorage)
      deleteImmediatelyNoTrash = true;
    // if we're deleting a message, we should pseudo-interrupt the msg
    //load of the current message.
    bool interrupted = 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)
    {
      rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash,
                                     getter_AddRefs(trashFolder));
      NS_ASSERTION(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 (!trashFolder)
        deleteImmediatelyNoTrash = true;
    }
  }

  if ((NS_SUCCEEDED(rv) && deleteImmediatelyNoTrash) || deleteModel == nsMsgImapDeleteModels::IMAPDelete )
  {
    if (allowUndo)
    {
      //need to take care of these two delete models
      nsRefPtr<nsImapMoveCopyMsgTxn> undoMsgTxn = new nsImapMoveCopyMsgTxn;
      if (!undoMsgTxn || NS_FAILED(undoMsgTxn->Init(this, &srcKeyArray, messageIds.get(), nsnull,
                                                      true, isMove, m_thread)))
        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->GetLength(&cnt);
      NS_ENSURE_SUCCESS(rv, rv);
      deleteMsgs = false;
      for (PRUint32 i=0; i <cnt; i++)
      {
        nsCOMPtr <nsIMsgDBHdr> msgHdr = do_QueryElementAt(messages, i);
        if (msgHdr)
        {
          msgHdr->GetFlags(&flags);
          if (!(flags & nsMsgMessageFlags::IMAPDeleted))
          {
            deleteMsgs = true;
            break;
          }
        }
      }
    }
    // if copy service listener is also a url listener, pass that
    // url listener into StoreImapFlags.
    nsCOMPtr <nsIUrlListener> urlListener = do_QueryInterface(listener);
    if (deleteMsgs)
      messageFlags |= kImapMsgSeenFlag;
    rv = StoreImapFlags(messageFlags, deleteMsgs, srcKeyArray.Elements(),
                        srcKeyArray.Length(), urlListener);

    if (NS_SUCCEEDED(rv))
    {
      if (mDatabase)
      {
        if (deleteModel == nsMsgImapDeleteModels::IMAPDelete)
          MarkMessagesImapDeleted(&srcKeyArray, deleteMsgs, mDatabase);
        else
        {
          EnableNotifications(allMessageCountNotifications, false, true /*dbBatching*/);  //"remove it immediately" model
          // Notify if this is an actual delete.
          if (!isMove)
          {
            nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
            if (notifier)
              notifier->NotifyMsgsDeleted(messages);
          }
          mDatabase->DeleteMessages(srcKeyArray.Length(), srcKeyArray.Elements(), nsnull);
          EnableNotifications(allMessageCountNotifications, true, 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->GetLength(&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, 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.
bool
nsImapMailFolder::TrashOrDescendentOfTrash(nsIMsgFolder* folder)
{
  NS_ENSURE_TRUE(folder, false);
  nsCOMPtr<nsIMsgFolder> parent;
  nsCOMPtr<nsIMsgFolder> curFolder = folder;
  nsresult rv;
  PRUint32 flags = 0;
  do
  {
    rv = curFolder->GetFlags(&flags);
    if (NS_FAILED(rv)) return false;
    if (flags & nsMsgFolderFlags::Trash)
      return true;
    curFolder->GetParent(getter_AddRefs(parent));
    if (!parent) return false;
    curFolder = parent;
  } while (NS_SUCCEEDED(rv) && curFolder);
  return false;
}
NS_IMETHODIMP
nsImapMailFolder::DeleteSubFolders(nsIArray* folders, nsIMsgWindow *msgWindow)
{
  nsCOMPtr<nsIMsgFolder> curFolder;
  nsCOMPtr<nsIUrlListener> urlListener;
  nsCOMPtr<nsIMsgFolder> trashFolder;
  PRInt32 i;
  PRUint32 folderCount = 0;
  nsresult rv;
  // "this" is the folder we're deleting from
  bool deleteNoTrash = TrashOrDescendentOfTrash(this) || !DeleteIsMoveToTrash();
  bool confirmed = false;
  bool confirmDeletion = true;

  nsCOMPtr<nsIMutableArray> foldersRemaining(do_CreateInstance(NS_ARRAY_CONTRACTID));
  folders->GetLength(&folderCount);

  for (i = folderCount - 1; i >= 0; i--)
  {
    curFolder = do_QueryElementAt(folders, i, &rv);
    if (NS_SUCCEEDED(rv))
    {
      PRUint32 folderFlags;
      curFolder->GetFlags(&folderFlags);
      if (folderFlags & nsMsgFolderFlags::Virtual)
      {
        RemoveSubFolder(curFolder);
        // since the folder pane only allows single selection, we can do this
        deleteNoTrash = confirmed = true;
        confirmDeletion = false;
      }
      else
        foldersRemaining->InsertElementAt(curFolder, 0, false);
    }
  }

  foldersRemaining->GetLength(&folderCount);

  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  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;
    bool canHaveSubFoldersOfTrash = true;
    trashFolder->GetCanCreateSubfolders(&canHaveSubFoldersOfTrash);
    if (canHaveSubFoldersOfTrash) // UW server doesn't set NOINFERIORS - check dual use pref
    {
      nsCOMPtr<nsIImapIncomingServer> imapServer;
      rv = GetImapIncomingServer(getter_AddRefs(imapServer));
      NS_ENSURE_SUCCESS(rv, rv);
      bool serverSupportsDualUseFolders;
      imapServer->GetDualUseFolders(&serverSupportsDualUseFolders);
      if (!serverSupportsDualUseFolders)
        canHaveSubFoldersOfTrash = false;
    }
    if (!canHaveSubFoldersOfTrash)
      deleteNoTrash = true;
    nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
    NS_ENSURE_SUCCESS(rv, rv);
    prefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash", &confirmDeletion);
  }
  if (!confirmed && (confirmDeletion || deleteNoTrash)) //let us alert the user if we are deleting folder immediately
  {
    nsCOMPtr<nsIStringBundle> bundle;
    rv = IMAPGetStringBundle(getter_AddRefs(bundle));
    NS_ENSURE_SUCCESS(rv, rv);

    nsAutoString folderName;
    rv = curFolder->GetName(folderName);
    NS_ENSURE_SUCCESS(rv, rv);
    const PRUnichar *formatStrings[1] = { folderName.get() };

    nsAutoString deleteFolderDialogTitle;
    rv = bundle->GetStringFromID(IMAP_DELETE_FOLDER_DIALOG_TITLE,
                                 getter_Copies(deleteFolderDialogTitle));
    NS_ENSURE_SUCCESS(rv, rv);

    nsAutoString deleteFolderButtonLabel;
    rv = bundle->GetStringFromID(IMAP_DELETE_FOLDER_BUTTON_LABEL,
                                 getter_Copies(deleteFolderButtonLabel));
    NS_ENSURE_SUCCESS(rv, rv);

    nsAutoString confirmationStr;
    rv = bundle->FormatStringFromID(
           (!deleteNoTrash) ? IMAP_MOVE_FOLDER_TO_TRASH : IMAP_DELETE_NO_TRASH,
           formatStrings, 1, getter_Copies(confirmationStr));
    NS_ENSURE_SUCCESS(rv, rv);
    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)
    {
      PRInt32 buttonPressed = 0;
      // Default the dialog to "cancel".
      const PRUint32 buttonFlags =
        (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
        (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1);

      bool dummyValue = false;
      rv = dialog->ConfirmEx(deleteFolderDialogTitle.get(), confirmationStr.get(),
                             buttonFlags,  deleteFolderButtonLabel.get(),
                             nsnull, nsnull, nsnull, &dummyValue,
                             &buttonPressed);
      NS_ENSURE_SUCCESS(rv, rv);
      confirmed = !buttonPressed; // "ok" is in position 0
    }
  }
  else
    confirmed = true;

  if (confirmed)
  {
    for (i = 0; i < folderCount; i++)
    {
      curFolder = do_QueryElementAt(foldersRemaining, i, &rv);
      if (NS_SUCCEEDED(rv))
      {
        urlListener = do_QueryInterface(curFolder);
        if (deleteNoTrash)
          rv = imapService->DeleteFolder(m_thread,
                                         curFolder,
                                         urlListener,
                                         msgWindow,
                                         nsnull);
        else
        {
          bool confirm = false;
          bool match = false;
          rv = curFolder->MatchOrChangeFilterDestination(nsnull, false, &match);
          if (match)
          {
            curFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirm);
            if (!confirm)
              return NS_OK;
          }
          rv = imapService->MoveFolder(m_thread,
                                       curFolder,
                                       trashFolder,
                                       urlListener,
                                       msgWindow,
                                       nsnull);
        }
      }
    }
  }
  //delete subfolders only if you are  deleting things from trash
  return confirmed && deleteNoTrash ? nsMsgDBFolder::DeleteSubFolders(foldersRemaining, msgWindow) : rv;
}

// FIXME: helper function to know whether we should check all IMAP folders
// for new mail; this is necessary because of a legacy hidden preference
// mail.check_all_imap_folders_for_new (now replaced by per-server preference
// mail.server.%serverkey%.check_all_folders_for_new), still present in some
// profiles.
/*static*/
bool nsImapMailFolder::ShouldCheckAllFolders(nsIImapIncomingServer *imapServer)
{
  // Check legacy global preference to see if we should check all folders for
  // new messages, or just the inbox and marked ones.
  bool checkAllFolders = false;
  nsresult rv;
  nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, false);
  // This pref might not exist, which is OK.
  (void) prefBranch->GetBoolPref("mail.check_all_imap_folders_for_new", &checkAllFolders);

  if (checkAllFolders)
    return true;

  // If the legacy preference doesn't exist or has its default value (False),
  // the true preference is read.
  imapServer->GetCheckAllFoldersForNew(&checkAllFolders);
  return checkAllFolders;
}

// 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;
    rv = GetImapIncomingServer(getter_AddRefs(imapServer));
    NS_ENSURE_SUCCESS(rv, rv);
    bool performingBiff = false;
    nsCOMPtr<nsIMsgIncomingServer> incomingServer = do_QueryInterface(imapServer, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    incomingServer->GetPerformingBiff(&performingBiff);
    m_urlListener = aListener;

    // See if we should check all folders for new messages, or just the inbox
    // and marked ones
    bool checkAllFolders = ShouldCheckAllFolders(imapServer);

    // Get new messages for inbox
    nsCOMPtr<nsIMsgFolder> inbox;
    rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
                                        getter_AddRefs(inbox));
    if (inbox)
    {
      nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(inbox, &rv);
      NS_ENSURE_SUCCESS(rv, rv);
      imapFolder->SetPerformingBiff(performingBiff);
      inbox->SetGettingNewMessages(true);
      rv = inbox->UpdateFolder(aWindow);
    }
    // Get new messages for other folders if marked, or all of them if the pref is set
    rv = imapServer->GetNewMessagesForNonInboxFolders(rootFolder, aWindow, checkAllFolders, performingBiff);
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::Shutdown(bool shutdownChildren)
{
  m_filterList = nsnull;
  m_initialized = false;
  // mPath is used to decide if folder pathname needs to be reconstructed in GetPath().
  mPath = nsnull;
  NS_IF_RELEASE(m_moveCoalescer);
  m_msgParser = nsnull;
  m_pendingOfflineMoves.Clear();
  return nsMsgDBFolder::Shutdown(shutdownChildren);
}

nsresult nsImapMailFolder::GetBodysToDownload(nsTArray<nsMsgKey> *keysOfMessagesToDownload)
{
  NS_ENSURE_ARG(keysOfMessagesToDownload);
  NS_ENSURE_TRUE(mDatabase, NS_ERROR_FAILURE);

  nsCOMPtr <nsISimpleEnumerator> enumerator;
  nsresult rv = mDatabase->EnumerateMessages(getter_AddRefs(enumerator));
  if (NS_SUCCEEDED(rv) && enumerator)
  {
    bool hasMore;
    while (NS_SUCCEEDED(rv = enumerator->HasMoreElements(&hasMore)) && hasMore)
    {
      nsCOMPtr <nsIMsgDBHdr> pHeader;
      rv = enumerator->GetNext(getter_AddRefs(pHeader));
      NS_ENSURE_SUCCESS(rv, rv);
      bool shouldStoreMsgOffline = false;
      nsMsgKey msgKey;
      pHeader->GetMessageKey(&msgKey);
      // MsgFitsDownloadCriteria ignores nsMsgFolderFlags::Offline, which we want
      if (m_downloadingFolderForOfflineUse)
        MsgFitsDownloadCriteria(msgKey, &shouldStoreMsgOffline);
      else
        ShouldStoreMsgOffline(msgKey, &shouldStoreMsgOffline);
      if (shouldStoreMsgOffline)
        keysOfMessagesToDownload->AppendElement(msgKey);
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::OnNewIdleMessages()
{
  nsresult rv;
  nsCOMPtr<nsIImapIncomingServer> imapServer;
  rv = GetImapIncomingServer(getter_AddRefs(imapServer));
  NS_ENSURE_SUCCESS(rv, rv);

  bool checkAllFolders = ShouldCheckAllFolders(imapServer);

  // 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 & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk | nsMsgFolderFlags::SentMail | nsMsgFolderFlags::ImapNoselect)))
    || (mFlags & (nsMsgFolderFlags::CheckNew|nsMsgFolderFlags::Inbox)))
    SetPerformingBiff(true);
  return UpdateFolder(nsnull);
}

NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxInfo(nsIImapProtocol* aProtocol, nsIMailboxSpec* aSpec)
{
  nsresult rv;
  ChangeNumPendingTotalMessages(-GetNumPendingTotalMessages());
  ChangeNumPendingUnread(-GetNumPendingUnread());
  m_numServerRecentMessages = 0; // clear this since we selected the folder.
  m_numServerUnseenMessages = 0; // clear this since we selected the folder.

  if (!mDatabase)
    GetDatabase();

  bool folderSelected;
  rv = aSpec->GetFolderSelected(&folderSelected);
  NS_ENSURE_SUCCESS(rv, rv);
  nsTArray<nsMsgKey> existingKeys;
  nsTArray<nsMsgKey> keysToDelete;
  PRUint32 numNewUnread;
  nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
  PRInt32 imapUIDValidity = 0;
  if (mDatabase)
  {
    rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
    if (NS_SUCCEEDED(rv) && dbFolderInfo)
    {
      dbFolderInfo->GetImapUidValidity(&imapUIDValidity);
      PRUint64 mailboxHighestModSeq;
      aSpec->GetHighestModSeq(&mailboxHighestModSeq);
      char intStrBuf[40];
      PR_snprintf(intStrBuf, sizeof(intStrBuf), "%llu",  mailboxHighestModSeq);
      dbFolderInfo->SetCharProperty(kModSeqPropertyName, nsDependentCString(intStrBuf));
    }
    nsRefPtr<nsMsgKeyArray> keys = new nsMsgKeyArray;
    if (!keys)
      return NS_ERROR_OUT_OF_MEMORY;
    rv = mDatabase->ListAllKeys(keys);
    NS_ENSURE_SUCCESS(rv, rv);
    existingKeys.AppendElements(keys->m_keys);
    PRUint32 keyCount = existingKeys.Length();
    mDatabase->ListAllOfflineDeletes(&existingKeys);
    if (keyCount < existingKeys.Length())
      existingKeys.Sort();
  }
  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)
  {
    NS_ASSERTION(imapUIDValidity == kUidUnknown,
                 "uid validity seems to have changed, blowing away db");
    nsCOMPtr<nsILocalFile> pathFile;
    rv = GetFilePath(getter_AddRefs(pathFile));
    if (NS_FAILED(rv)) return rv;

    nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr <nsIDBFolderInfo> transferInfo;
    if (dbFolderInfo)
      dbFolderInfo->GetTransferInfo(getter_AddRefs(transferInfo));

    // A backup message database might have been created earlier, for example
    // if the user requested a reindex. We'll use the earlier one if we can,
    // otherwise we'll try to backup at this point.
    nsresult rvbackup = OpenBackupMsgDatabase();
    if (mDatabase)
    {
      dbFolderInfo = nsnull;
      if (NS_FAILED(rvbackup))
      {
        CloseAndBackupFolderDB(EmptyCString());
        if (NS_FAILED(OpenBackupMsgDatabase()) && mBackupDatabase)
        {
          mBackupDatabase->RemoveListener(this);
          mBackupDatabase = nsnull;
        }
      }
      else
        mDatabase->ForceClosed();
    }
    mDatabase = nsnull;

    nsCOMPtr <nsILocalFile> summaryFile;
    rv = GetSummaryFileLocation(pathFile, getter_AddRefs(summaryFile));
    // Remove summary file.
    if (NS_SUCCEEDED(rv) && summaryFile)
      summaryFile->Remove(false);

    // Create a new summary file, update the folder message counts, and
    // Close the summary file db.
    bool created;
    rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));

    if (NS_FAILED(rv) && mDatabase)
    {
      mDatabase->ForceClosed();
      mDatabase = nsnull;
    }
    else if (NS_SUCCEEDED(rv) && mDatabase)
    {
      if (transferInfo)
        SetDBTransferInfo(transferInfo);

      SummaryChanged();
      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);
      // need to forget highest mod seq when uid validity rolls.
      dbFolderInfo->SetCharProperty(kModSeqPropertyName, EmptyCString());
      dbFolderInfo->SetUint32Property(kHighestRecordedUIDPropertyName, 0);
    }
    // delete all my msgs, the keys are bogus now
    // add every message in this folder
    existingKeys.Clear();
    //      keysToDelete.CopyArray(&existingKeys);

    if (flagState)
    {
      nsTArray<nsMsgKey> no_existingKeys;
      FindKeysToAdd(no_existingKeys, m_keysToFetch, numNewUnread, flagState);
    }
    if (NS_FAILED(rv))
      pathFile->Remove(false);

  }
  else if (!flagState /*&& !NET_IsOffline() */) // if there are no messages on the server
    keysToDelete = existingKeys;
  else /* if ( !NET_IsOffline()) */
  {
    PRUint32 boxFlags;
    aSpec->GetBox_flags(&boxFlags);
    FindKeysToDelete(existingKeys, keysToDelete, flagState, boxFlags);
    // if this is the result of an expunge then don't grab headers
    if (!(boxFlags & kJustExpunged))
      FindKeysToAdd(existingKeys, m_keysToFetch, numNewUnread, flagState);
  }
  m_totalKeysToFetch = m_keysToFetch.Length();
  if (!keysToDelete.IsEmpty() && mDatabase)
  {
    nsCOMPtr<nsIMutableArray> hdrsToDelete(do_CreateInstance(NS_ARRAY_CONTRACTID));
    MsgGetHeadersFromKeys(mDatabase, keysToDelete, hdrsToDelete);
    // Notify nsIMsgFolderListeners of a mass delete, but only if we actually have headers
    PRUint32 numHdrs;
    hdrsToDelete->GetLength(&numHdrs);
    if (numHdrs)
    {
      nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
      if (notifier)
        notifier->NotifyMsgsDeleted(hdrsToDelete);
    }
    // It would be nice to notify RDF or whoever of a mass delete here.
    mDatabase->DeleteMessages(keysToDelete.Length(), keysToDelete.Elements(), nsnull);
  }
  PRInt32 numUnreadFromServer;
  aSpec->GetNumUnseenMessages(&numUnreadFromServer);
  
  bool partialUIDFetch;
  flagState->GetPartialUIDFetch(&partialUIDFetch);
  
  // For partial UID fetches, we can only trust the numUnread from the server.
  if (partialUIDFetch)
    numNewUnread = numUnreadFromServer;
    
  // If we are performing biff for this folder, tell the
  // stand-alone biff about the new high water mark
  if (m_performingBiff && numNewUnread)
  {
    // 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(true);
     SetNumNewMessages(numNewUnread);
  }
  SyncFlags(flagState);
  if (mDatabase && mNumUnreadMessages + m_keysToFetch.Length() > numUnreadFromServer)
    mDatabase->SyncCounts();

  if (!m_keysToFetch.IsEmpty() && aProtocol)
    PrepareToAddHeadersToMailDB(aProtocol);
  else
  {
    bool gettingNewMessages;
    GetGettingNewMessages(&gettingNewMessages);
    if (gettingNewMessages)
      ProgressStatus(aProtocol, IMAP_NO_NEW_MESSAGES, nsnull);
    SetPerformingBiff(false);
  }
  aSpec->GetNumMessages(&m_numServerTotalMessages);
  aSpec->GetNumUnseenMessages(&m_numServerUnseenMessages);
  aSpec->GetNumRecentMessages(&m_numServerRecentMessages);

  // some servers don't return UIDNEXT on SELECT - don't crunch
  // existing values in that case.
  PRInt32 nextUID;
  aSpec->GetNextUID(&nextUID);
  if (nextUID != nsMsgKey_None)
    m_nextUID = nextUID;

  return rv;
}

NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxStatus(
  nsIImapProtocol* aProtocol, nsIMailboxSpec* aSpec)
{
  NS_ENSURE_ARG_POINTER(aSpec);
  PRInt32 numUnread, numTotal;
  aSpec->GetNumUnseenMessages(&numUnread);
  aSpec->GetNumMessages(&numTotal);
  aSpec->GetNumRecentMessages(&m_numServerRecentMessages);
  PRInt32 prevNextUID = m_nextUID;
  aSpec->GetNextUID(&m_nextUID);
  bool summaryChanged = false;

  // If m_numServerUnseenMessages 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_numServerUnseenMessages)
    ? m_numServerUnseenMessages : GetNumPendingUnread() + mNumUnreadMessages;
  if (numUnread != previousUnreadMessages || m_nextUID != prevNextUID)
  {
    PRInt32 unreadDelta = numUnread - (GetNumPendingUnread() + mNumUnreadMessages);
    if (numUnread - previousUnreadMessages != unreadDelta)
       NS_WARNING("unread count should match server count");
    ChangeNumPendingUnread(unreadDelta);
    if (unreadDelta > 0 &&
        !(mFlags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk)))
    {
      SetHasNewMessages(true);
      SetNumNewMessages(unreadDelta);
      SetBiffState(nsMsgBiffState_NewMail);
    }
    summaryChanged = true;
  }
  SetPerformingBiff(false);
  if (m_numServerUnseenMessages != numUnread || m_numServerTotalMessages != numTotal)
  {
    if (numUnread > m_numServerUnseenMessages ||
        m_numServerTotalMessages > numTotal)
      NotifyHasPendingMsgs();
    summaryChanged = true;
    m_numServerUnseenMessages = numUnread;
    m_numServerTotalMessages = numTotal;
  }
  if (summaryChanged)
    SummaryChanged();

  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::ParseMsgHdrs(nsIImapProtocol *aProtocol, nsIImapHeaderXferInfo *aHdrXferInfo)
{
  PRInt32 numHdrs;
  nsCOMPtr <nsIImapHeaderInfo> headerInfo;
  nsCOMPtr <nsIImapUrl> aImapUrl;
  nsImapAction imapAction = nsIImapUrl::nsImapTest; // unused value.
  if (!mDatabase)
    GetDatabase();

  nsresult rv = aHdrXferInfo->GetNumHeaders(&numHdrs);
  if (aProtocol)
  {
    (void) aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
    if (aImapUrl)
      aImapUrl->GetImapAction(&imapAction);
  }
  for (PRUint32 i = 0; NS_SUCCEEDED(rv) && i < numHdrs; i++)
  {
    rv = aHdrXferInfo->GetHeader(i, getter_AddRefs(headerInfo));
    NS_ENSURE_SUCCESS(rv, rv);
    if (!headerInfo)
      break;
    PRInt32 msgSize;
    nsMsgKey msgKey;
    bool containsKey;
    const char *msgHdrs;
    headerInfo->GetMsgSize(&msgSize);
    headerInfo->GetMsgUid(&msgKey);
    if (msgKey == nsMsgKey_None) // not a valid uid.
      continue;
    if (imapAction == nsIImapUrl::nsImapMsgPreview)
    {
      nsCOMPtr <nsIMsgDBHdr> msgHdr;
      headerInfo->GetMsgHdrs(&msgHdrs);
      // create an input stream based on the hdr string.
      nsCOMPtr<nsIStringInputStream> inputStream =
            do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
      NS_ENSURE_SUCCESS(rv, rv);
      inputStream->ShareData(msgHdrs, strlen(msgHdrs));
      GetMessageHeader(msgKey, getter_AddRefs(msgHdr));
      if (msgHdr)
        GetMsgPreviewTextFromStream(msgHdr, inputStream);
      continue;
    }
    if (mDatabase && NS_SUCCEEDED(mDatabase->ContainsKey(msgKey, &containsKey)) && containsKey)
    {
      NS_ERROR("downloading hdrs for hdr we already have");
      continue;
    }
    nsresult rv = SetupHeaderParseStream(msgSize, EmptyCString(), nsnull);
    NS_ENSURE_SUCCESS(rv, rv);
    headerInfo->GetMsgHdrs(&msgHdrs);
    rv = ParseAdoptedHeaderLine(msgHdrs, msgKey);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = NormalEndHeaderParseStream(aProtocol, aImapUrl);
  }
  return rv;
}

nsresult nsImapMailFolder::SetupHeaderParseStream(PRUint32 aSize,
                                                  const nsACString& content_type, nsIMailboxSpec *boxSpec)
{
  if (!mDatabase)
    GetDatabase();
  m_nextMessageByteLength = aSize;
  if (!m_msgParser)
  {
    nsresult rv;
    m_msgParser = do_CreateInstance(kParseMailMsgStateCID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  else
    m_msgParser->Clear();

  m_msgParser->SetMailDB(mDatabase);
  if (mBackupDatabase)
    m_msgParser->SetBackupMailDB(mBackupDatabase);
  return m_msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
}

nsresult nsImapMailFolder::ParseAdoptedHeaderLine(const char *aMessageLine, PRUint32 aMsgKey)
{
  // we can get blocks that contain more than one line,
  // but they never contain partial lines
  const char *str = aMessageLine;
  m_curMsgUid = aMsgKey;
  m_msgParser->SetEnvelopePos(m_curMsgUid);
  // m_envelope_pos, for local folders,
  // is the msg key. Setting this will set the msg key for the new header.

  PRInt32 len = strlen(str);
  char *currentEOL  = PL_strstr(str, MSG_LINEBREAK);
  const char *currentLine = str;
  while (currentLine < (str + len))
  {
    if (currentEOL)
    {
      m_msgParser->ParseAFolderLine(currentLine,
        (currentEOL + MSG_LINEBREAK_LEN) -
        currentLine);
      currentLine = currentEOL + MSG_LINEBREAK_LEN;
      currentEOL  = PL_strstr(currentLine, MSG_LINEBREAK);
    }
    else
    {
      m_msgParser->ParseAFolderLine(currentLine, PL_strlen(currentLine));
      currentLine = str + len + 1;
    }
  }
  return NS_OK;
}

nsresult nsImapMailFolder::NormalEndHeaderParseStream(nsIImapProtocol *aProtocol, nsIImapUrl* imapUrl)
{
  nsCOMPtr<nsIMsgDBHdr> newMsgHdr;
  nsresult rv;
  NS_ENSURE_TRUE(m_msgParser, NS_ERROR_NULL_POINTER);

  nsMailboxParseState parseState;
  m_msgParser->GetState(&parseState);
  if (parseState == nsIMsgParseMailMsgState::ParseHeadersState)
    m_msgParser->ParseAFolderLine(CRLF, 2);
  rv = m_msgParser->GetNewMsgHdr(getter_AddRefs(newMsgHdr));
  NS_ENSURE_SUCCESS(rv, rv);

  char *headers;
  PRInt32 headersSize;

  nsCOMPtr <nsIMsgWindow> msgWindow;
  nsCOMPtr <nsIMsgMailNewsUrl> msgUrl;
  if (imapUrl)
  {
    msgUrl = do_QueryInterface(imapUrl, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
  }

  nsCOMPtr<nsIMsgIncomingServer> server;
  rv = GetServer(getter_AddRefs(server));
  NS_ENSURE_SUCCESS(rv, rv);
  newMsgHdr->SetMessageKey(m_curMsgUid);
  TweakHeaderFlags(aProtocol, newMsgHdr);
  PRUint32 messageSize;
  if (NS_SUCCEEDED(newMsgHdr->GetMessageSize(&messageSize)))
    mFolderSize += messageSize;
  m_msgMovedByFilter = false;

  // If this is the inbox, try to apply filters. Otherwise, test the inherited
  // folder property "applyIncomingFilters" (which defaults to empty). If this
  // inherited property has the string value "true", then apply filters even
  // if this is not the Inbox folder.
  if (mFlags & nsMsgFolderFlags::Inbox || m_applyIncomingFilters)
  {
    PRUint32 msgFlags;
    newMsgHdr->GetFlags(&msgFlags);
    if (!(msgFlags & (nsMsgMessageFlags::Read | nsMsgMessageFlags::IMAPDeleted))) // only fire on unread msgs that haven't been deleted
    {
      PRInt32 duplicateAction = nsIMsgIncomingServer::keepDups;
      if (server)
        server->GetIncomingDuplicateAction(&duplicateAction);
      if ((duplicateAction != nsIMsgIncomingServer::keepDups) &&
          mFlags & nsMsgFolderFlags::Inbox)
      {
        bool isDup;
        server->IsNewHdrDuplicate(newMsgHdr, &isDup);
        if (isDup)
        {
          // we want to do something similar to applying filter hits.
          // if a dup is marked read, it shouldn't trigger biff.
          // Same for deleting it or moving it to trash.
          switch (duplicateAction)
          {
            case nsIMsgIncomingServer::deleteDups:
              {
                PRUint32 newFlags;
                newMsgHdr->OrFlags(nsMsgMessageFlags::Read | nsMsgMessageFlags::IMAPDeleted, &newFlags);
                StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, true,
                               &m_curMsgUid, 1, nsnull);
                m_msgMovedByFilter = true;
              }
              break;
            case nsIMsgIncomingServer::moveDupsToTrash:
              {
                nsCOMPtr <nsIMsgFolder> trash;
                GetTrashFolder(getter_AddRefs(trash));
                if (trash)
                {
                  nsCString trashUri;
                  trash->GetURI(trashUri);
                  nsresult err = MoveIncorporatedMessage(newMsgHdr, mDatabase, trashUri, nsnull, msgWindow);
                  if (NS_SUCCEEDED(err))
                    m_msgMovedByFilter = true;
                }
              }
              break;
            case nsIMsgIncomingServer::markDupsRead:
              {
                PRUint32 newFlags;
                newMsgHdr->OrFlags(nsMsgMessageFlags::Read, &newFlags);
                StoreImapFlags(kImapMsgSeenFlag, true, &m_curMsgUid, 1, nsnull);
              }
              break;
          }
          PRInt32 numNewMessages;
          GetNumNewMessages(false, &numNewMessages);
          SetNumNewMessages(numNewMessages - 1);
        }
      }
      rv = m_msgParser->GetAllHeaders(&headers, &headersSize);

      if (NS_SUCCEEDED(rv) && headers && !m_msgMovedByFilter &&
          !m_filterListRequiresBody)
      {
        if (m_filterList)
        {
          GetMoveCoalescer();  // not sure why we're doing this here.
          m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr,
                                          this, mDatabase, headers, headersSize,
                                          this, msgWindow);
          NotifyFolderEvent(mFiltersAppliedAtom);
        }
      }
    }
  }
  // here we need to tweak flags from uid state..
  if (mDatabase && (!m_msgMovedByFilter || ShowDeletedMessages()))
  {
    nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
    // Check if this header corresponds to a pseudo header
    // we have from doing a pseudo-offline move and then downloading
    // the real header from the server. In that case, we notify
    // db/folder listeners that the pseudo-header has become the new
    // header, i.e., the key has changed.
    nsCString newMessageId;
    nsMsgKey pseudoKey = nsMsgKey_None;
    newMsgHdr->GetMessageId(getter_Copies(newMessageId));
    if (m_pseudoHdrs.IsInitialized())
      m_pseudoHdrs.Get(newMessageId, &pseudoKey);
    if (notifier && pseudoKey != nsMsgKey_None)
    {
      notifier->NotifyMsgKeyChanged(pseudoKey, newMsgHdr);
      m_pseudoHdrs.Remove(newMessageId);
    }
    mDatabase->AddNewHdrToDB(newMsgHdr, true);
    if (notifier)
      notifier->NotifyMsgAdded(newMsgHdr);
    // mark the header as not yet reported classified
    OrProcessingFlags(m_curMsgUid, nsMsgProcessingFlags::NotReportedClassified);
  }
  // adjust highestRecordedUID
  if (mDatabase)
  {
    nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
    nsMsgKey highestUID;
    mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
    dbFolderInfo->GetUint32Property(kHighestRecordedUIDPropertyName, 0, &highestUID);
    if (m_curMsgUid > highestUID)
      dbFolderInfo->SetUint32Property(kHighestRecordedUIDPropertyName, m_curMsgUid);

  }
  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)
{
  NS_ENSURE_TRUE(m_copyState, NS_ERROR_NULL_POINTER);
  nsresult rv;
  if (m_copyState->m_tmpFile) // leftover file spec nuke it
  {
    rv = m_copyState->m_tmpFile->Remove(false);
    if (NS_FAILED(rv))
    {
      nsCString nativePath;
      m_copyState->m_tmpFile->GetNativePath(nativePath);
      PR_LOG(IMAP, PR_LOG_ALWAYS, ("couldn't remove prev temp file %s: %lx\n", nativePath.get(), rv));
    }
    m_copyState->m_tmpFile = nsnull;
  }
  if (message)
    m_copyState->m_message = do_QueryInterface(message, &rv);

  rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
                                       "nscpmsg.txt",
                                        getter_AddRefs(m_copyState->m_tmpFile));
  if (NS_FAILED(rv))
    PR_LOG(IMAP, PR_LOG_ALWAYS, ("couldn't find nscpmsg.txt:%lx\n", rv));
  NS_ENSURE_SUCCESS(rv, rv);

  // create a unique file, since multiple copies may be open on multiple folders
  rv = m_copyState->m_tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
  if (NS_FAILED(rv))
  {
    PR_LOG(IMAP, PR_LOG_ALWAYS, ("couldn't create temp nscpmsg.txt:%lx\n", rv));
    // Last ditch attempt to create a temp file, because virus checker might
    // be locking the previous temp file, and CreateUnique fails if the file
    // is locked. Use the message key to make a unique name.
    if (message)
    {
      nsCString tmpFileName("nscpmsg-");
      nsMsgKey msgKey;
      message->GetMessageKey(&msgKey);
      tmpFileName.AppendInt(msgKey);
      tmpFileName.Append(".txt");
      m_copyState->m_tmpFile->SetNativeLeafName(tmpFileName);
      rv = m_copyState->m_tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
      if (NS_FAILED(rv))
      {
        PR_LOG(IMAP, PR_LOG_ALWAYS, ("couldn't create temp nscpmsg.txt:%lx\n", rv));
        OnCopyCompleted(m_copyState->m_srcSupport, rv);
        return rv;
      }
    }
  }

  nsCOMPtr<nsIOutputStream> fileOutputStream;
  nsCOMPtr <nsILocalFile> localFile = do_QueryInterface(m_copyState->m_tmpFile);
  rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_copyState->m_msgFileStream), localFile, -1, 00600);
  if (NS_FAILED(rv))
    PR_LOG(IMAP, PR_LOG_ALWAYS, ("couldn't create output file stream:%lx\n", rv));

  if (!m_copyState->m_dataBuffer)
    m_copyState->m_dataBuffer = (char*) PR_CALLOC(COPY_BUFFER_SIZE+1);
  NS_ENSURE_TRUE(m_copyState->m_dataBuffer, NS_ERROR_OUT_OF_MEMORY);
  m_copyState->m_dataBufferSize = COPY_BUFFER_SIZE;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::CopyDataToOutputStreamForAppend(nsIInputStream *aIStream,
                     PRInt32 aLength, nsIOutputStream *outputStream)
{
  PRUint32 readCount;
  PRUint32 writeCount;
  if (!m_copyState)
    m_copyState = new nsImapMailCopyState();

  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);
    NS_ENSURE_TRUE(m_copyState->m_dataBuffer, NS_ERROR_OUT_OF_MEMORY);
    m_copyState->m_dataBufferSize = aLength + m_copyState->m_leftOver;
  }

  char *start, *end;
  PRUint32 linebreak_len = 1;

  nsresult rv = aIStream->Read(m_copyState->m_dataBuffer+m_copyState->m_leftOver, aLength, &readCount);
  if (NS_FAILED(rv))
    return rv;

  m_copyState->m_leftOver += readCount;
  m_copyState->m_dataBuffer[m_copyState->m_leftOver] = '\0';

  start = m_copyState->m_dataBuffer;
  if (m_copyState->m_eatLF)
  {
    if (*start == '\n')
      start++;
    m_copyState->m_eatLF = false;
  }
  end = PL_strpbrk(start, "\r\n");
  if (end && *end == '\r' && *(end+1) == '\n')
    linebreak_len = 2;

  while (start && end)
  {
    if (PL_strncasecmp(start, "X-Mozilla-Status:", 17) &&
        PL_strncasecmp(start, "X-Mozilla-Status2:", 18) &&
        PL_strncmp(start, "From - ", 7))
    {
      rv = outputStream->Write(start,
                                             end-start,
                                             &writeCount);
      rv = outputStream->Write(CRLF, 2, &writeCount);
    }
    start = end+linebreak_len;
    if (start >=
        m_copyState->m_dataBuffer+m_copyState->m_leftOver)
    {
       m_copyState->m_leftOver = 0;
       break;
    }
    linebreak_len = 1;

    end = PL_strpbrk(start, "\r\n");
    if (end && *end == '\r')
    {
      if (*(end+1) == '\n')
        linebreak_len = 2;
      else if (! *(end+1)) // block might have split CRLF so remember if
        m_copyState->m_eatLF = true; // we should eat LF
    }

    if (start && !end)
    {
      m_copyState->m_leftOver -= (start - m_copyState->m_dataBuffer);
      memcpy(m_copyState->m_dataBuffer, start, m_copyState->m_leftOver+1); // including null
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::CopyDataDone()
{
  m_copyState = nsnull;
  return NS_OK;
}

// sICopyMessageListener methods, BeginCopy, CopyData, EndCopy, EndMove, StartMessage, EndMessage
NS_IMETHODIMP nsImapMailFolder::CopyData(nsIInputStream *aIStream, PRInt32 aLength)
{
  NS_ENSURE_TRUE(m_copyState && m_copyState->m_msgFileStream && m_copyState->m_dataBuffer, NS_ERROR_NULL_POINTER);
  nsresult rv = CopyDataToOutputStreamForAppend(aIStream, aLength,
                                                m_copyState->m_msgFileStream);
  if (NS_FAILED(rv))
  {
    PR_LOG(IMAP, PR_LOG_ALWAYS, ("CopyData failed:%lx\n", rv));
    OnCopyCompleted(m_copyState->m_srcSupport, rv);
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::EndCopy(bool copySucceeded)
{
  nsresult rv = copySucceeded ? NS_OK : NS_ERROR_FAILURE;
  if (copySucceeded && m_copyState && m_copyState->m_msgFileStream)
  {
    nsCOMPtr<nsIUrlListener> urlListener;
    m_copyState->m_msgFileStream->Close();
    // m_tmpFile can be stale because we wrote to it
    nsCOMPtr<nsIFile> tmpFile;
    m_copyState->m_tmpFile->Clone(getter_AddRefs(tmpFile));
    m_copyState->m_tmpFile = tmpFile;
    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_thread,
                                            m_copyState->m_tmpFile,
                                            this, EmptyCString(), true,
                                            m_copyState->m_selectedState,
                                            urlListener, nsnull,
                                            copySupport,
                                            m_copyState->m_msgWindow);
  }
  if (NS_FAILED(rv) || !copySucceeded)
    PR_LOG(IMAP, PR_LOG_ALWAYS, ("EndCopy failed:%lx\n", rv));
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::EndMove(bool 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, bool *applyMore)
{
  //
  //  This routine is called indirectly from ApplyFiltersToHdr in two
  //  circumstances, controlled by m_filterListRequiresBody:
  //
  //  If false, after headers are parsed in NormalEndHeaderParseStream.
  //  If true, after the message body is downloaded in NormalEndMsgWriteStream.
  //
  //  In NormalEndHeaderParseStream, the message has not been added to the
  //  database, and it is important that database notifications and count 
  //  updates do not occur. In NormalEndMsgWriteStream, the message has been
  //  added to the database, and database notifications and count updates
  //  should be performed.
  //

  NS_ENSURE_ARG_POINTER(applyMore);

  nsMsgRuleActionType actionType;
  nsCString 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_filterListRequiresBody)
    GetMessageHeader(m_curMsgUid, getter_AddRefs(msgHdr));
  else if (m_msgParser)
    m_msgParser->GetNewMsgHdr(getter_AddRefs(msgHdr));
  if (!msgHdr)
    return NS_ERROR_NULL_POINTER; //fatal error, cannot apply filters

  bool deleteToTrash = DeleteIsMoveToTrash();
  nsCOMPtr<nsISupportsArray> filterActionList = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
  NS_ENSURE_TRUE(filterActionList, rv);
  rv = filter->GetSortedActionList(filterActionList);
  NS_ENSURE_SUCCESS(rv, rv);

  PRUint32 numActions;
  rv = filterActionList->Count(&numActions);
  NS_ENSURE_SUCCESS(rv, rv);

  bool loggingEnabled = false;
  if (m_filterList && numActions)
    (void)m_filterList->GetLoggingEnabled(&loggingEnabled);

  bool msgIsNew = true;

  for (PRUint32 actionIndex = 0; actionIndex < numActions; 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 ||
          actionType == nsMsgFilterAction::CopyToFolder)
      {
        filterAction->GetTargetFolderUri(actionTargetFolderUri);
        if (actionTargetFolderUri.IsEmpty())
        {
          NS_ASSERTION(false, "actionTargetFolderUri is empty");
          continue;
        }
      }

      PRUint32 msgFlags;
      nsMsgKey    msgKey;
      nsCAutoString trashNameVal;

      msgHdr->GetFlags(&msgFlags);
      msgHdr->GetMessageKey(&msgKey);
      bool isRead = (msgFlags & nsMsgMessageFlags::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(actionTargetFolderUri);
            // msgHdr->OrFlags(nsMsgMessageFlags::Read, &newFlags);  // mark read in trash.
          }
          else  // (!deleteToTrash)
          {
            mDatabase->MarkHdrRead(msgHdr, true, nsnull);
            mDatabase->MarkImapDeleted(msgKey, true, nsnull);
            StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, true,
                           &msgKey, 1, nsnull);
            m_msgMovedByFilter = true; // this will prevent us from adding the header to the db.
          }
          msgIsNew = false;
        }
        // note that delete falls through to move.
        case nsMsgFilterAction::MoveToFolder:
        {
          // if moving to a different file, do it.
          nsCString uri;
          rv = GetURI(uri);

          if (!actionTargetFolderUri.Equals(uri))
          {
            msgHdr->GetFlags(&msgFlags);

            if (msgFlags & nsMsgMessageFlags::MDNReportNeeded && !isRead)
            {
               mDatabase->MarkMDNNeeded(msgKey, false, nsnull);
               mDatabase->MarkMDNSent(msgKey, true, nsnull);
            }
            nsresult err = MoveIncorporatedMessage(msgHdr, mDatabase, actionTargetFolderUri, filter, msgWindow);
            if (NS_SUCCEEDED(err))
              m_msgMovedByFilter = true;
          }
          // don't apply any more filters, even if it was a move to the same folder
          *applyMore = false; 
        }
        break;
        case nsMsgFilterAction::CopyToFolder:
        {
          nsCString uri;
          rv = GetURI(uri);

          if (!actionTargetFolderUri.Equals(uri))
          {
            // XXXshaver I'm not actually 100% what the right semantics are for
            // MDNs and copied messages, but I suspect deep down inside that
            // we probably want to suppress them only on the copies.
            msgHdr->GetFlags(&msgFlags);
            if (msgFlags & nsMsgMessageFlags::MDNReportNeeded && !isRead)
            {
               mDatabase->MarkMDNNeeded(msgKey, false, nsnull);
               mDatabase->MarkMDNSent(msgKey, true, nsnull);
            }

            nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
            NS_ENSURE_TRUE(messageArray, rv);
            messageArray->AppendElement(msgHdr, false);

            nsCOMPtr<nsIMsgFolder> dstFolder;
            rv = GetExistingFolder(actionTargetFolderUri, getter_AddRefs(dstFolder));
            NS_ENSURE_SUCCESS(rv, rv);

            nsCOMPtr<nsIMsgCopyService> copyService =
              do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
            NS_ENSURE_SUCCESS(rv, rv);
            rv = copyService->CopyMessages(this, messageArray, dstFolder,
                                           false, nsnull, msgWindow, false);
            NS_ENSURE_SUCCESS(rv, rv);
          }
        }
        break;
        case nsMsgFilterAction::MarkRead:
        {
          mDatabase->MarkHdrRead(msgHdr, true, nsnull);
          StoreImapFlags(kImapMsgSeenFlag, true, &msgKey, 1, nsnull);
          msgIsNew = false;
        }
        break;
        case nsMsgFilterAction::MarkUnread:
        {
          mDatabase->MarkHdrRead(msgHdr, false, nsnull);
          StoreImapFlags(kImapMsgSeenFlag, false, &msgKey, 1, nsnull);
          msgIsNew = true;
        }
        break;
        case nsMsgFilterAction::MarkFlagged:
        {
          mDatabase->MarkHdrMarked(msgHdr, true, nsnull);
          StoreImapFlags(kImapMsgFlaggedFlag, true, &msgKey, 1, nsnull);
        }
        break;
        case nsMsgFilterAction::KillThread:
        case nsMsgFilterAction::WatchThread:
        {
          nsCOMPtr <nsIMsgThread> msgThread;
          nsMsgKey threadKey;
          mDatabase->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(msgThread));
          if (msgThread)
          {
            msgThread->GetThreadKey(&threadKey);
            if (actionType == nsMsgFilterAction::KillThread)
              mDatabase->MarkThreadIgnored(msgThread, threadKey, true, nsnull);
            else
              mDatabase->MarkThreadWatched(msgThread, threadKey, true, nsnull);
          }
          else
          {
            if (actionType == nsMsgFilterAction::KillThread)
              msgHdr->SetUint32Property("ProtoThreadFlags", nsMsgMessageFlags::Ignored);
            else
              msgHdr->SetUint32Property("ProtoThreadFlags", nsMsgMessageFlags::Watched);
          }
          if (actionType == nsMsgFilterAction::KillThread)
          {
            mDatabase->MarkHdrRead(msgHdr, true, nsnull);
            StoreImapFlags(kImapMsgSeenFlag, true, &msgKey, 1, nsnull);
            msgIsNew = false;
          }
        }
        break;
        case nsMsgFilterAction::KillSubthread:
        {
          mDatabase->MarkHeaderKilled(msgHdr, true, nsnull);
          mDatabase->MarkHdrRead(msgHdr, true, nsnull);
          StoreImapFlags(kImapMsgSeenFlag, true, &msgKey, 1, nsnull);
          msgIsNew = false;
        }
        break;
        case nsMsgFilterAction::ChangePriority:
        {
          nsMsgPriorityValue filterPriority; // a PRInt32
          filterAction->GetPriority(&filterPriority);
          mDatabase->SetUint32PropertyByHdr(msgHdr, "priority",
                                            static_cast<PRUint32>(filterPriority));
        }
        break;
        case nsMsgFilterAction::Label:
        {
          nsMsgLabelValue filterLabel;
          filterAction->GetLabel(&filterLabel);
          mDatabase->SetUint32PropertyByHdr(msgHdr, "label",
                                            static_cast<PRUint32>(filterLabel));
          StoreImapFlags((filterLabel << 9), true, &msgKey, 1, nsnull);
        }
        break;
        case nsMsgFilterAction::AddTag:
        {
          nsCString keyword;
          filterAction->GetStrValue(keyword);
          nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
          NS_ENSURE_TRUE(messageArray, rv);
          messageArray->AppendElement(msgHdr, false);
          AddKeywordsToMessages(messageArray, keyword);
          break;
        }
        case nsMsgFilterAction::JunkScore:
        {
          nsCAutoString junkScoreStr;
          PRInt32 junkScore;
          filterAction->GetJunkScore(&junkScore);
          junkScoreStr.AppendInt(junkScore);
          mDatabase->SetStringProperty(msgKey, "junkscore", junkScoreStr.get());
          mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "filter");

          // If score is available, set up to store junk status on server.
          if (junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE ||
              junkScore == nsIJunkMailPlugin::IS_HAM_SCORE)
          {
            nsTArray<nsMsgKey> *keysToClassify = m_moveCoalescer->GetKeyBucket(
                       (junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE) ? 0 : 1);
            NS_ASSERTION(keysToClassify, "error getting key bucket");
            if (keysToClassify)
              keysToClassify->AppendElement(msgKey);
            if (msgIsNew && junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE)
            {              
              msgIsNew = false;
              mDatabase->MarkHdrNotNew(msgHdr, nsnull);
              // nsMsgDBFolder::SendFlagNotifications by the call to
              // SetBiffState(nsMsgBiffState_NoMail) will reset numNewMessages
              // only if the message is also read and database notifications
              // are active, but we are not going to mark it read in this
              // action, preferring to leave the choice to the user.
              // So correct numNewMessages.
              if (m_filterListRequiresBody)
              {
                msgHdr->GetFlags(&msgFlags);
                if (!(msgFlags & nsMsgMessageFlags::Read))
                {
                  PRInt32 numNewMessages;
                  GetNumNewMessages(false, &numNewMessages);
                  SetNumNewMessages(--numNewMessages);
                  SetHasNewMessages(numNewMessages != 0);
                }
              }
            }
          }
        }
        break;
      case nsMsgFilterAction::Forward:
        {
          nsCString forwardTo;
          filterAction->GetStrValue(forwardTo);
          nsCOMPtr<nsIMsgIncomingServer> server;
          rv = GetServer(getter_AddRefs(server));
          NS_ENSURE_SUCCESS(rv, rv);
          if (!forwardTo.IsEmpty())
          {
            nsCOMPtr<nsIMsgComposeService> compService =
              do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
            NS_ENSURE_SUCCESS(rv, rv);
            rv = compService->ForwardMessage(NS_ConvertASCIItoUTF16(forwardTo),
                                             msgHdr, msgWindow, server,
                                             nsIMsgComposeService::kForwardAsDefault);
          }
        }
        break;

      case nsMsgFilterAction::Reply:
        {
          nsCString replyTemplateUri;
          filterAction->GetStrValue(replyTemplateUri);
          nsCOMPtr <nsIMsgIncomingServer> server;
          GetServer(getter_AddRefs(server));
          NS_ENSURE_SUCCESS(rv, rv);
          if (!replyTemplateUri.IsEmpty())
          {
            nsCOMPtr <nsIMsgComposeService> compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
            if (compService)
              rv = compService->ReplyWithTemplate(msgHdr, replyTemplateUri.get(), msgWindow, server);
          }
        }
        break;

        case nsMsgFilterAction::StopExecution:
        {
          // don't apply any more filters
          *applyMore = false;
        }
        break;

        case nsMsgFilterAction::Custom:
        {
          nsCOMPtr<nsIMsgFilterCustomAction> customAction;
          rv = filterAction->GetCustomAction(getter_AddRefs(customAction));
          NS_ENSURE_SUCCESS(rv, rv);

          nsCAutoString value;
          filterAction->GetStrValue(value);

          nsCOMPtr<nsIMutableArray> messageArray(
              do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
          NS_ENSURE_TRUE(messageArray, rv);
          messageArray->AppendElement(msgHdr, false);

          customAction->Apply(messageArray, value, nsnull,
                              nsMsgFilterType::InboxRule, msgWindow);
          // allow custom action to affect new
          msgHdr->GetFlags(&msgFlags);
          if (!(msgFlags & nsMsgMessageFlags::New))
            msgIsNew = false;
        }
        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(false, &numNewMessages);
    // When database notifications are active, new counts will be reset
    // to zero in nsMsgDBFolder::SendFlagNotifications by the call to
    // SetBiffState(nsMsgBiffState_NoMail), so don't repeat them here.
    if (!m_filterListRequiresBody)
      SetNumNewMessages(--numNewMessages);
    if (mDatabase)
      mDatabase->MarkHdrNotNew(msgHdr, nsnull);
  }
  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_thread, this, this, url, nsCAutoString(uids), flags, true);
}

// "this" is the parent folder
NS_IMETHODIMP nsImapMailFolder::PlaybackOfflineFolderCreate(const nsAString& aFolderName, nsIMsgWindow *aWindow, nsIURI **url)
{
  nsresult rv;
  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv,rv);
  return imapService->CreateFolder(m_thread, this, aFolderName, this, url);
}

NS_IMETHODIMP 
nsImapMailFolder::ReplayOfflineMoveCopy(nsMsgKey *aMsgKeys, PRUint32 aNumKeys,
                                        bool isMove, nsIMsgFolder *aDstFolder,
                                        nsIUrlListener *aUrlListener, nsIMsgWindow *aWindow)
{
  nsresult rv;

  nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(aDstFolder);
  if (imapFolder)
  {
    nsImapMailFolder *destImapFolder = static_cast<nsImapMailFolder*>(aDstFolder);
    nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
    nsCOMPtr<nsIMsgDatabase> dstFolderDB;
    aDstFolder->GetMsgDatabase(getter_AddRefs(dstFolderDB));
    if (dstFolderDB)
    {
      // find the fake header in the destination db, and use that to
      // set the pending attributes on the real headers. To do this,
      // we need to iterate over the offline ops in the destination db,
      // looking for ones with matching keys and source folder uri. 
      // If we find that offline op, its "key" will be the key of the fake
      // header, so we just need to get the header for that key 
      // from the dest db.
      nsTArray<nsMsgKey> offlineOps;
      if (NS_SUCCEEDED(dstFolderDB->ListAllOfflineOpIds(&offlineOps)))
      {
        nsCString srcFolderUri;
        GetURI(srcFolderUri);
        for (PRUint32 msgIndex = 0; msgIndex < aNumKeys; msgIndex++)
        {
          nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
          for (PRUint32 opIndex = 0; opIndex < offlineOps.Length(); opIndex++)
          {
            dstFolderDB->GetOfflineOpForKey(offlineOps[opIndex], false,
                                            getter_AddRefs(currentOp));
            if (currentOp)
            {
              nsMsgKey srcMessageKey;
              currentOp->GetSrcMessageKey(&srcMessageKey);
              if (srcMessageKey == aMsgKeys[msgIndex])
              {
                nsCString opSrcUri;
                currentOp->GetSourceFolderURI(getter_Copies(opSrcUri));
                if (opSrcUri.Equals(srcFolderUri))
                {
                  nsCOMPtr<nsIMsgDBHdr> fakeDestHdr;
                  dstFolderDB->GetMsgHdrForKey(offlineOps[opIndex],
                    getter_AddRefs(fakeDestHdr));
                  if (fakeDestHdr)
                    messages->AppendElement(fakeDestHdr, false);
                  break;
                }
              }
            }
          }
        }
        destImapFolder->SetPendingAttributes(messages, isMove);
      }
    }
    // if we can't get the dst folder db, we should still try to playback
    // the offline move/copy.
  }

  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  nsCOMPtr <nsIURI> resultUrl;
  nsCAutoString uids;
  AllocateUidStringFromKeys(aMsgKeys, aNumKeys, uids);
  rv = imapService->OnlineMessageCopy(m_thread, this, uids, aDstFolder,
                                      true, isMove, aUrlListener,
                                      getter_AddRefs(resultUrl), nsnull, aWindow);
  if (resultUrl)
  {
    nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(resultUrl, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr <nsIUrlListener> folderListener = do_QueryInterface(aDstFolder);
    if (folderListener)
      mailnewsUrl->RegisterListener(folderListener);
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::AddMoveResultPseudoKey(nsMsgKey aMsgKey)
{
  nsCOMPtr<nsIMsgDBHdr> pseudoHdr;
  nsresult rv = mDatabase->GetMsgHdrForKey(aMsgKey, getter_AddRefs(pseudoHdr));
  NS_ENSURE_SUCCESS(rv, rv);
  nsCString messageId;
  pseudoHdr->GetMessageId(getter_Copies(messageId));
  // err on the side of caution and ignore messages w/o messageid.
  if (messageId.IsEmpty())
    return NS_OK;
  if (!m_pseudoHdrs.IsInitialized())
    m_pseudoHdrs.Init(10);
  m_pseudoHdrs.Put(messageId, aMsgKey);
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::StoreImapFlags(PRInt32 flags, bool addFlags,
                                               nsMsgKey *keys, PRUint32 numKeys,
                                               nsIUrlListener *aUrlListener)
{
  nsresult rv;
  if (!WeAreOffline())
  {
    nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCAutoString msgIds;
    AllocateUidStringFromKeys(keys, numKeys, msgIds);
    if (addFlags)
      imapService->AddMessageFlags(m_thread, this, aUrlListener ? aUrlListener : this,
                                   nsnull, msgIds, flags, true);
    else
      imapService->SubtractMessageFlags(m_thread, this, aUrlListener ? aUrlListener : this,
                                        nsnull, msgIds, flags, true);
  }
  else
  {
    GetDatabase();
    if (mDatabase)
    {
      PRUint32 total = numKeys;
      for (PRUint32 keyIndex = 0; keyIndex < total; keyIndex++)
      {
        nsCOMPtr <nsIMsgOfflineImapOperation> op;
        rv = mDatabase->GetOfflineOpForKey(keys[keyIndex], true, getter_AddRefs(op));
        SetFlag(nsMsgFolderFlags::OfflineEvents);
        if (NS_SUCCEEDED(rv) && op)
        {
          imapMessageFlagsType newFlags;
          op->GetNewFlags(&newFlags);
          op->SetFlagOperation(addFlags ? newFlags | flags : newFlags & ~flags);
        }
      }
      mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); // flush offline flags
    }
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::LiteSelect(nsIUrlListener *aUrlListener,
                                           nsIMsgWindow *aMsgWindow)
{
  nsresult rv;
  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  return imapService->LiteSelectFolder(m_thread, this, aUrlListener,
                                       aMsgWindow, nsnull);
}

nsresult nsImapMailFolder::GetFolderOwnerUserName(nsACString& userName)
{
  if ((mFlags & nsMsgFolderFlags::ImapPersonal) ||
    !(mFlags & (nsMsgFolderFlags::ImapPublic | nsMsgFolderFlags::ImapOtherUser)))
  {
    // this is one of our personal mail folders
    // return our username on this host
    nsCOMPtr<nsIMsgIncomingServer> server;
    nsresult rv = GetServer(getter_AddRefs(server));
    return NS_FAILED(rv) ? rv : server->GetUsername(userName);
  }

  // the only other type of owner is if it's in the other users' namespace
  if (!(mFlags & nsMsgFolderFlags::ImapOtherUser))
    return NS_OK;

  if (m_ownerUserName.IsEmpty())
  {
    nsCString onlineName;
    GetOnlineName(onlineName);
    m_ownerUserName = nsIMAPNamespaceList::GetFolderOwnerNameFromPath(GetNamespaceForFolder(), onlineName.get());
  }
  userName = m_ownerUserName;
  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
    nsCString serverKey;
    nsCString onlineName;
    GetServerKey(serverKey);
    GetOnlineName(onlineName);
    char hierarchyDelimiter;
    GetHierarchyDelimiter(&hierarchyDelimiter);

    m_namespace = nsIMAPNamespaceList::GetNamespaceForFolder(
                    serverKey.get(), onlineName.get(), hierarchyDelimiter);
    NS_ASSERTION(m_namespace, "didn't get namespace for folder");
    if (m_namespace)
    {
      nsIMAPNamespaceList::SuggestHierarchySeparatorForNamespace(m_namespace, hierarchyDelimiter);
      m_folderIsNamespace = nsIMAPNamespaceList::GetFolderIsNamespace(
                              serverKey.get(), onlineName.get(),
                              hierarchyDelimiter, m_namespace);
    }
  }
  return m_namespace;
}

void nsImapMailFolder::SetNamespaceForFolder(nsIMAPNamespace *ns)
{
#ifdef DEBUG_bienvenu
  NS_ASSERTION(ns, "null namespace");
#endif
  m_namespace = ns;
}

NS_IMETHODIMP nsImapMailFolder::FolderPrivileges(nsIMsgWindow *window)
{
  NS_ENSURE_ARG_POINTER(window);
  nsresult rv ;  // if no window...
#ifdef DEBUG_bienvenu
  m_adminUrl.Assign("http://www.netscape.com");
#endif
  if (!m_adminUrl.IsEmpty())
  {
    nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
    if (extProtService) 
    {
      nsCAutoString scheme;
      nsCOMPtr<nsIURI> uri;
      if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(uri), m_adminUrl.get())))
        return rv;
      uri->GetScheme(scheme);
      if (!scheme.IsEmpty()) 
      {
        // if the URL scheme does not correspond to an exposed protocol, then we
        // need to hand this link click over to the external protocol handler.
        bool isExposed;
        rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
        if (NS_SUCCEEDED(rv) && !isExposed) 
          return extProtService->LoadUrl(uri);
      }
    }
  }
  else
  {
    nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv,rv);
    rv = imapService->GetFolderAdminUrl(m_thread, this, window, this, nsnull);
    if (NS_SUCCEEDED(rv))
      m_urlRunning = true;
  }
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::GetHasAdminUrl(bool *aBool)
{
  NS_ENSURE_ARG_POINTER(aBool);
  nsCOMPtr<nsIImapIncomingServer> imapServer;
  nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
  nsCString manageMailAccountUrl;
  if (NS_SUCCEEDED(rv) && imapServer)
    rv = imapServer->GetManageMailAccountUrl(manageMailAccountUrl);
  *aBool = (NS_SUCCEEDED(rv) && !manageMailAccountUrl.IsEmpty());
  return rv;
}

NS_IMETHODIMP nsImapMailFolder::GetAdminUrl(nsACString& aResult)
{
  aResult = m_adminUrl;
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::SetAdminUrl(const nsACString& 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 nsACString& 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_thread, this, aWindow, command, nsDependentCString(uids), url);
}

NS_IMETHODIMP nsImapMailFolder::FetchCustomMsgAttribute(const nsACString& 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_thread, this, aWindow, attribute, nsDependentCString(uids), url);
}

nsresult nsImapMailFolder::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
                                                   nsIMsgDatabase *sourceDB,
                                                   const nsACString& destFolderUri,
                                                   nsIMsgFilter *filter,
                                                   nsIMsgWindow *msgWindow)
{
  nsresult rv;
  if (m_moveCoalescer)
  {
    nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsIRDFResource> res;
    rv = rdf->GetResource(destFolderUri, getter_AddRefs(res));
    if (NS_FAILED(rv))
      return rv;

    nsCOMPtr<nsIMsgFolder> destIFolder(do_QueryInterface(res, &rv));
    if (NS_FAILED(rv))
      return rv;

    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...
      bool canFileMessages = true;
      nsCOMPtr<nsIMsgFolder> parentFolder;
      destIFolder->GetParent(getter_AddRefs(parentFolder));
      if (parentFolder)
        destIFolder->GetCanFileMessages(&canFileMessages);
      if (filter && (!parentFolder || !canFileMessages))
      {
        filter->SetEnabled(false);
        m_filterList->SaveToDefaultFile();
        destIFolder->ThrowAlertMsg("filterDisabled",msgWindow);
        return NS_MSG_NOT_A_MAIL_FOLDER;
      }
      // put the header into the source db, since it needs to be there when we copy it
      // and we need a valid header to pass to StartAsyncCopyMessagesInto
      nsMsgKey keyToFilter;
      mailHdr->GetMessageKey(&keyToFilter);

      if (sourceDB && destIFolder)
      {
        bool 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, nsTArray<nsMsgKey> pairs here in the imap code.
        // nsTArray<nsMsgKey> *idsToMoveFromInbox = msgFolder->GetImapIdsToMoveFromInbox();
        // idsToMoveFromInbox->AppendElement(keyToFilter);
        if (imapDeleteIsMoveToTrash)
        {
        }
        bool isRead = false;
        mailHdr->GetIsRead(&isRead);
        if (!isRead)
          destIFolder->SetFlag(nsMsgFolderFlags::GotNew);
        if (imapDeleteIsMoveToTrash)
          rv = NS_OK;
      }
    }
  }

  // we have to return an error because we do not actually move the message
  // it is done async and that can fail
  return rv;
}

/**
 * This method assumes that key arrays and flag states are sorted by increasing key.
 */
void nsImapMailFolder::FindKeysToDelete(const nsTArray<nsMsgKey> &existingKeys,
                                        nsTArray<nsMsgKey> &keysToDelete,
                                        nsIImapFlagAndUidState *flagState,
                                        PRUint32 boxFlags)
{
  bool showDeletedMessages = ShowDeletedMessages();
  PRInt32 numMessageInFlagState;
  bool partialUIDFetch;
  PRUint32 uidOfMessage;
  imapMessageFlagsType flags;

  flagState->GetNumberOfMessages(&numMessageInFlagState);
  flagState->GetPartialUIDFetch(&partialUIDFetch);

  // if we're doing a partialUIDFetch, just delete the keys from the db
  // that have the deleted flag set (if not using imap delete model)
  // and return.
  if (partialUIDFetch)
  {
    if (!showDeletedMessages)
    {
      for (PRUint32 i = 0; i < numMessageInFlagState; i++)
      {
        flagState->GetUidOfMessage(i, &uidOfMessage);
        // flag state will be zero filled up to first real uid, so ignore those.
        if (uidOfMessage)
        {
          flagState->GetMessageFlags(i, &flags);
          if (flags & kImapMsgDeletedFlag)
            keysToDelete.AppendElement(uidOfMessage);
        }
      }
    }
    else if (boxFlags & kJustExpunged)
    {
      // we've just issued an expunge with a partial flag state. We should
      // delete headers with the imap deleted flag set, because we can't
      // tell from the expunge response which messages were deleted.
      nsCOMPtr <nsISimpleEnumerator> hdrs;
      nsresult rv = GetMessages(getter_AddRefs(hdrs));
      NS_ENSURE_SUCCESS(rv,);
      bool hasMore = false;
      nsCOMPtr <nsIMsgDBHdr> pHeader;
      while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && hasMore)
      {
        rv = hdrs->GetNext(getter_AddRefs(pHeader));
        NS_ENSURE_SUCCESS(rv,);
        PRUint32 msgFlags;
        pHeader->GetFlags(&msgFlags);
        if (msgFlags & nsMsgMessageFlags::IMAPDeleted)
        {
          nsMsgKey msgKey;
          pHeader->GetMessageKey(&msgKey);
          keysToDelete.AppendElement(msgKey);
        }
      }
    }
    return;
  }
  // otherwise, we have a complete set of uid's and flags, so we delete
  // anything thats in existingKeys but not in the flag state, as well
  // as messages with the deleted flag set.
  PRUint32 total = existingKeys.Length();
  int onlineIndex = 0; // current index into flagState
  for (PRUint32 keyIndex = 0; keyIndex < total; keyIndex++)
  {

    while ((onlineIndex < numMessageInFlagState) &&
         (flagState->GetUidOfMessage(onlineIndex, &uidOfMessage), (existingKeys[keyIndex] > uidOfMessage) ))
      onlineIndex++;

    flagState->GetUidOfMessage(onlineIndex, &uidOfMessage);
    flagState->GetMessageFlags(onlineIndex, &flags);
    // delete this key if it is not there or marked deleted
    if ( (onlineIndex >= numMessageInFlagState ) ||
       (existingKeys[keyIndex] != uidOfMessage) ||
       ((flags & kImapMsgDeletedFlag) && !showDeletedMessages) )
    {
      nsMsgKey doomedKey = existingKeys[keyIndex];
      if ((PRInt32) doomedKey <= 0 && doomedKey != nsMsgKey_None)
        continue;
      else
        keysToDelete.AppendElement(existingKeys[keyIndex]);
    }

    flagState->GetUidOfMessage(onlineIndex, &uidOfMessage);
    if (existingKeys[keyIndex] == uidOfMessage)
      onlineIndex++;
  }
}

void nsImapMailFolder::FindKeysToAdd(const nsTArray<nsMsgKey> &existingKeys, nsTArray<nsMsgKey> &keysToFetch, PRUint32 &numNewUnread, nsIImapFlagAndUidState *flagState)
{
  bool showDeletedMessages = ShowDeletedMessages();
  int dbIndex=0; // current index into existingKeys
  PRInt32 existTotal, numberOfKnownKeys;
  PRInt32 messageIndex;

  numNewUnread = 0;
  existTotal = numberOfKnownKeys = existingKeys.Length();
  flagState->GetNumberOfMessages(&messageIndex);
  bool partialUIDFetch;
  flagState->GetPartialUIDFetch(&partialUIDFetch);

  for (PRInt32 flagIndex=0; flagIndex < messageIndex; flagIndex++)
  {
    PRUint32 uidOfMessage;
    flagState->GetUidOfMessage(flagIndex, &uidOfMessage);
    while ( (flagIndex < numberOfKnownKeys) && (dbIndex < existTotal) &&
        existingKeys[dbIndex] < uidOfMessage)
      dbIndex++;

    if ( (flagIndex >= numberOfKnownKeys)  ||
       (dbIndex >= existTotal) ||
       (existingKeys[dbIndex] != uidOfMessage ) )
    {
      numberOfKnownKeys++;

      imapMessageFlagsType flags;
      flagState->GetMessageFlags(flagIndex, &flags);
      NS_ASSERTION(uidOfMessage != nsMsgKey_None, "got invalid msg key");
      if (uidOfMessage && uidOfMessage != nsMsgKey_None && (showDeletedMessages || ! (flags & kImapMsgDeletedFlag)))
      {
        if (mDatabase)
        {
          bool dbContainsKey;
          if (NS_SUCCEEDED(mDatabase->ContainsKey(uidOfMessage, &dbContainsKey)) &&
              dbContainsKey)
          {
            // this is expected in the partial uid fetch case because the 
            // flag state does not contain all messages, so the db has
            // messages the flag state doesn't know about.
            if (!partialUIDFetch)
              NS_ERROR("db has key - flagState messed up?");
            continue;
          }
        }
        keysToFetch.AppendElement(uidOfMessage);
        if (! (flags & kImapMsgSeenFlag))
          numNewUnread++;
      }
    }
  }
}

NS_IMETHODIMP nsImapMailFolder::GetMsgHdrsToDownload(bool *aMoreToDownload,
                                                     PRInt32 *aTotalCount,
                                                     PRUint32 *aLength,
                                                     nsMsgKey **aKeys)
{
  NS_ENSURE_ARG_POINTER(aMoreToDownload);
  NS_ENSURE_ARG_POINTER(aTotalCount);
  NS_ENSURE_ARG_POINTER(aLength);
  NS_ENSURE_ARG_POINTER(aKeys);

  *aMoreToDownload = false;
  *aTotalCount = m_totalKeysToFetch;
  if (m_keysToFetch.IsEmpty())
  {
    *aLength = 0;
    return NS_OK;
  }

  // if folder isn't open in a window, no reason to limit the number of headers
  // we download.
  nsCOMPtr<nsIMsgMailSession> session = do_GetService(NS_MSGMAILSESSION_CONTRACTID);
  bool folderOpen = false;
  if (session)
    session->IsFolderOpenInWindow(this, &folderOpen);

  PRInt32 hdrChunkSize = 200;
  if (folderOpen)
  {
    nsresult rv;
    nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
    NS_ENSURE_SUCCESS(rv, rv);
    if (prefBranch)
      prefBranch->GetIntPref("mail.imap.hdr_chunk_size", &hdrChunkSize);
  }
  PRInt32 numKeysToFetch = m_keysToFetch.Length();
  PRInt32 startIndex = 0;
  if (folderOpen && hdrChunkSize > 0 && m_keysToFetch.Length() > hdrChunkSize)
  {
    numKeysToFetch = hdrChunkSize;
    *aMoreToDownload = true;
    startIndex = m_keysToFetch.Length() - hdrChunkSize;
  }
  *aKeys = (nsMsgKey *) nsMemory::Clone(&m_keysToFetch[startIndex],
                                       numKeysToFetch * sizeof(nsMsgKey));
  NS_ENSURE_TRUE(*aKeys, NS_ERROR_OUT_OF_MEMORY);
  // Remove these for the incremental header download case, so that
  // we know we don't have to download them again.
  m_keysToFetch.RemoveElementsAt(startIndex, numKeysToFetch);
  *aLength = numKeysToFetch;

  return NS_OK;
}

void nsImapMailFolder::PrepareToAddHeadersToMailDB(nsIImapProtocol* aProtocol)
{
  // now, tell it we don't need any bodies.
  aProtocol->NotifyBodysToDownload(nsnull, 0);
}

void nsImapMailFolder::TweakHeaderFlags(nsIImapProtocol* aProtocol, nsIMsgDBHdr *tweakMe)
{
  if (mDatabase && aProtocol && tweakMe)
  {
    tweakMe->SetMessageKey(m_curMsgUid);
    tweakMe->SetMessageSize(m_nextMessageByteLength);

    bool foundIt = false;
    imapMessageFlagsType imap_flags;

    nsCString customFlags;
    nsresult rv = aProtocol->GetFlagsForUID(m_curMsgUid, &foundIt, &imap_flags, getter_Copies(customFlags));
    if (NS_SUCCEEDED(rv) && foundIt)
    {
      // make a mask and clear these message flags
      PRUint32 mask = nsMsgMessageFlags::Read | nsMsgMessageFlags::Replied |
                      nsMsgMessageFlags::Marked | nsMsgMessageFlags::IMAPDeleted |
                      nsMsgMessageFlags::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 |= nsMsgMessageFlags::Read;
      else // if (imap_flags & kImapMsgRecentFlag)
        newFlags |= nsMsgMessageFlags::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 nsMsgMessageFlags::New => clear kMDNNeeded flag
                   nsMsgMessageFlags::New => do nothing, leave kMDNNeeded on
               */
      PRUint16 userFlags;
      rv = aProtocol->GetSupportedUserFlags(&userFlags);
      if (NS_SUCCEEDED(rv) && (userFlags & (kImapMsgSupportUserFlag |
                            kImapMsgSupportMDNSentFlag)))
      {
        if (imap_flags & kImapMsgMDNSentFlag)
        {
          newFlags |= nsMsgMessageFlags::MDNReportSent;
          if (dbHdrFlags & nsMsgMessageFlags::MDNReportNeeded)
            tweakMe->AndFlags(~nsMsgMessageFlags::MDNReportNeeded, &dbHdrFlags);
        }
      }

      if (imap_flags & kImapMsgAnsweredFlag)
        newFlags |= nsMsgMessageFlags::Replied;
      if (imap_flags & kImapMsgFlaggedFlag)
        newFlags |= nsMsgMessageFlags::Marked;
      if (imap_flags & kImapMsgDeletedFlag)
        newFlags |= nsMsgMessageFlags::IMAPDeleted;
      if (imap_flags & kImapMsgForwardedFlag)
        newFlags |= nsMsgMessageFlags::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, userFlags, customFlags);
    }
  }
}

NS_IMETHODIMP
nsImapMailFolder::SetupMsgWriteStream(nsIFile * aFile, bool addDummyEnvelope)
{
  nsresult rv;
  aFile->Remove(false);
  nsCOMPtr<nsILocalFile>  localFile = do_QueryInterface(aFile, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_tempMessageStream), localFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 00700);
  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(nsIArray *messages, nsIMsgWindow *window)
{
  nsCAutoString messageIds;
  nsTArray<nsMsgKey> 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);

  rv = AcquireSemaphore(static_cast<nsIMsgFolder*>(this));
  if (NS_FAILED(rv))
  {
    ThrowAlertMsg("operationFailedFolderBusy", window);
    return rv;
  }
  return imapService->DownloadMessagesForOffline(messageIds, this, this, window);
}

NS_IMETHODIMP nsImapMailFolder::DownloadAllForOffline(nsIUrlListener *listener, nsIMsgWindow *msgWindow)
{
  nsresult rv;
  nsCOMPtr <nsIURI> runningURI;
  bool noSelect;
  GetFlag(nsMsgFolderFlags::ImapNoselect, &noSelect);

  if (!noSelect)
  {
    nsCAutoString messageIdsToDownload;
    nsTArray<nsMsgKey> msgsToDownload;

    GetDatabase();
    m_downloadingFolderForOfflineUse = true;

    rv = AcquireSemaphore(static_cast<nsIMsgFolder*>(this));
    if (NS_FAILED(rv))
    {
      m_downloadingFolderForOfflineUse = false;
      ThrowAlertMsg("operationFailedFolderBusy", msgWindow);
      return rv;
    }
    nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    // Selecting the folder with nsIImapUrl::shouldStoreMsgOffline true will
    // cause us to fetch any message bodies we don't have.
    m_urlListener = listener;
    rv = imapService->SelectFolder(m_thread, this, this, msgWindow,
                                   getter_AddRefs(runningURI));
    if (NS_SUCCEEDED(rv))
    {
      nsCOMPtr<nsIImapUrl> imapUrl(do_QueryInterface(runningURI));
      if (imapUrl)
        imapUrl->SetStoreResultsOffline(true);
      m_urlRunning = true;
    }
  }
  else
    rv = NS_MSG_FOLDER_UNREADABLE;
  return rv;
}

NS_IMETHODIMP
nsImapMailFolder::ParseAdoptedMsgLine(const char *adoptedMessageLine,
                                      nsMsgKey uidOfMessage,
                                      PRInt32 aMsgSize,
                                      nsIImapUrl *aImapUrl)
{
  NS_ENSURE_ARG_POINTER(aImapUrl);
  PRUint32 count = 0;
  nsresult rv;
  // remember the uid of the message we're downloading.
  m_curMsgUid = uidOfMessage;
  if (!m_offlineHeader)
  {
    rv = GetMessageHeader(uidOfMessage, getter_AddRefs(m_offlineHeader));
    if (NS_SUCCEEDED(rv) && !m_offlineHeader)
      rv = NS_ERROR_UNEXPECTED;
    NS_ENSURE_SUCCESS(rv, rv);
    m_offlineHeader->SetMessageSize(aMsgSize);
    rv = StartNewOfflineMessage();
    NS_ENSURE_SUCCESS(rv, rv);
  }
  // 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.
  const char *nextLine = adoptedMessageLine;
  do
  {
    m_numOfflineMsgLines++;
    nextLine = PL_strstr(nextLine, MSG_LINEBREAK);
    if (nextLine)
      nextLine += MSG_LINEBREAK_LEN;
  }
  while (nextLine && *nextLine);

  if (m_tempMessageStream)
  {
    nsCOMPtr <nsISeekableStream> seekable (do_QueryInterface(m_tempMessageStream));
    if (seekable)
      seekable->Seek(PR_SEEK_END, 0);
    rv = m_tempMessageStream->Write(adoptedMessageLine,
                PL_strlen(adoptedMessageLine), &count);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  return NS_OK;
}

void nsImapMailFolder::EndOfflineDownload()
{
  if (m_tempMessageStream)
  {
    m_tempMessageStream->Close();
    m_tempMessageStream = nsnull;
    ReleaseSemaphore(static_cast<nsIMsgFolder*>(this));
    if (mDatabase)
      mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
  }
  m_offlineHeader = nsnull;
}

NS_IMETHODIMP
nsImapMailFolder::NormalEndMsgWriteStream(nsMsgKey uidOfMessage,
                                          bool markRead,
                                          nsIImapUrl *imapUrl)
{
  if (m_offlineHeader)
    EndNewOfflineMessage();
  m_curMsgUid = uidOfMessage;

  // Apply filter now if it needed a body
  if (m_filterListRequiresBody)
  {
    if (m_filterList)
    {
      nsCOMPtr<nsIMsgDBHdr> newMsgHdr;
      GetMessageHeader(uidOfMessage, getter_AddRefs(newMsgHdr));
      GetMoveCoalescer();
      nsCOMPtr<nsIMsgWindow> msgWindow;
      if (imapUrl)
      {
        nsresult rv;
        nsCOMPtr<nsIMsgMailNewsUrl> msgUrl;
        msgUrl = do_QueryInterface(imapUrl, &rv);
        if (msgUrl && NS_SUCCEEDED(rv))
          msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
      }
      m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr,
                                      this, mDatabase, nsnull, nsnull, this,
                                      msgWindow);
      NotifyFolderEvent(mFiltersAppliedAtom);
    }
    // Process filter plugins and other items normally done at the end of
    // HeaderFetchCompleted.
    bool pendingMoves = m_moveCoalescer && m_moveCoalescer->HasPendingMoves();
    PlaybackCoalescedOperations();

    bool filtersRun;
    CallFilterPlugins(nsnull, &filtersRun);
    PRInt32 numNewBiffMsgs = 0;
    if (m_performingBiff)
      GetNumNewMessages(false, &numNewBiffMsgs);

    if (!filtersRun && m_performingBiff && mDatabase && numNewBiffMsgs > 0 &&
        (!pendingMoves || !ShowPreviewText()))
    {
      // 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(true);

      SetBiffState(nsIMsgFolder::nsMsgBiffState_NewMail);
      if (server)
        server->SetPerformingBiff(false);
      m_performingBiff = false;
    }

    if (m_filterList)
      (void)m_filterList->FlushLogIfNecessary();
  }

  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::AbortMsgWriteStream()
{
  m_offlineHeader = nsnull;
  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)
      return NS_ERROR_FAILURE; // don't assert here...
    nsCString messageIds;
    rv = imapUrl->GetListOfMessageIds(messageIds);
    if (NS_FAILED(rv)) return rv;
    nsCOMPtr<nsIImapService> imapService =  do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv,rv);
    return imapService->AddMessageFlags(m_thread, this, nsnull, nsnull,
                                      messageIds,
                                      kImapMsgDeletedFlag,
                                      true);
  }
  /* unhandled copystate */
  else if (m_copyState) // whoops, this is the wrong folder - should use the source folder
  {
    nsCOMPtr<nsIMsgFolder> srcFolder;
    srcFolder = do_QueryInterface(m_copyState->m_srcSupport, &rv);
    if (srcFolder)
      srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom);
  }
  else
    rv = NS_ERROR_FAILURE;

  return rv;
}

NS_IMETHODIMP
nsImapMailFolder::CloseMockChannel(nsIImapMockChannel * aChannel)
{
  aChannel->Close();
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::ReleaseUrlCacheEntry(nsIMsgMailNewsUrl *aUrl)
{
  NS_ENSURE_ARG_POINTER(aUrl);
  return aUrl->SetMemCacheEntry(nsnull);
}

NS_IMETHODIMP
nsImapMailFolder::BeginMessageUpload()
{
  return NS_ERROR_FAILURE;
}

nsresult nsImapMailFolder::HandleCustomFlags(nsMsgKey uidOfMessage,
                                             nsIMsgDBHdr *dbHdr,
                                             PRUint16 userFlags,
                                             nsCString &keywords)
{
  ToLowerCase(keywords);
  bool messageClassified = true;
  // Mac Mail uses "NotJunk"
  if (keywords.Find("NonJunk", CaseInsensitiveCompare) != -1 ||
      keywords.Find("NotJunk", CaseInsensitiveCompare) != -1)
  {
    nsCAutoString msgJunkScore;
    msgJunkScore.AppendInt(nsIJunkMailPlugin::IS_HAM_SCORE);
    mDatabase->SetStringProperty(uidOfMessage, "junkscore", msgJunkScore.get());
  }
  // ### TODO: we really should parse the keywords into space delimited keywords before checking
  else if (keywords.Find("Junk", CaseInsensitiveCompare) != -1)
  {
    PRUint32 newFlags;
    dbHdr->AndFlags(~nsMsgMessageFlags::New, &newFlags);
    nsCAutoString msgJunkScore;
    msgJunkScore.AppendInt(nsIJunkMailPlugin::IS_SPAM_SCORE);
    mDatabase->SetStringProperty(uidOfMessage, "junkscore", msgJunkScore.get());
  }
  else
    messageClassified = false;
  if (messageClassified)
  {
    // only set the junkscore origin if it wasn't set before. 
    nsCString existingProperty;
    dbHdr->GetStringProperty("junkscoreorigin", getter_Copies(existingProperty));
    if (existingProperty.IsEmpty())
      dbHdr->SetStringProperty("junkscoreorigin", "imapflag");
  }
  return (userFlags & kImapMsgSupportUserFlag) ?
          dbHdr->SetStringProperty("keywords", keywords.get()) : NS_OK;
}

// synchronize the message flags in the database with the server flags
nsresult nsImapMailFolder::SyncFlags(nsIImapFlagAndUidState *flagState)
{
  nsresult rv = GetDatabase(); // we need a database for this
  NS_ENSURE_SUCCESS(rv, rv);
  bool partialUIDFetch;
  flagState->GetPartialUIDFetch(&partialUIDFetch);

  // update all of the database flags
  PRInt32 messageIndex;
  PRUint32 messageSize;

  // Take this opportunity to recalculate the folder size, if we're not a 
  // partial (condstore) fetch.
  PRUint64 newFolderSize = 0;

  flagState->GetNumberOfMessages(&messageIndex);

  PRUint16 supportedUserFlags;
  flagState->GetSupportedUserFlags(&supportedUserFlags);

  for (PRInt32 flagIndex = 0; flagIndex < messageIndex; flagIndex++)
  {
    PRUint32 uidOfMessage;
    flagState->GetUidOfMessage(flagIndex, &uidOfMessage);
    imapMessageFlagsType flags;
    flagState->GetMessageFlags(flagIndex, &flags);
    nsCOMPtr<nsIMsgDBHdr> dbHdr;
    bool 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)))
      newFolderSize += messageSize;

    nsCString keywords;
    if (NS_SUCCEEDED(flagState->GetCustomFlags(uidOfMessage, getter_Copies(keywords))))
        HandleCustomFlags(uidOfMessage, dbHdr, supportedUserFlags, keywords);

    NotifyMessageFlagsFromHdr(dbHdr, uidOfMessage, flags);
  }
  if (!partialUIDFetch && newFolderSize != mFolderSize)
  {
    PRUint32 oldFolderSize = mFolderSize;
    mFolderSize = (PRUint32) newFolderSize;
    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);

  PRUint32 supportedFlags;
  GetSupportedUserFlags(&supportedFlags);
  if (supportedFlags & kImapMsgSupportForwardedFlag)
    mDatabase->MarkForwarded(msgKey, (flags & kImapMsgForwardedFlag) != 0, nsnull);
  // this turns on labels, but it doesn't handle the case where the user
  // unlabels a message on one machine, and expects it to be unlabeled
  // on their other machines. If I turn that on, I'll be removing all the labels
  // that were assigned before we started storing them on the server, which will
  // make some people very unhappy.
  if (flags & kImapMsgLabelFlags)
    mDatabase->SetLabel(msgKey, (flags & kImapMsgLabelFlags) >> 9);
  else
  {
    if (supportedFlags & kImapMsgLabelFlags)
      mDatabase->SetLabel(msgKey, 0);
  }
  if (supportedFlags & kImapMsgSupportMDNSentFlag)
    mDatabase->MarkMDNSent(msgKey, (flags & kImapMsgMDNSentFlag) != 0, nsnull);

  return NS_OK;
}

// message flags operation - this is called from the imap protocol,
// proxied over from the imap thread to the ui thread, when a flag changes
NS_IMETHODIMP
nsImapMailFolder::NotifyMessageFlags(PRUint32 aFlags, nsMsgKey aMsgKey, PRUint64 aHighestModSeq)
{
  if (NS_SUCCEEDED(GetDatabase()) && mDatabase)
  {
    bool msgDeleted = aFlags & kImapMsgDeletedFlag;
    if (aHighestModSeq || msgDeleted)
    {
      nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
      mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
      if (dbFolderInfo)
      {
        if (aHighestModSeq)
        {
          char intStrBuf[40];
          PR_snprintf(intStrBuf, sizeof(intStrBuf), "%llu",  aHighestModSeq);
          dbFolderInfo->SetCharProperty(kModSeqPropertyName, nsDependentCString(intStrBuf));
        }
        if (msgDeleted)
        {
          PRUint32 oldDeletedCount;
          dbFolderInfo->GetUint32Property(kDeletedHdrCountPropertyName, 0, &oldDeletedCount);
          dbFolderInfo->SetUint32Property(kDeletedHdrCountPropertyName, oldDeletedCount + 1);
        }
      }
    }
    nsCOMPtr<nsIMsgDBHdr> dbHdr;
    bool containsKey;
    nsresult rv = mDatabase->ContainsKey(aMsgKey , &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(aMsgKey, getter_AddRefs(dbHdr));
    if(NS_SUCCEEDED(rv) && dbHdr)
      NotifyMessageFlagsFromHdr(dbHdr, aMsgKey, aFlags);

  }
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::NotifyMessageDeleted(const char * onlineFolderName, bool deleteAllMsgs, const char * msgIdString)
{
  if (deleteAllMsgs)
    return NS_OK;

  nsTArray<nsMsgKey> affectedMessages;
  ParseUidString(msgIdString, affectedMessages);

  if (msgIdString && !ShowDeletedMessages())
  {
    GetDatabase();
    NS_ENSURE_TRUE(mDatabase, NS_OK);
    if (!ShowDeletedMessages())
    {
      if (!affectedMessages.IsEmpty()) // perhaps Search deleted these messages
          mDatabase->DeleteMessages(affectedMessages.Length(), affectedMessages.Elements(), nsnull);
    }
    else // && !imapDeleteIsMoveToTrash
      SetIMAPDeletedFlag(mDatabase, affectedMessages, nsnull);
  }
  return NS_OK;
}

bool nsImapMailFolder::ShowDeletedMessages()
{
  nsresult rv;
  nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionList, &rv);
  NS_ENSURE_SUCCESS(rv, false);

  bool showDeleted = false;
  nsCString serverKey;
  GetServerKey(serverKey);
  hostSession->GetShowDeletedMessagesForHost(serverKey.get(), showDeleted);

  return showDeleted;
}

bool nsImapMailFolder::DeleteIsMoveToTrash()
{
  nsresult err;
  nsCOMPtr<nsIImapHostSessionList> hostSession = do_GetService(kCImapHostSessionList, &err);
  NS_ENSURE_SUCCESS(err, true);
  bool rv = true;

  nsCString serverKey;
  GetServerKey(serverKey);
  hostSession->GetDeleteIsMoveToTrashForHost(serverKey.get(), rv);
  return rv;
}

nsresult nsImapMailFolder::GetTrashFolder(nsIMsgFolder **pTrashFolder)
{
  NS_ENSURE_ARG_POINTER(pTrashFolder);
  nsCOMPtr<nsIMsgFolder> rootFolder;
  nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
  if(NS_SUCCEEDED(rv) && rootFolder)
  {
    rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash, pTrashFolder);
    if (!*pTrashFolder)
      rv = NS_ERROR_FAILURE;
  }
  return rv;
}


// store nsMsgMessageFlags::IMAPDeleted in the specified mailhdr records
void nsImapMailFolder::SetIMAPDeletedFlag(nsIMsgDatabase *mailDB, const nsTArray<nsMsgKey> &msgids, bool markDeleted)
{
  nsresult markStatus = 0;
  PRUint32 total = msgids.Length();

  for (PRUint32 msgIndex=0; !markStatus && (msgIndex < total); msgIndex++)
    markStatus = mailDB->MarkImapDeleted(msgids[msgIndex], markDeleted, nsnull);
}

NS_IMETHODIMP
nsImapMailFolder::GetMessageSizeFromDB(const char * id, PRUint32 *size)
{
  NS_ENSURE_ARG_POINTER(size);
  nsresult rv;
  *size = 0;
  (void) GetDatabase();
  if (id && mDatabase)
  {
    PRUint32 key = strtoul(id, nsnull, 10);
    nsCOMPtr<nsIMsgDBHdr> mailHdr;
    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::GetCurMoveCopyMessageInfo(nsIImapUrl *runningUrl,
                                            PRTime *aDate,
                                            nsACString& aKeywords,
                                            PRUint32* aResult)
{
  nsCOMPtr <nsISupports> copyState;
  runningUrl->GetCopyState(getter_AddRefs(copyState));
  if (copyState)
  {
    nsCOMPtr<nsImapMailCopyState> mailCopyState = do_QueryInterface(copyState);
    PRUint32 supportedFlags = 0;
    GetSupportedUserFlags(&supportedFlags);
    if (mailCopyState && mailCopyState->m_message)
    {
      nsMsgLabelValue label;
      mailCopyState->m_message->GetFlags(aResult);
      if (supportedFlags & (kImapMsgSupportUserFlag | kImapMsgLabelFlags))
      {
        mailCopyState->m_message->GetLabel(&label);
        if (label != 0)
          *aResult |= label << 25;
      }
      if (aDate)
        mailCopyState->m_message->GetDate(aDate);
      if (supportedFlags & kImapMsgSupportUserFlag)
      {
        // setup the custom imap keywords, which includes the message keywords
        // plus any junk status
        nsCString junkscore;
        mailCopyState->m_message->GetStringProperty("junkscore",
                                                    getter_Copies(junkscore));
        bool isJunk = false, isNotJunk = false;
        if (!junkscore.IsEmpty())
        {
          if (junkscore.EqualsLiteral("0"))
            isNotJunk = true;
          else
            isJunk = true;
        }

        nsCString keywords; // MsgFindKeyword can't use nsACString
        mailCopyState->m_message->GetStringProperty("keywords",
                                                    getter_Copies(keywords));
        PRInt32 start;
        PRInt32 length;
        bool hasJunk = MsgFindKeyword(NS_LITERAL_CSTRING("junk"),
                                        keywords, &start, &length);
        if (hasJunk && !isJunk)
          keywords.Cut(start, length);
        else if (!hasJunk && isJunk)
          keywords.AppendLiteral(" Junk");
        bool hasNonJunk = MsgFindKeyword(NS_LITERAL_CSTRING("nonjunk"),
                                           keywords, &start, &length);
        if (!hasNonJunk)
          hasNonJunk = MsgFindKeyword(NS_LITERAL_CSTRING("notjunk"),
                                      keywords, &start, &length);
        if (hasNonJunk && !isNotJunk)
          keywords.Cut(start, length);
        else if (!hasNonJunk && isNotJunk)
          keywords.AppendLiteral(" NonJunk");

        // Cleanup extra spaces
        while (!keywords.IsEmpty() && keywords.First() == ' ')
          keywords.Cut(0, 1);
        while (!keywords.IsEmpty() && keywords.Last() == ' ')
          keywords.Cut(keywords.Length() - 1, 1);
        while (!keywords.IsEmpty() &&
               (start = keywords.Find(NS_LITERAL_CSTRING("  "))) >= 0)
          keywords.Cut(start, 1);
        aKeywords.Assign(keywords);
      }
    }
    // if we don't have a source header, and it's not the drafts folder,
    // then mark the message read, since it must be an append to the
    // fcc or templates folder.
    else if (mailCopyState)
    {
      *aResult = mailCopyState->m_newMsgFlags;
      if (supportedFlags & kImapMsgSupportUserFlag)
        aKeywords.Assign(mailCopyState->m_newMsgKeywords);
    }
  }
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
  return NS_OK;
}

NS_IMETHODIMP nsImapMailFolder::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
{
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::OnStartRunningUrl(nsIURI *aUrl)
{
  NS_PRECONDITION(aUrl, "sanity check - need to be be running non-null url");
  nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
  if (mailUrl)
  {
    bool updatingFolder;
    mailUrl->GetUpdatingFolder(&updatingFolder);
    m_updatingFolder = updatingFolder;
  }
  m_urlRunning = true;
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
{
  nsresult rv;
  bool endedOfflineDownload = false;
  nsImapAction imapAction = nsIImapUrl::nsImapTest;
  m_urlRunning = false;
  m_updatingFolder = false;
  nsCOMPtr<nsIMsgMailSession> session =
    do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  if (aUrl)
  {
  nsCOMPtr <nsIImapUrl> imapUrl = do_QueryInterface(aUrl, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  bool downloadingForOfflineUse;
  imapUrl->GetStoreResultsOffline(&downloadingForOfflineUse);
    bool hasSemaphore = false;
    // if we have the folder locked, clear it.
    TestSemaphore(static_cast<nsIMsgFolder*>(this), &hasSemaphore);
    if (hasSemaphore)
      ReleaseSemaphore(static_cast<nsIMsgFolder*>(this));
  if (downloadingForOfflineUse)
  {
    endedOfflineDownload = true;
    EndOfflineDownload();
  }
    nsCOMPtr<nsIMsgWindow> msgWindow;
    nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
    bool folderOpen = false;
    if (mailUrl)
      mailUrl->GetMsgWindow(getter_AddRefs(msgWindow));
    if (session)
      session->IsFolderOpenInWindow(this, &folderOpen);
#ifdef DEBUG_bienvenu1
    nsCString urlSpec;
    aUrl->GetSpec(getter_Copies(urlSpec));
    printf("stop running url %s\n", urlSpec);
#endif

   if (imapUrl)
   {
      DisplayStatusMsg(imapUrl, EmptyString());
      imapUrl->GetImapAction(&imapAction);
      if (imapAction == nsIImapUrl::nsImapMsgFetch || imapAction == nsIImapUrl::nsImapMsgDownloadForOffline)
      {
        ReleaseSemaphore(static_cast<nsIMsgFolder*>(this));
        if (!endedOfflineDownload)
          EndOfflineDownload();
      }

      // Notify move, copy or delete (online operations)
      // Not sure whether nsImapDeleteMsg is even used, deletes in all three models use nsImapAddMsgFlags.
      nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
      if (notifier && m_copyState)
      {
        if (imapAction == nsIImapUrl::nsImapOnlineMove)
          notifier->NotifyMsgsMoveCopyCompleted(true, m_copyState->m_messages, this, nsnull);
        else if (imapAction == nsIImapUrl::nsImapOnlineCopy)
          notifier->NotifyMsgsMoveCopyCompleted(false, m_copyState->m_messages, this, nsnull);
        else if (imapAction == nsIImapUrl::nsImapDeleteMsg)
          notifier->NotifyMsgsDeleted(m_copyState->m_messages);
      }

      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(getter_AddRefs(srcDB));
              if (NS_SUCCEEDED(rv) && srcDB)
              {
                nsRefPtr<nsImapMoveCopyMsgTxn> msgTxn;
                nsTArray<nsMsgKey> srcKeyArray;
                if (m_copyState->m_allowUndo)
                {
                  msgTxn = 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.Length(), srcKeyArray.Elements(), nsnull);
                else
                  MarkMessagesImapDeleted(&srcKeyArray, true, srcDB);
              }
              srcFolder->EnableNotifications(allMessageCountNotifications, true, true/* dbBatching*/);
              // even if we're showing deleted messages,
              // we still need to notify FE so it will show the imap deleted flag
              srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom);
              // is there a way to see that we think we have new msgs?
              nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
              if (NS_SUCCEEDED(rv))
              {
                bool showPreviewText;
                prefBranch->GetBoolPref("mail.biff.alert.show_preview", &showPreviewText);
                // if we're showing preview text, update ourselves if we got a new unread
                // message copied so that we can download the new headers and have a chance
                // to preview the msg bodies.
                if (!folderOpen && showPreviewText && m_copyState->m_unreadCount > 0
                    && ! (mFlags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk)))
                  UpdateFolder(msgWindow);
              }
            }
            else
            {
              srcFolder->EnableNotifications(allMessageCountNotifications, true, 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)
            {
              nsresult rv2 = txnMgr->DoTransaction(m_copyState->m_undoMsgTxn);
              NS_ASSERTION(NS_SUCCEEDED(rv2), "doing transaction failed");
            }
          }
           (void) OnCopyCompleted(m_copyState->m_srcSupport, aExitCode);
        }

        // we're the dest folder of a move/copy - if we're not open in the ui,
        // then we should clear our nsMsgDatabase pointer. Otherwise, the db would
        // be open until the user selected it and then selected another folder.
        // but don't do this for the trash or inbox - we'll leave them open
        if (!folderOpen && ! (mFlags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::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(getter_AddRefs(db));
            if (NS_SUCCEEDED(rv) && db)
            {
              nsTArray<nsMsgKey> keyArray;
              nsCString keyString;
              imapUrl->GetListOfMessageIds(keyString);
              ParseUidString(keyString.get(), keyArray);
              MarkMessagesImapDeleted(&keyArray, false, db);
              db->Commit(nsMsgDBCommitType::kLargeCommit);
            }
          }
        }
        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(getter_AddRefs(db));
              if (NS_SUCCEEDED(rv) && db)
              {
                nsTArray<nsMsgKey> keyArray;
                nsCString keyString;
                imapUrl->GetListOfMessageIds(keyString);
                ParseUidString(keyString.get(), keyArray);
                  
                // Notify listeners of delete.
                if (notifier)
                {
                  nsCOMPtr<nsIMutableArray> msgHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID));
                  MsgGetHeadersFromKeys(db, keyArray, msgHdrs);

                  // XXX Currently, the DeleteMessages below gets executed twice on deletes.
                  // Once in DeleteMessages, once here. The second time, it silently fails
                  // to delete. This is why we're also checking whether the array is empty.
                  PRUint32 numHdrs;
                  msgHdrs->GetLength(&numHdrs);
                  if (numHdrs)
                    notifier->NotifyMsgsDeleted(msgHdrs);
                }

                db->DeleteMessages(keyArray.Length(), keyArray.Elements(), nsnull);
                db->SetSummaryValid(true);
                db->Commit(nsMsgDBCommitType::kLargeCommit);
              }
            }
          }
        }
        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)
              {
                nsCOMPtr<nsIUrlListener> saveUrlListener = m_urlListener;
                if (folderOpen)
                {
                  // This gives a way for the caller to get notified
                  // when the UpdateFolder url is done.
                  if (m_copyState->m_listener)
                    m_urlListener = do_QueryInterface(m_copyState->m_listener);
                }
                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);
                if (folderOpen ||
                    imapAction == nsIImapUrl::nsImapAppendDraftFromFile)
                {
                  UpdateFolderWithListener(msgWindow, m_urlListener);
                  m_urlListener = saveUrlListener;
                }
              }
            }
            else
              //clear the copyState if copy has failed
              (void) OnCopyCompleted(m_copyState->m_srcSupport, aExitCode);
          }
          break;
      case nsIImapUrl::nsImapMoveFolderHierarchy:
        if (m_copyState) // delete folder gets here, but w/o an m_copyState
        {
          nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
          NS_ENSURE_SUCCESS(rv, rv);
          nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(m_copyState->m_srcSupport);
          if (srcFolder)
          {
            nsCOMPtr<nsIMsgFolder> destFolder;
            nsString srcName;
            srcFolder->GetName(srcName);
            GetChildNamed(srcName, getter_AddRefs(destFolder));
            if (destFolder)
              copyService->NotifyCompletion(m_copyState->m_srcSupport, destFolder, aExitCode);
          }
          m_copyState = nsnull;
        }
        break;
      case nsIImapUrl::nsImapRenameFolder:
        if (NS_FAILED(aExitCode))
        {
          nsCOMPtr <nsIAtom> folderRenameAtom;
          folderRenameAtom = MsgGetAtom("RenameCompleted");
          NotifyFolderEvent(folderRenameAtom);
        }
        break;
      case nsIImapUrl::nsImapDeleteAllMsgs:
          if (NS_SUCCEEDED(aExitCode))
          {
            if (folderOpen)
              UpdateFolder(msgWindow);
            else
            {
              ChangeNumPendingTotalMessages(-mNumPendingTotalMessages);
              ChangeNumPendingUnread(-mNumPendingUnreadMessages);
              m_numServerUnseenMessages = 0;
            }

          }
          break;
      case nsIImapUrl::nsImapListFolder:
          if (NS_SUCCEEDED(aExitCode))
          {
            // listing folder will open db; don't leave the db open.
            SetMsgDatabase(nsnull);
            if (!m_verifiedAsOnlineFolder)
            {
              // If folder is not verified, we remove it.
              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 = MsgGetAtom("FolderCreateFailed");
          NotifyFolderEvent(folderCreateAtom);
        }
        break;
      case nsIImapUrl::nsImapSubscribe:
        if (NS_SUCCEEDED(aExitCode) && msgWindow)
        {
          nsCString 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)
              {
                nsCString uri;
                nsCOMPtr <nsIMsgFolder> msgFolder = do_QueryInterface(foundFolder);
                if (msgFolder)
                {
                  msgFolder->GetURI(uri);
                  nsCOMPtr<nsIMsgWindowCommands> windowCommands;
                  msgWindow->GetWindowCommands(getter_AddRefs(windowCommands));
                  if (windowCommands)
                    windowCommands->SelectFolder(uri);
                }
              }
            }
          }
        }
        break;
      case nsIImapUrl::nsImapExpungeFolder:
        m_expunging = false;
        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);

  }
  // if we're not running a url, we must not be getting new mail.
  SetGettingNewMessages(false);
  // don't send OnStopRunning notification if still compacting offline store.
  if (m_urlListener && (imapAction != nsIImapUrl::nsImapExpungeFolder ||
                        !m_compactingOfflineStore))
  {
    nsCOMPtr<nsIUrlListener> saveListener = m_urlListener;
    m_urlListener = nsnull;
    saveListener->OnStopRunningUrl(aUrl, aExitCode);
  }
  return rv;
}

void nsImapMailFolder::UpdatePendingCounts()
{
  if (m_copyState)
  {
    ChangePendingTotal(m_copyState->m_isCrossServerOp ? 1 : m_copyState->m_totalCount);

    // count the moves that were unread
    int numUnread = m_copyState->m_unreadCount;
    if (numUnread)
    {
      m_numServerUnseenMessages += numUnread; // adjust last status count by this delta.
      ChangeNumPendingUnread(numUnread);
    }
    SummaryChanged();
  }
}

NS_IMETHODIMP
nsImapMailFolder::ClearFolderRights()
{
  SetFolderNeedsACLListed(false);
  delete m_folderACL;
  m_folderACL = new nsMsgIMAPFolderACL(this);
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::AddFolderRights(const nsACString& userName, const nsACString& rights)
{
  SetFolderNeedsACLListed(false);
  GetFolderACL()->SetFolderRightsForUser(userName, rights);
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::RefreshFolderRights()
{
  if (GetFolderACL()->GetIsFolderShared())
    SetFlag(nsMsgFolderFlags::PersonalShared);
  else
    ClearFlag(nsMsgFolderFlags::PersonalShared);
  return NS_OK;
}

NS_IMETHODIMP
nsImapMailFolder::SetCopyResponseUid(const char* msgIdString,
                                     nsIImapUrl * aUrl)
{   // CopyMessages() only
  nsresult rv = NS_OK;
  nsRefPtr<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 = mailCopyState->m_undoMsgTxn;
  }
  else if (aUrl && m_pendingOfflineMoves.Length())
  {
    nsCString urlSourceMsgIds, undoTxnSourceMsgIds;
    aUrl->GetListOfMessageIds(urlSourceMsgIds);
    nsRefPtr<nsImapMoveCopyMsgTxn> imapUndo = m_pendingOfflineMoves[0];
    if (imapUndo)
    {
      imapUndo->GetSrcMsgIds(undoTxnSourceMsgIds);
      if (undoTxnSourceMsgIds.Equals(urlSourceMsgIds))
        msgTxn = imapUndo;
      // ### we should handle batched moves, but lets keep it simple for a2.
      m_pendingOfflineMoves.Clear();
    }
  }
  if (msgTxn)
    msgTxn->SetCopyResponseUid(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)
{
  NS_ENSURE_ARG_POINTER(aUrl);
  nsresult rv = GetDatabase();
  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> ..."
  nsCString tokenString(searchHitLine);
  char *currentPosition = PL_strcasestr(tokenString.get(), "SEARCH");
  if (currentPosition)
  {
    currentPosition += strlen("SEARCH");</