/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */

#include "msgCore.h"    // precompiled header...

#include "nsMsgFolder.h"	 
#include "nsMsgFolderFlags.h"
#include "nsIMessage.h"
#include "prprf.h"
#include "nsMsgKeyArray.h"
#include "nsMsgDatabase.h"
#include "nsIDBFolderInfo.h"
#include "nsISupportsArray.h"
#include "nsIPref.h"
#include "nsIRDFService.h"
#include "nsIServiceManager.h"
#include "nsRDFCID.h"
#include "nsXPIDLString.h"
#include "nsCOMPtr.h"
#include "nsIMsgMailSession.h"
#include "nsIMsgAccountManager.h"
#include "nsMsgBaseCID.h"
#include "nsIAllocator.h"
#include "nsIURL.h"
#include "nsMsgUtils.h" // for NS_MsgHashIfNecessary()

static NS_DEFINE_CID(kStandardUrlCID, NS_STANDARDURL_CID);
static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
static NS_DEFINE_CID(kMsgMailSessionCID, NS_MSGMAILSESSION_CID);

PRInt32 nsMsgFolder::gInstanceCount	= 0;

nsIAtom * nsMsgFolder::kTotalMessagesAtom	= nsnull;
nsIAtom * nsMsgFolder::kPrettyNameAtom	= nsnull;
nsIAtom * nsMsgFolder::kNumNewBiffMessagesAtom	= nsnull;
nsIAtom * nsMsgFolder::kBiffStateAtom	= nsnull;
nsIAtom * nsMsgFolder::kTotalUnreadMessagesAtom	= nsnull;
nsIAtom * nsMsgFolder::kFlaggedAtom	= nsnull;
nsIAtom * nsMsgFolder::kStatusAtom	= nsnull;


nsMsgFolder::nsMsgFolder(void)
  : nsRDFResource(),
    mFlags(0),
    mParent(nsnull),
    mNumUnreadMessages(-1),
    mNumTotalMessages(-1),
    mPrefFlags(0),
    mBiffState(nsMsgBiffState_NoMail),
    mNumNewBiffMessages(0),
    mIsServerIsValid(PR_FALSE),
    mIsServer(PR_FALSE)
	{
//  NS_INIT_REFCNT(); done by superclass

  mSemaphoreHolder = NULL;

#ifdef HAVE_DB
	mLastMessageLoaded	= nsMsgKey_None;
#endif
	mNumPendingUnreadMessages = 0;
	mNumPendingTotalMessages  = 0;
	NS_NewISupportsArray(getter_AddRefs(mSubFolders));

	mIsCachable = PR_TRUE;

	mListeners = new nsVoidArray();
	
	mPath = null_nsCOMPtr();
	m_server = nsnull;

  if (gInstanceCount == 0) {
    kBiffStateAtom           = NS_NewAtom("BiffState");
    kNumNewBiffMessagesAtom  = NS_NewAtom("NumNewBiffMessages");
    kPrettyNameAtom          = NS_NewAtom("PrettyName");
    kTotalUnreadMessagesAtom = NS_NewAtom("TotalUnreadMessages");
    kTotalMessagesAtom       = NS_NewAtom("TotalMessages");
    kStatusAtom              = NS_NewAtom("Status");
    kFlaggedAtom             = NS_NewAtom("Flagged");

  }
  
  gInstanceCount++;
}

nsMsgFolder::~nsMsgFolder(void)
{
	if(mSubFolders)
	{
		PRUint32 count;
    nsresult rv = mSubFolders->Count(&count);
    NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed");

		for (int i = count - 1; i >= 0; i--)
			mSubFolders->RemoveElementAt(i);
	}

    delete mListeners;

    gInstanceCount--;
    if (gInstanceCount <= 0) {
      NS_IF_RELEASE(kBiffStateAtom);
      NS_IF_RELEASE(kNumNewBiffMessagesAtom);
      NS_IF_RELEASE(kPrettyNameAtom);
    }
}

NS_IMPL_ADDREF_INHERITED(nsMsgFolder, nsRDFResource)
NS_IMPL_RELEASE_INHERITED(nsMsgFolder, nsRDFResource)

NS_IMETHODIMP nsMsgFolder::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
	if (!aInstancePtr) return NS_ERROR_NULL_POINTER;
	*aInstancePtr = nsnull;
	if (aIID.Equals(nsCOMTypeInfo<nsIMsgFolder>::GetIID()) ||
      aIID.Equals(nsCOMTypeInfo<nsIFolder>::GetIID()))
	{
		*aInstancePtr = NS_STATIC_CAST(nsIMsgFolder*, this);
	}              

	if(*aInstancePtr)
	{
		AddRef();
		return NS_OK;
	}

	return nsRDFResource::QueryInterface(aIID, aInstancePtr);
}

NS_IMETHODIMP
nsMsgFolder::Init(const char* aURI)
{
  // for now, just initialize everything during Init()

  nsresult rv;

  rv = nsRDFResource::Init(aURI);

  return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::Shutdown(PRBool shutdownChildren)
{
	return NS_OK;
}

  // nsICollection methods:
NS_IMETHODIMP
nsMsgFolder::Count(PRUint32 *result) {
  return mSubFolders->Count(result);
}

NS_IMETHODIMP
nsMsgFolder::GetElementAt(PRUint32 i, nsISupports* *result) {
  return mSubFolders->GetElementAt(i, result);
}

NS_IMETHODIMP
nsMsgFolder::QueryElementAt(PRUint32 i, const nsIID & iid, void * *result) {
  return mSubFolders->QueryElementAt(i, iid, result);
}

NS_IMETHODIMP
nsMsgFolder::SetElementAt(PRUint32 i, nsISupports* value) {
  return mSubFolders->SetElementAt(i, value);
}

NS_IMETHODIMP
nsMsgFolder::AppendElement(nsISupports *aElement) {
  return mSubFolders->AppendElement(aElement);
}

NS_IMETHODIMP
nsMsgFolder::RemoveElement(nsISupports *aElement) {
  return mSubFolders->RemoveElement(aElement);
}

NS_IMETHODIMP
nsMsgFolder::Enumerate(nsIEnumerator* *result) {
  // nsMsgFolders only have subfolders, no message elements
  return mSubFolders->Enumerate(result);
}

NS_IMETHODIMP
nsMsgFolder::Clear(void) {
  return mSubFolders->Clear();
}

NS_IMETHODIMP
nsMsgFolder::GetURI(char* *name) {
  return nsRDFResource::GetValue(name);
}


////////////////////////////////////////////////////////////////////////////////

typedef PRBool
(*nsArrayFilter)(nsISupports* element, void* data);

static nsresult
nsFilterBy(nsISupportsArray* array, nsArrayFilter filter, void* data,
           nsISupportsArray* *result)
{
  nsCOMPtr<nsISupportsArray> f;
  nsresult rv = NS_NewISupportsArray(getter_AddRefs(f));
  if (NS_FAILED(rv)) return rv;
  PRUint32 cnt;
  rv = array->Count(&cnt);
  if (NS_FAILED(rv)) return rv;
  for (PRUint32 i = 0; i < cnt; i++) {
    nsCOMPtr<nsISupports> element = getter_AddRefs(array->ElementAt(i));
    if (filter(element, data)) {
      rv = f->AppendElement(element);
      if (NS_FAILED(rv)) {
        return rv;
      }
    }
  }
  *result = f;
  return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////

NS_IMETHODIMP 
nsMsgFolder::AddUnique(nsISupports* element)
{
  // XXX fix this
  return mSubFolders->AppendElement(element);
}

// I'm assuming this means "Replace Subfolder"?
NS_IMETHODIMP
nsMsgFolder::ReplaceElement(nsISupports* element, nsISupports* newElement)
{
  PRBool success=PR_FALSE;
  PRInt32 location = mSubFolders->IndexOf(element);
  if (location>0)
    success = mSubFolders->ReplaceElementAt(newElement, location);
  
  return success ? NS_OK : NS_ERROR_UNEXPECTED;
}

NS_IMETHODIMP
nsMsgFolder::GetSubFolders(nsIEnumerator* *result)
{
  return mSubFolders->Enumerate(result);
}

NS_IMETHODIMP 
nsMsgFolder::FindSubFolder(const char *subFolderName, nsIFolder **aFolder)
{
	nsresult rv = NS_OK;
	NS_WITH_SERVICE(nsIRDFService, rdf, kRDFServiceCID, &rv);
  
	if(NS_FAILED(rv)) 
		return rv;

  // XXX use necko here
	nsCAutoString uri("");
	uri.Append(mURI);
	uri.Append('/');

	uri.Append(subFolderName);

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

	nsCOMPtr<nsIFolder> folder(do_QueryInterface(res, &rv));
	if (NS_FAILED(rv))
		return rv;
	if (aFolder)
	{
		*aFolder = folder;
		NS_ADDREF(*aFolder);
		return NS_OK;
	}
	else
		return NS_ERROR_NULL_POINTER;
}

NS_IMETHODIMP
nsMsgFolder::GetHasSubFolders(PRBool *_retval)
{
  PRUint32 cnt;
  nsresult rv = mSubFolders->Count(&cnt);
  if (NS_FAILED(rv)) return rv;
  *_retval = (cnt > 0);
  return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::AddFolderListener(nsIFolderListener * listener)
{
	mListeners->AppendElement(listener);
	return NS_OK;

}

NS_IMETHODIMP nsMsgFolder::RemoveFolderListener(nsIFolderListener * listener)
{

  mListeners->RemoveElement(listener);
  return NS_OK;

}

NS_IMETHODIMP nsMsgFolder::SetParent(nsIFolder *parent)
{
	//Don't addref due to ownership issues.
	mParent = parent;
	return NS_OK;
}


NS_IMETHODIMP nsMsgFolder::GetParent(nsIFolder **parent)
{
	if(!parent)
		return NS_ERROR_NULL_POINTER;

	*parent = mParent;
	NS_IF_ADDREF(*parent);
	return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetMessages(nsISimpleEnumerator* *result)
{
  // XXX should this return an empty enumeration?
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsMsgFolder::StartFolderLoading(void)
{
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::EndFolderLoading(void)
{
	return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::UpdateFolder(nsIMsgWindow *)
{
  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetThreads(nsISimpleEnumerator ** threadEnumerator)
{
  // XXX should this return an empty enumeration?
	return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsMsgFolder::GetThreadForMessage(nsIMessage *message, nsIMsgThread **thread)
{
	return NS_ERROR_FAILURE;	
}

NS_IMETHODIMP
nsMsgFolder::HasMessage(nsIMessage *message, PRBool *hasMessage)
{
	return NS_ERROR_FAILURE;	
}

////////////////////////////////////////////////////////////////////////////////

NS_IMETHODIMP nsMsgFolder::GetFolderURL(char **url)
{
	if(*url)
	{
		*url = NULL;
		return NS_OK;
	}
	else
		return NS_ERROR_NULL_POINTER;	
}

#ifdef HAVE_DB
// this class doesn't have a url
NS_IMETHODIMP nsMsgFolder::BuildUrl(nsMsgDatabase *db, nsMsgKey key, char ** url)
{
	if(*url)
	{
		*url = NULL;
		return NS_OK;
	}
	else
		return NS_ERROR_NULL_POINTER;
}
#endif

NS_IMETHODIMP nsMsgFolder::GetServer(nsIMsgIncomingServer ** aServer)
{
  NS_ENSURE_ARG_POINTER(aServer);

  nsresult rv;

  // short circut the server if we have it.
  if (m_server) {
    *aServer = m_server;
    NS_ADDREF(*aServer);
    return NS_OK;
  }

  // ok, we lost. Time to look it up
  rv = parseURI(PR_TRUE);
  
  // done getting m_server
  *aServer = m_server;
  NS_IF_ADDREF(*aServer);
  
	return NS_OK;
}

nsresult
nsMsgFolder::parseURI(PRBool needServer)
{
  nsresult rv;
  nsCOMPtr<nsIURL> url;
  rv = nsComponentManager::CreateInstance(kStandardUrlCID, nsnull,
                                          NS_GET_IID(nsIURL),
                                          (void **)getter_AddRefs(url));
  if (NS_FAILED(rv)) return rv;
  
  rv = url->SetSpec(mURI);
  if (NS_FAILED(rv)) return rv;

  //
  // pull some info out of the URI
  //
  
  // empty path tells us it's a server.
  if (!mIsServerIsValid) {
    nsXPIDLCString path;
    rv = url->GetPath(getter_Copies(path));
    if (NS_SUCCEEDED(rv)) {
      if (!nsCRT::strcmp(path, "/"))
        mIsServer = PR_TRUE;
      else
        mIsServer = PR_FALSE;
    }
    mIsServerIsValid = PR_TRUE;
  }

  // grab the name off the leaf of the server
  if (mName.IsEmpty()) {
    // mName:
    // the name is the trailing directory in the path
    nsXPIDLCString fileName;
    rv = url->GetFileName(getter_Copies(fileName));
    if (NS_SUCCEEDED(rv)) {
      // XXX conversion to unicode here? is fileName in UTF8?
      mName = fileName;
    }
  }

  // grab the server by parsing the URI and looking it up
  // in the account manager...
  // But avoid this extra work by first asking the parent, if any

  if (!m_server) {
    
    nsCOMPtr<nsIMsgIncomingServer> server;
    // first try asking the parent instead of the URI
    nsCOMPtr<nsIFolder> parent;
    rv = GetParent(getter_AddRefs(parent));
    
    if (NS_SUCCEEDED(rv) && parent) {
      nsCOMPtr<nsIMsgFolder> parentMsgFolder =
        do_QueryInterface(parent, &rv);

      if (NS_SUCCEEDED(rv))
        rv = parentMsgFolder->GetServer(getter_AddRefs(server));
    }

    // no parent. do the extra work of asking
    if (!server && needServer) {
      // Get username and hostname so we can get the server
      nsXPIDLCString userName;
      rv = url->GetPreHost(getter_Copies(userName));
      if (NS_SUCCEEDED(rv) && (const char*)userName)
        nsUnescape(NS_CONST_CAST(char*,(const char*)userName));
      
      nsXPIDLCString hostName;
      rv = url->GetHost(getter_Copies(hostName));
      if (NS_SUCCEEDED(rv) && (const char*)hostName)
        nsUnescape(NS_CONST_CAST(char*,(const char*)hostName));
      
      // turn it back into a server:
      
      NS_WITH_SERVICE(nsIMsgAccountManager, accountManager,
                      NS_MSGACCOUNTMANAGER_PROGID, &rv);
      if (NS_FAILED(rv)) return rv;

#ifdef DEBUG_alecf
      // this is a failing case, and it would be nice if this
      // was never called
      // (we should ALWAYS handle this case, but try to design
      // the code so we have a parent wherever possible)
      printf("No parent->");
#endif
      rv = accountManager->FindServer(userName,
                                      hostName,
                                      GetIncomingServerType(),
                                      getter_AddRefs(server));
      
      if (NS_FAILED(rv)) return rv;
      
    }

    // keep weak ref to server - do not addref!
    m_server = server;

  } /* !m_server */
    
  // now try to find the local path for this folder
  if (m_server) {
    
    nsXPIDLCString urlPath;
    url->GetFilePath(getter_Copies(urlPath));

    // transform the filepath from the URI, such as
    // "/folder1/folder2/foldern"
    // to
    // "folder1.sbd/folder2.sbd/foldern"
    // (remove leading / and add .sbd to first n-1 folders)
    // to be appended onto the server's path
      
    nsCAutoString newPath("");

    char *newStr=nsnull;
    nsCAutoString hashedToken;
    char *token =
      nsCRT::strtok(NS_CONST_CAST(char *,(const char*)urlPath), "/", &newStr);

    // trick to make sure we only add the path to the first n-1 folders
    PRBool haveFirst=PR_FALSE;
    while (token) {
      // skip leading '/' (and other // style things)
      if (nsCRT::strcmp(token, "")!=0) {

        // add .sbd onto the previous path
        if (haveFirst) {
          newPath+=".sbd";
          newPath += "/";
        }
        
        hashedToken = token;
        NS_MsgHashIfNecessary(hashedToken);
        newPath += hashedToken;
        haveFirst=PR_TRUE;
      }
      
      token = nsCRT::strtok(newStr, "/", &newStr);
    }

    // now append munged path onto server path
    nsCOMPtr<nsIFileSpec> serverPath;
    rv = m_server->GetLocalPath(getter_AddRefs(serverPath));
    if (NS_FAILED(rv)) return rv;

    if (serverPath) {
      rv = serverPath->AppendRelativeUnixPath(newPath.GetBuffer());
      NS_ASSERTION(NS_SUCCEEDED(rv),"failed to append to the serverPath");
      if (NS_FAILED(rv)) {
      	mPath = null_nsCOMPtr();
      	return rv;
      }
      mPath = serverPath;
    }
  }
    
  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetIsServer(PRBool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);

  // make sure we've parsed the URI
  if (!mIsServerIsValid) {
    nsresult rv = parseURI();
    if (NS_FAILED(rv) || !mIsServerIsValid)
      return NS_ERROR_FAILURE;
  }
    
  *aResult = mIsServer;
  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetCanSubscribe(PRBool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  // by default, you can't subscribe. 
  // if otherwise, override it.
  *aResult = PR_FALSE;
  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetCanFileMessages(PRBool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  
  PRBool isServer = PR_FALSE;
  nsresult rv = GetIsServer(&isServer);
  if (NS_FAILED(rv)) return rv;
  
  // by default, you can't file messages into servers, only to folders
  // if otherwise, override it.
  *aResult = !isServer;
  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetCanCreateSubfolders(PRBool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);
  // by default, you can create subfolders on server and folders
  // if otherwise, override it.
  *aResult = PR_TRUE;
  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::GetCanRename(PRBool *aResult)
{
  NS_ENSURE_ARG_POINTER(aResult);

  PRBool isServer = PR_FALSE;
  nsresult rv = GetIsServer(&isServer);
  if (NS_FAILED(rv)) return rv;
  	
  // by default, you can't rename servers, only folders
  // if otherwise, override it.
  if (isServer) {
	*aResult = PR_FALSE;
  }
  // old comment, from the 4.x code base:
  // Here's a weird case necessitated because we don't have a separate
  // preference for any folder name except the FCC folder (Sent). Others
  // are known by name, and as such, can't be renamed. I guess.
  //
  // new comment:
  // we have prefs for drafts and templates now, can we remove those
  // parts of this case?
  else if (mFlags & MSG_FOLDER_FLAG_TRASH ||
      mFlags & MSG_FOLDER_FLAG_DRAFTS ||
      mFlags & MSG_FOLDER_FLAG_QUEUE ||
      mFlags & MSG_FOLDER_FLAG_INBOX ||
      mFlags & MSG_FOLDER_FLAG_TEMPLATES) {
        *aResult = PR_FALSE;
  }
  else {
	*aResult = PR_TRUE;
  }
  return NS_OK;
}



NS_IMETHODIMP nsMsgFolder::GetPrettyName(PRUnichar ** name)
{
	if (!name)
		return NS_ERROR_NULL_POINTER;
	*name = mName.ToNewUnicode();
	return (*name) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}

NS_IMETHODIMP nsMsgFolder::SetPrettyName(const PRUnichar *name)
{

  NotifyUnicharPropertyChanged(kPrettyNameAtom, mName.GetUnicode(), name);
  mName = name;
  return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetName(PRUnichar **name)
{
	if (!name)
		return NS_ERROR_NULL_POINTER;

  nsresult rv;
  if (mName.IsEmpty()) {
    rv = parseURI();
    if (NS_FAILED(rv)) return rv;
  }
  
  *name = mName.ToNewUnicode();
  
  if (!(*name)) return NS_ERROR_OUT_OF_MEMORY;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::SetName(const PRUnichar * name)
{
  // override the URI-generated name
	mName = name;
	return NS_OK;
}

//For default, just return name
NS_IMETHODIMP nsMsgFolder::GetAbbreviatedName(PRUnichar * *aAbbreviatedName)
{
	return GetName(aAbbreviatedName);
}

NS_IMETHODIMP nsMsgFolder::GetChildNamed(const char *name, nsISupports ** aChild)
{
	NS_ASSERTION(aChild, "NULL child");
	nsresult rv;
	// will return nsnull if we can't find it
	*aChild = nsnull;

	nsCOMPtr<nsIMsgFolder> folder;

	PRUint32 count;
	rv = mSubFolders->Count(&count);
	if (NS_FAILED(rv)) return rv;

	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
		folder = do_QueryInterface(supports, &rv);
		if(NS_SUCCEEDED(rv))
		{
			PRUnichar *folderName = nsnull;

			folder->GetName(&folderName);
			// case-insensitive compare is probably LCD across OS filesystems
			if (folderName && nsCRT::strcasecmp(folderName, name)==0)
			{
				*aChild = folder;
				NS_ADDREF(*aChild);
				delete[] folderName;
				return NS_OK;
			}
			delete[] folderName;
		}
	}
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetChildWithURI(const char *uri, PRBool deep, nsIMsgFolder ** child)
{
	NS_ASSERTION(child, "NULL child");
	nsresult rv;
	// will return nsnull if we can't find it
	*child = nsnull;


	PRUint32 count;
	rv = mSubFolders->Count(&count);
	if (NS_FAILED(rv)) return rv;

	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
		nsCOMPtr<nsIRDFResource> folderResource = do_QueryInterface(supports);
		nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports);
		if(folderResource  && folder)
		{
			char *folderURI =nsnull;
			rv = folderResource->GetValue(&folderURI);
			if(NS_FAILED(rv)) return rv;

			// case-insensitive compare is probably LCD across OS filesystems
			PRBool equal = (folderURI && nsCRT::strcasecmp(folderURI, uri)==0);
			nsAllocator::Free(folderURI);
			if (equal)
			{
				*child = folder;
				NS_ADDREF(*child);
				return NS_OK;
			}
			else if(deep)
			{
				rv = folder->GetChildWithURI(uri, deep, child);
				if(NS_FAILED(rv))
					return rv;

				if(*child)
					return NS_OK;
			}
		}
  }

  return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetPrettiestName(PRUnichar **name)
{
  if (NS_SUCCEEDED(GetPrettyName(name)))
    return NS_OK;
  return GetName(name);
}

static PRBool
nsCanBeInFolderPane(nsISupports* element, void* data)
{
#ifdef HAVE_PANE
  nsIMsgFolder* subFolder = NS_STATIC_CAST(nsIMsgFolder*, element);
  return subFolder->CanBeInFolderPane(); 
#else
  return PR_TRUE;
#endif
}

NS_IMETHODIMP
nsMsgFolder::GetVisibleSubFolders(nsIEnumerator* *result) 
{
  nsresult rv;
  nsCOMPtr<nsISupportsArray> vFolders;
  rv = nsFilterBy(mSubFolders, nsCanBeInFolderPane, nsnull, getter_AddRefs(vFolders));
  if (NS_FAILED(rv)) return rv;
  rv = vFolders->Enumerate(result);
  return rv;
}

#ifdef HAVE_ADMINURL
NS_IMETHODIMP nsMsgFolder::GetAdminUrl(MWContext *context, MSG_AdminURLType type)
{
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::HaveAdminUrl(MSG_AdminURLType type, PRBool *haveAdminUrl)
{
	if(haveAdminUrl)
	{
		*haveAdminUrl = PR_FALSE;
		return NS_OK;
	}
	return
		NS_ERROR_NULL_POINTER;
}
#endif

NS_IMETHODIMP nsMsgFolder::GetDeleteIsMoveToTrash(PRBool *deleteIsMoveToTrash)
{
	if(deleteIsMoveToTrash)
	{
		*deleteIsMoveToTrash = PR_FALSE;
		return NS_OK;
	}
	return
		NS_ERROR_NULL_POINTER;
}

NS_IMETHODIMP nsMsgFolder::GetShowDeletedMessages(PRBool *showDeletedMessages)
{
	if(showDeletedMessages)
	{
		*showDeletedMessages = PR_FALSE;
		return NS_OK;
	}
	return
		NS_ERROR_NULL_POINTER;
}


NS_IMETHODIMP nsMsgFolder::OnCloseFolder ()
{
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::Delete ()
{
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::DeleteSubFolders(nsISupportsArray *folders)
{
	nsresult rv;

	PRUint32 count;
	rv = folders->Count(&count);
	nsCOMPtr<nsIMsgFolder> folder;
	for(PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(folders->ElementAt(i));
		folder = do_QueryInterface(supports);
		if(folder)	
			PropagateDelete(folder, PR_TRUE);
	}
	return rv;

}

NS_IMETHODIMP nsMsgFolder::PropagateDelete(nsIMsgFolder *folder, PRBool deleteStorage)
{
	nsresult status = NS_OK;

	nsCOMPtr<nsIMsgFolder> child;

	// first, find the folder we're looking to delete
	PRUint32 cnt;
	nsresult rv = mSubFolders->Count(&cnt);
	if (NS_FAILED(rv)) return rv;
	for (PRUint32 i = 0; i < cnt; i++)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
		child = do_QueryInterface(supports, &status);
		if(NS_SUCCEEDED(status))
		{
			if (folder == child.get())
			{
				// maybe delete disk storage for it, and its subfolders
				status = child->RecursiveDelete(deleteStorage);	

				if (status == NS_OK) 
				{

					//Remove from list of subfolders.
					mSubFolders->RemoveElement(supports);
					//Remove self as parent
					child->SetParent(nsnull);
					nsCOMPtr<nsISupports> childSupports(do_QueryInterface(child));
					nsCOMPtr<nsISupports> folderSupports;
					rv = QueryInterface(nsCOMTypeInfo<nsISupports>::GetIID(), getter_AddRefs(folderSupports));
					if(childSupports && NS_SUCCEEDED(rv))
						NotifyItemDeleted(folderSupports, childSupports, "folderView");
					break;
				}
			}
			else
			{
				status = child->PropagateDelete (folder, deleteStorage);
			}
		}
	}
	
	return status;
}

NS_IMETHODIMP nsMsgFolder::RecursiveDelete(PRBool deleteStorage)
{
	// If deleteStorage is PR_TRUE, recursively deletes disk storage for this folder
	// and all its subfolders.
	// Regardless of deleteStorage, always unlinks them from the children lists and
	// frees memory for the subfolders but NOT for _this_

	nsresult status = NS_OK;
	
	PRUint32 cnt;
  nsresult rv = mSubFolders->Count(&cnt);
  if (NS_FAILED(rv)) return rv;
  while (cnt > 0)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(0));
		nsCOMPtr<nsIMsgFolder> child(do_QueryInterface(supports, &status));

		if(NS_SUCCEEDED(status))
		{
			status = child->RecursiveDelete(deleteStorage);  // recur
			mSubFolders->RemoveElement(child);  // unlink it from this's child list
			child->SetParent(nsnull);
			nsCOMPtr<nsISupports> childSupports(do_QueryInterface(child));
			nsCOMPtr<nsISupports> folderSupports;
			rv = QueryInterface(nsCOMTypeInfo<nsISupports>::GetIID(), getter_AddRefs(folderSupports));
			if(childSupports && NS_SUCCEEDED(rv))
				NotifyItemDeleted(folderSupports, childSupports, "folderView");
		}
		cnt--;
	}

	// now delete the disk storage for _this_
	if (deleteStorage && (status == NS_OK))
		status = Delete();

	return status;
}

NS_IMETHODIMP nsMsgFolder::CreateSubfolder(const char *folderName)
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult nsMsgFolder::AddSubfolder(nsAutoString *folderName,
                                   nsIMsgFolder** newFolder)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP nsMsgFolder::Compact()
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP nsMsgFolder::EmptyTrash()
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP nsMsgFolder::Rename(const char *name)
{
    nsresult status = NS_OK;
	nsAutoString2 unicharString(name);
	status = SetName((PRUnichar *) unicharString.GetUnicode());
	//After doing a SetName we need to make sure that broadcasting this message causes a
	//new sort to happen.
#ifdef HAVE_MASTER
	if (m_master)
		m_master->BroadcastFolderChanged(this);
#endif
	return status;

}

NS_IMETHODIMP nsMsgFolder::Adopt(nsIMsgFolder *srcFolder, PRUint32* outPos)
{
	return NS_OK;

}

NS_IMETHODIMP nsMsgFolder::ContainsChildNamed(const char *name, PRBool* containsChild)
{
	nsCOMPtr<nsISupports> child;
	
	if(containsChild)
	{
		*containsChild = PR_FALSE;
		if(NS_SUCCEEDED(GetChildNamed(name, getter_AddRefs(child))))
		{
			*containsChild = child != nsnull;
		}
		return NS_OK;
	}
	else
		return NS_ERROR_NULL_POINTER;
}



NS_IMETHODIMP nsMsgFolder::IsAncestorOf(nsIMsgFolder *child, PRBool *isAncestor)
{
	if(!isAncestor)
		return NS_ERROR_NULL_POINTER;
	
	nsresult rv = NS_OK;

	PRUint32 count;
  rv = mSubFolders->Count(&count);
  if (NS_FAILED(rv)) return rv;

	for (PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
		nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(supports, &rv));
		if(NS_SUCCEEDED(rv))
		{
			if (folder.get() == child )
			{
				*isAncestor = PR_TRUE;
			}
			else
				folder->IsAncestorOf(child, isAncestor);

		}
		if(*isAncestor)
			return NS_OK;
    }
	*isAncestor = PR_FALSE;
	return rv;

}


NS_IMETHODIMP nsMsgFolder::GenerateUniqueSubfolderName(const char *prefix, nsIMsgFolder *otherFolder,
                                                       char **name)
{
	if(!name)
		return NS_ERROR_NULL_POINTER;

	/* only try 256 times */
	for (int count = 0; (count < 256); count++)
	{
		PRUint32 prefixSize = PL_strlen(prefix);

		//allocate string big enough for prefix, 256, and '\0'
		char *uniqueName = (char*)PR_MALLOC(prefixSize + 4);
		if(!uniqueName)
			return NS_ERROR_OUT_OF_MEMORY;
		PR_snprintf(uniqueName, prefixSize + 4, "%s%d",prefix,count);
		PRBool containsChild;
		PRBool otherContainsChild = PR_FALSE;

		ContainsChildNamed(uniqueName, &containsChild);
		if(otherFolder)
		{
			((nsIMsgFolder*)otherFolder)->ContainsChildNamed(uniqueName, &otherContainsChild);
		}

		if (!containsChild && !otherContainsChild)
		{
			*name = uniqueName;
			return NS_OK;
		}
		else
			PR_FREEIF(uniqueName);
	}
	*name = nsnull;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::UpdateSummaryTotals(PRBool /* force */)
{
	//We don't support this
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::SummaryChanged()
{
	UpdateSummaryTotals(PR_FALSE);
#ifdef HAVE_MASTER
    if (mMaster)
        mMaster->BroadcastFolderChanged(this);
#endif
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetNumUnread(PRBool deep, PRInt32 *numUnread)
{
	if(!numUnread)
		return NS_ERROR_NULL_POINTER;

	nsresult rv;
	PRUint32 total = mNumUnreadMessages + mNumPendingUnreadMessages;
	if (deep)
	{
		nsCOMPtr<nsIMsgFolder> folder;
		PRUint32 count;
    rv = mSubFolders->Count(&count);
    if (NS_SUCCEEDED(rv)) {

      for (PRUint32 i = 0; i < count; i++)
      {
        nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
        folder = do_QueryInterface(supports, &rv);
        if(NS_SUCCEEDED(rv))
        {
          PRInt32 num;
          folder->GetNumUnread(deep, &num);
          if (num >= 0) // it's legal for counts to be negative if we don't know
            total += num;
        }
      }
    }
	}
  *numUnread = total;
	return NS_OK;

}

NS_IMETHODIMP nsMsgFolder::GetTotalMessages(PRBool deep, PRInt32 *totalMessages)
{
	if(!totalMessages)
		return NS_ERROR_NULL_POINTER;

	nsresult rv;
	PRInt32 total = mNumTotalMessages + mNumPendingTotalMessages;
	if (deep)
	{
		nsCOMPtr<nsIMsgFolder> folder;
		PRUint32 count;
    rv = mSubFolders->Count(&count);
    if (NS_SUCCEEDED(rv)) {

      for (PRUint32 i = 0; i < count; i++)
      {
        nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
        folder = do_QueryInterface(supports, &rv);
        if(NS_SUCCEEDED(rv))
        {
          PRInt32 num;
          folder->GetTotalMessages (deep, &num);
          if (num >= 0) // it's legal for counts to be negative if we don't know
            total += num;
        }
			}
		}
	}
  *totalMessages = total;
  return NS_OK;
}

#ifdef HAVE_DB
NS_IMETHOD GetTotalMessagesInDB(PRUint32 *totalMessages) const;					// How many messages in database.
#endif
	
PRInt32 nsMsgFolder::GetNumPendingUnread() 
{
	return mNumPendingUnreadMessages;
}

PRInt32 nsMsgFolder::GetNumPendingTotalMessages() 
{
	return mNumPendingTotalMessages;
}

	
void nsMsgFolder::ChangeNumPendingUnread(PRInt32 delta)
{
	if(delta)
	{
		PRInt32 oldUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages;
		mNumPendingUnreadMessages += delta;
		PRInt32 newUnreadMessages = mNumUnreadMessages + mNumPendingUnreadMessages;

		NotifyIntPropertyChanged(kTotalUnreadMessagesAtom, oldUnreadMessages, newUnreadMessages);
	}
}

void nsMsgFolder::ChangeNumPendingTotalMessages(PRInt32 delta)
{
	if(delta)
	{
		PRInt32 oldTotalMessages = mNumTotalMessages + mNumPendingTotalMessages;
		mNumPendingTotalMessages += delta;
		PRInt32 newTotalMessages = mNumTotalMessages + mNumPendingTotalMessages;

		NotifyIntPropertyChanged(kTotalMessagesAtom, oldTotalMessages, newTotalMessages);
	}

}

#ifdef HAVE_DB	
// These functions are used for tricking the front end into thinking that we have more 
// messages than are really in the DB.  This is usually after and IMAP message copy where
// we don't want to do an expensive select until the user actually opens that folder
// These functions are called when MSG_Master::GetFolderLineById is populating a MSG_FolderLine
// struct used by the FE

NS_IMETHODIMP nsMsgFolder::SetFolderPrefFlags(PRUint32 flags)
{

}

NS_IMETHODIMP nsMsgFolder::GetFolderPrefFlags(PRUint32 *flags)
{

}


NS_IMETHODIMP nsMsgFolder::SetLastMessageLoaded(nsMsgKey lastMessageLoaded)
{

}

NS_IMETHODIMP nsMsgFolder::GetLastMessageLoaded()
{

}

#endif

NS_IMETHODIMP nsMsgFolder::SetFlag(PRUint32 flag)
{
	// OnFlagChange can be expensive, so don't call it if we don't need to
	PRBool flagSet;
	nsresult rv;

	if(!NS_SUCCEEDED(rv = GetFlag(flag, &flagSet)))
		return rv;

	if (!flagSet)
	{
		mFlags |= flag;
		OnFlagChange(flag);
	}

	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::ClearFlag(PRUint32 flag)
{
	// OnFlagChange can be expensive, so don't call it if we don't need to
	PRBool flagSet;
	nsresult rv;

	if(!NS_SUCCEEDED(rv = GetFlag(flag, &flagSet)))
		return rv;

	if (!flagSet)
	{
		mFlags &= ~flag;
		OnFlagChange (flag);
	}

	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetFlag(PRUint32 flag, PRBool *_retval)
{
	*_retval = ((mFlags & flag) != 0);
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::ToggleFlag(PRUint32 flag)
{
  mFlags ^= flag;
	OnFlagChange (flag);

	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::OnFlagChange(PRUint32 flag)
{
	//Still need to implement
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetFlags(PRUint32 *_retval)
{
	*_retval = mFlags;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetFoldersWithFlag(PRUint32 flags, PRUint32 resultsize, PRUint32 *numFolders, nsIMsgFolder **result)
{
	PRUint32 num = 0;
	if ((flags & mFlags) == flags) {
		if (result && (num < resultsize)) {
			result[num] = this;
			NS_IF_ADDREF(result[num]);
		}
		num++;
	}

	nsresult rv;
	nsCOMPtr<nsIMsgFolder> folder;
	PRUint32 cnt;
  rv = mSubFolders->Count(&cnt);
  if (NS_SUCCEEDED(rv)) {
    for (PRUint32 i=0; i < cnt; i++) 
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
		folder = do_QueryInterface(supports, &rv);
      if(NS_SUCCEEDED(rv) && folder)
      {
        // CAREFUL! if NULL is passed in for result then the caller
        // still wants the full count!  Otherwise, the result should be at most the
        // number that the caller asked for.
        PRUint32 numSubFolders;

        if (!result)
        {
          folder->GetFoldersWithFlag(flags, 0, &numSubFolders, NULL);
          num += numSubFolders;
        }
        else if (num < resultsize)
        {
          folder->GetFoldersWithFlag(flags, resultsize - num, &numSubFolders, result+num);
          num += numSubFolders;
        }
        else
        {
          break;
        }
      }
    }
  }
	*numFolders = num;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetExpansionArray(nsISupportsArray *expansionArray)
{
  // the application of flags in GetExpansionArray is subtly different
  // than in GetFoldersWithFlag 

	nsresult rv;
	PRUint32 cnt;
  rv = mSubFolders->Count(&cnt);
  if (NS_FAILED(rv)) return rv;
  for (PRUint32 i = 0; i < cnt; i++)
	{
		nsCOMPtr<nsISupports> supports = getter_AddRefs(mSubFolders->ElementAt(i));
		nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(supports, &rv);
		if(NS_SUCCEEDED(rv))
		{
			PRUint32 cnt2;
      rv = expansionArray->Count(&cnt2);
      if (NS_SUCCEEDED(rv)) {
        expansionArray->InsertElementAt(folder, cnt2);
        PRUint32 flags;
        folder->GetFlags(&flags);
        if (!(flags & MSG_FOLDER_FLAG_ELIDED))
          folder->GetExpansionArray(expansionArray);
      }
    }
  }

	return NS_OK;
}

#ifdef HAVE_PANE
NS_IMETHODIMP nsMsgFolder::SetFlagInAllFolderPanes(PRUInt32 which)
{

}

#endif

#ifdef HAVE_NET
NS_IMETHODIMP nsMsgFolder::EscapeMessageId(const char *messageId, const char **escapeMessageID)
{

}
#endif

NS_IMETHODIMP nsMsgFolder::GetExpungedBytesCount(PRUint32 *count)
{
	if(!count)
		return NS_ERROR_NULL_POINTER;

	*count = 0;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetDeletable(PRBool *deletable)
{

	if(!deletable)
		return NS_ERROR_NULL_POINTER;

	*deletable = PR_FALSE;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetRequiresCleanup(PRBool *requiredCleanup)
{
	if(!requiredCleanup)
		return NS_ERROR_NULL_POINTER;

	*requiredCleanup = PR_FALSE;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::ClearRequiresCleanup()
{
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::ManyHeadersToDownload(PRBool *_retval)
{
	if (!_retval)
		return NS_ERROR_NULL_POINTER;
	*_retval = PR_FALSE;
	return NS_OK;
}


NS_IMETHODIMP nsMsgFolder::GetKnowsSearchNntpExtension(PRBool *knowsExtension)
{
	if(!knowsExtension)
		return NS_ERROR_NULL_POINTER;

	*knowsExtension = PR_FALSE;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetAllowsPosting(PRBool *allowsPosting)
{
	if(!allowsPosting)
		return NS_ERROR_NULL_POINTER;

	*allowsPosting = PR_TRUE;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetDisplayRecipients(PRBool *displayRecipients)
{
	nsresult rv;

	*displayRecipients = PR_FALSE;

	if (mFlags & MSG_FOLDER_FLAG_SENTMAIL && !(mFlags & MSG_FOLDER_FLAG_INBOX))
		*displayRecipients = PR_TRUE;
	else if (mFlags & MSG_FOLDER_FLAG_QUEUE)
		*displayRecipients = PR_TRUE;
	else 
	{
		// Only mail folders can be FCC folders
		if (mFlags & MSG_FOLDER_FLAG_MAIL || mFlags & MSG_FOLDER_FLAG_IMAPBOX) 
		{
			// There's one FCC folder for sent mail, and one for sent news
			nsIMsgFolder *fccFolders[2]; 
			int numFccFolders = 0;
#ifdef HAVE_MASTER
			m_master->GetFolderTree()->GetFoldersWithFlag (MSG_FOLDER_FLAG_SENTMAIL, fccFolders, 2, &numFccFolders);
#endif
			for (int i = 0; i < numFccFolders; i++)
			{
				PRBool isAncestor;
				if(NS_SUCCEEDED(rv = fccFolders[i]->IsAncestorOf(this, &isAncestor)))
				{
					if (isAncestor)
						*displayRecipients = PR_TRUE;
				}
				NS_RELEASE(fccFolders[i]);
			}
		}
	}
	return NS_OK;
}


NS_IMETHODIMP nsMsgFolder::AcquireSemaphore(nsISupports *semHolder)
{
	nsresult rv = NS_OK;

	if (mSemaphoreHolder == NULL)
	{
		mSemaphoreHolder = semHolder;	//Don't AddRef due to ownership issues.
	}
	else
		rv = NS_MSG_FOLDER_BUSY;

	return rv;

}

NS_IMETHODIMP nsMsgFolder::ReleaseSemaphore(nsISupports *semHolder)
{
	if (!mSemaphoreHolder || mSemaphoreHolder == semHolder)
	{
		mSemaphoreHolder = NULL;
	}
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::TestSemaphore(nsISupports *semHolder, PRBool *result)
{
	if(!result)
		return NS_ERROR_NULL_POINTER;

	*result = (mSemaphoreHolder == semHolder);

	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetLocked(PRBool *isLocked)
{ 
	*isLocked =  mSemaphoreHolder != NULL;
	return  NS_OK;
}

#ifdef HAVE_PANE
    MWContext *GetFolderPaneContext();
#endif

#ifdef HAVE_MASTER
    MSG_Master  *GetMaster() {return m_master;}
#endif

NS_IMETHODIMP nsMsgFolder::GetRelativePathName(char **pathName)
{
	if(!pathName)
		return NS_ERROR_NULL_POINTER;

	*pathName = nsnull;
	return NS_OK;
}


NS_IMETHODIMP nsMsgFolder::GetSizeOnDisk(PRUint32 *size)
{
	if(!size)
		return NS_ERROR_NULL_POINTER;

	*size = 0;
	return NS_OK;

}

#ifdef HAVE_NET
NS_IMETHODIMP nsMsgFolder::ShouldPerformOperationOffline(PRBool *performOffline)
{

}
#endif


#ifdef DOES_FOLDEROPERATIONS
NS_IMETHODIMP nsMsgFolder::DownloadToTempFileAndUpload(MessageCopyInfo *copyInfo,
                                                       nsMsgKeyArray &keysToSave,
                                                       MSG_FolderInfo *dstFolder,
                                                       nsMsgDatabase *sourceDB)
{

}

NS_IMETHODIMP nsMsgFolder::UpdateMoveCopyStatus(MWContext *context, PRBool isMove, int32 curMsgCount, int32 totMessages)
{

}

#endif

NS_IMETHODIMP nsMsgFolder::RememberPassword(const char *password)
{
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetRememberedPassword(char ** password)
{

	if(!password)
		return NS_ERROR_NULL_POINTER;

	*password = nsnull;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::UserNeedsToAuthenticateForFolder(PRBool displayOnly, PRBool *needsAuthenticate)
{
	if(!needsAuthenticate)
		return NS_ERROR_NULL_POINTER;

	*needsAuthenticate = PR_FALSE;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetUsername(char **userName)
{
  NS_ENSURE_ARG_POINTER(userName);
  nsresult rv;
  nsCOMPtr <nsIMsgIncomingServer> server;
  
  rv = GetServer(getter_AddRefs(server));
  if (NS_FAILED(rv)) return rv;

  if (server)
    return server->GetUsername(userName);

  return NS_ERROR_UNEXPECTED;
}

NS_IMETHODIMP nsMsgFolder::GetHostname(char **hostName)
{
  NS_ENSURE_ARG_POINTER(hostName);

  nsresult rv;

  nsCOMPtr<nsIMsgIncomingServer> server;
  rv = GetServer(getter_AddRefs(server));
  if (NS_FAILED(rv)) return rv;

  if (server)
    return server->GetHostName(hostName);
  
  return NS_ERROR_UNEXPECTED;
}

NS_IMETHODIMP nsMsgFolder::GetNewMessages(nsIMsgWindow *)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP nsMsgFolder::GetBiffState(PRUint32 *aBiffState)
{
	if(!aBiffState)
		return NS_ERROR_NULL_POINTER;
	*aBiffState = mBiffState;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::SetBiffState(PRUint32 aBiffState)
{
	if(mBiffState != aBiffState)
	{
		PRUint32 oldBiffState = mBiffState;
		mBiffState = aBiffState;
		nsCOMPtr<nsISupports> supports;
		if(NS_SUCCEEDED(QueryInterface(nsCOMTypeInfo<nsISupports>::GetIID(), getter_AddRefs(supports))))
			NotifyPropertyFlagChanged(supports, kBiffStateAtom, oldBiffState, mBiffState);
	}
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetNumNewMessages(PRInt32 *aNumNewMessages)
{
	if(!aNumNewMessages)
		return NS_ERROR_NULL_POINTER;

	*aNumNewMessages = mNumNewBiffMessages;
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::SetNumNewMessages(PRInt32 aNumNewMessages)
{
	if(aNumNewMessages != mNumNewBiffMessages)
	{
		PRInt32 oldNumMessages = mNumNewBiffMessages;
		mNumNewBiffMessages = aNumNewMessages;

    nsCAutoString oldNumMessagesStr;
    oldNumMessagesStr.Append(oldNumMessages);
		nsCAutoString newNumMessagesStr;
    newNumMessagesStr.Append(aNumNewMessages);
		NotifyPropertyChanged(kNumNewBiffMessagesAtom, oldNumMessagesStr, newNumMessagesStr);
	}

	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetNewMessagesNotificationDescription(PRUnichar * *aDescription)
{
	nsresult rv;
	nsAutoString description("");
	nsCOMPtr<nsIMsgIncomingServer> server;
	rv = GetServer(getter_AddRefs(server));
  
	if(NS_SUCCEEDED(rv))
	{
		if (!(mFlags & MSG_FOLDER_FLAG_INBOX))
		{
			nsXPIDLString folderName;
			rv = GetPrettyName(getter_Copies(folderName));
			if (NS_SUCCEEDED(rv) && folderName)
				description = folderName;
		}

    // append the server name
    nsXPIDLString serverName;
		rv = server->GetPrettyName(getter_Copies(serverName));
		if(NS_SUCCEEDED(rv)) {
      // put this test here because we don't want to just put "folder name on"
      // in case the above failed
      if (!(mFlags & MSG_FOLDER_FLAG_INBOX))
        description += " on ";
			description += serverName;
    }
	}
	*aDescription = description.ToNewUnicode();
	return NS_OK;
}

NS_IMETHODIMP nsMsgFolder::GetRootFolder(nsIMsgFolder * *aRootFolder)
{
	if (!aRootFolder) return NS_ERROR_NULL_POINTER;

	nsresult rv;
	nsCOMPtr<nsIMsgIncomingServer> server;
	rv = GetServer(getter_AddRefs(server));
	if (NS_FAILED(rv)) return rv;
	NS_ASSERTION(server, "server is null");
	// if this happens, bail.
	if (!server) return NS_ERROR_NULL_POINTER;

	nsCOMPtr<nsIFolder> aRoot;
	rv = server->GetRootFolder(getter_AddRefs(aRoot));

	if (NS_FAILED(rv)) return rv;
	if (!aRoot) return NS_ERROR_NULL_POINTER;

	return aRoot->QueryInterface(nsCOMTypeInfo<nsIMsgFolder>::GetIID(), (void**)aRootFolder);
}

NS_IMETHODIMP
nsMsgFolder::GetMsgDatabase(nsIMsgDatabase** aMsgDatabase)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsMsgFolder::GetPath(nsIFileSpec * *aPath)
{
  NS_ENSURE_ARG_POINTER(aPath);
  nsresult rv=NS_OK;

  if (!mPath)
    rv = parseURI(PR_TRUE);
  
  *aPath = mPath;
  NS_IF_ADDREF(*aPath);
  
  return rv;
}

NS_IMETHODIMP
nsMsgFolder::SetPath(nsIFileSpec  *aPath)
{
  // XXX - make a local copy!
  mPath = aPath;

  return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::MarkMessagesRead(nsISupportsArray *messages, PRBool markRead)
{
	PRUint32 count;
	nsresult rv;

	rv = messages->Count(&count);
	if(NS_FAILED(rv))
		return rv;

	for(PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsISupports> msgSupports = getter_AddRefs(messages->ElementAt(i));
		nsCOMPtr<nsIMessage> message = do_QueryInterface(msgSupports);

		if(message)
			rv = message->MarkRead(markRead);

		if(NS_FAILED(rv))
			return rv;

	}
	return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::MarkMessagesFlagged(nsISupportsArray *messages, PRBool markFlagged)
{
	PRUint32 count;
	nsresult rv;

	rv = messages->Count(&count);
	if(NS_FAILED(rv))
		return rv;

	for(PRUint32 i = 0; i < count; i++)
	{
		nsCOMPtr<nsISupports> msgSupports = getter_AddRefs(messages->ElementAt(i));
		nsCOMPtr<nsIMessage> message = do_QueryInterface(msgSupports);

		if(message)
			rv = message->MarkFlagged(markFlagged);

		if(NS_FAILED(rv))
			return rv;

	}
	return NS_OK;
}

NS_IMETHODIMP
nsMsgFolder::MarkAllMessagesRead(void)
{
	return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsMsgFolder::CopyMessages(nsIMsgFolder* srcFolder,
                          nsISupportsArray *messages,
                          PRBool isMove,
                          nsIMsgWindow *window,
                          nsIMsgCopyServiceListener* listener)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP
nsMsgFolder::CopyFileMessage(nsIFileSpec* fileSpec,
                             nsIMessage* messageToReplace,
                             PRBool isDraftOrTemplate,
                             nsIMsgWindow *window,
                             nsIMsgCopyServiceListener* listener)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP nsMsgFolder::MatchName(nsString *name, PRBool *matches)
{
	if (!matches)
		return NS_ERROR_NULL_POINTER;
	*matches = mName.EqualsIgnoreCase(*name);
	return NS_OK;
}

nsresult
nsMsgFolder::NotifyPropertyChanged(nsIAtom *property,
                                   char *oldValue, char* newValue)
{
	nsCOMPtr<nsISupports> supports;
	if(NS_SUCCEEDED(QueryInterface(nsCOMTypeInfo<nsISupports>::GetIID(), getter_AddRefs(supports))))
	{
		PRInt32 i;
		for(i = 0; i < mListeners->Count(); i++)
		{
			//Folderlistener's aren't refcounted.
			nsIFolderListener* listener =(nsIFolderListener*)mListeners->ElementAt(i);
			listener->OnItemPropertyChanged(supports, property, oldValue, newValue);
		}

		//Notify listeners who listen to every folder
		nsresult rv;
		NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
		if(NS_SUCCEEDED(rv))
			mailSession->NotifyFolderItemPropertyChanged(supports, property, oldValue, newValue);

	}

	return NS_OK;

}

nsresult
nsMsgFolder::NotifyUnicharPropertyChanged(nsIAtom *property,
                                          const PRUnichar* oldValue,
                                          const PRUnichar *newValue)
{
  nsresult rv;
  nsCOMPtr<nsISupports> supports;
  rv = QueryInterface(NS_GET_IID(nsISupports),
                      (void **)getter_AddRefs(supports));
  if (NS_FAILED(rv)) return rv;
  
  PRInt32 i;
  for (i=0; i<mListeners->Count(); i++) {
    // folderlisteners arent refcounted in the array
    nsIFolderListener* listener=(nsIFolderListener*)mListeners->ElementAt(i);
    listener->OnItemUnicharPropertyChanged(supports, property, oldValue, newValue);
  }

  // Notify listeners who listen to every folder
  NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv);
  if (NS_SUCCEEDED(rv))
    rv = mailSession->NotifyFolderItemUnicharPropertyChanged(supports,
                                                             property,
                                                             oldValue,
                                                             newValue);
  return NS_OK;
}

nsresult nsMsgFolder::NotifyIntPropertyChanged(nsIAtom *property, PRInt32 oldValue, PRInt32 newValue)
{
	nsCOMPtr<nsISupports> supports;
	if(NS_SUCCEEDED(QueryInterface(nsCOMTypeInfo<nsISupports>::GetIID(), getter_AddRefs(supports))))
	{
		PRInt32 i;
		for(i = 0; i < mListeners->Count(); i++)
		{
			//Folderlistener's aren't refcounted.
			nsIFolderListener* listener =(nsIFolderListener*)mListeners->ElementAt(i);
			listener->OnItemIntPropertyChanged(supports, property, oldValue, newValue);
		}

		//Notify listeners who listen to every folder
		nsresult rv;
		NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
		if(NS_SUCCEEDED(rv))
			mailSession->NotifyFolderItemIntPropertyChanged(supports, property, oldValue, newValue);

	}

	return NS_OK;

}

nsresult
nsMsgFolder::NotifyBoolPropertyChanged(nsIAtom* property,
                                       PRBool oldValue, PRBool newValue)
{
	nsCOMPtr<nsISupports> supports;
	if(NS_SUCCEEDED(QueryInterface(nsCOMTypeInfo<nsISupports>::GetIID(), getter_AddRefs(supports))))
	{
		PRInt32 i;
		for(i = 0; i < mListeners->Count(); i++)
		{
			//Folderlistener's aren't refcounted.
			nsIFolderListener* listener =(nsIFolderListener*)mListeners->ElementAt(i);
			listener->OnItemBoolPropertyChanged(supports, property, oldValue, newValue);
		}

		//Notify listeners who listen to every folder
		nsresult rv;
		NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
		if(NS_SUCCEEDED(rv))
			mailSession->NotifyFolderItemBoolPropertyChanged(supports, property, oldValue, newValue);

	}

	return NS_OK;

}

nsresult
nsMsgFolder::NotifyPropertyFlagChanged(nsISupports *item, nsIAtom *property,
                                       PRUint32 oldValue, PRUint32 newValue)
{
	PRInt32 i;
	for(i = 0; i < mListeners->Count(); i++)
	{
		//Folderlistener's aren't refcounted.
		nsIFolderListener *listener = (nsIFolderListener*)mListeners->ElementAt(i);
		listener->OnItemPropertyFlagChanged(item, property, oldValue, newValue);
	}

	//Notify listeners who listen to every folder
	nsresult rv;
	NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
	if(NS_SUCCEEDED(rv))
		mailSession->NotifyFolderItemPropertyFlagChanged(item, property, oldValue, newValue);

	return NS_OK;
}

nsresult nsMsgFolder::NotifyItemAdded(nsISupports *parentItem, nsISupports *item, const char* viewString)
{
	static PRBool notify = PR_TRUE;

	if (!notify)
		return NS_OK;

	PRInt32 i;
	for(i = 0; i < mListeners->Count(); i++)
	{
		//Folderlistener's aren't refcounted.
		nsIFolderListener *listener = (nsIFolderListener*)mListeners->ElementAt(i);
		listener->OnItemAdded(parentItem, item, viewString);
	}

	//Notify listeners who listen to every folder
	nsresult rv;
	NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
	if(NS_SUCCEEDED(rv))
		mailSession->NotifyFolderItemAdded(parentItem, item, viewString);

	return NS_OK;

}

nsresult nsMsgFolder::NotifyItemDeleted(nsISupports *parentItem, nsISupports *item, const char* viewString)
{

	PRInt32 i;
	for(i = 0; i < mListeners->Count(); i++)
	{
		//Folderlistener's aren't refcounted.
		nsIFolderListener *listener = (nsIFolderListener*)mListeners->ElementAt(i);
		listener->OnItemRemoved(parentItem, item, viewString);
	}
	//Notify listeners who listen to every folder
	nsresult rv;
  NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
	if(NS_SUCCEEDED(rv))
		mailSession->NotifyFolderItemDeleted(parentItem, item, viewString);

	return NS_OK;

}

nsresult nsMsgFolder::NotifyFolderLoaded()
{
	PRInt32 i;
	for(i = 0; i < mListeners->Count(); i++)
	{
		//Folderlistener's aren't refcounted.
		nsIFolderListener *listener = (nsIFolderListener*)mListeners->ElementAt(i);
		listener->OnFolderLoaded(this);
	}
	//Notify listeners who listen to every folder
	nsresult rv;
	NS_WITH_SERVICE(nsIMsgMailSession, mailSession, kMsgMailSessionCID, &rv); 
	if(NS_SUCCEEDED(rv))
		mailSession->NotifyFolderLoaded(this);

	return NS_OK;
}

nsresult
nsGetMailFolderSeparator(nsString& result)
{
  result = ".sbd";
  return NS_OK;
}

