gecko-dev/mailnews/base/util/nsMsgFolder.cpp

1575 строки
37 KiB
C++

/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "msgCore.h" // precompiled header...
#include "nsMsgFolder.h"
#include "nsMsgFolderFlags.h"
#include "prprf.h"
#include "nsMsgKeyArray.h"
#include "nsMsgDatabase.h"
#include "nsDBFolderInfo.h"
#include "nsISupportsArray.h"
#include "nsIPref.h"
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
// we need this because of an egcs 1.0 (and possibly gcc) compiler bug
// that doesn't allow you to call ::nsISupports::GetIID() inside of a class
// that multiply inherits from nsISupports
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
nsMsgFolder::nsMsgFolder(void)
: nsRDFResource(), mFlags(0),
mNumUnreadMessages(-1), mNumTotalMessages(0),
mCsid(0),
mDepth(0),
mPrefFlags(0),
mListeners(nsnull)
{
// NS_INIT_REFCNT(); done by superclass
#ifdef HAVE_MASTER
mMaster = NULL;
#endif
#ifdef HAVE_SEMAPHORE
mSemaphoreHolder = NULL;
#endif
#ifdef HAVE_DB
mLastMessageLoaded = nsMsgKey_None;
#endif
mNumPendingUnreadMessages = 0;
mNumPendingTotalMessages = 0;
NS_NewISupportsArray(&mSubFolders);
mIsCachable = TRUE;
}
nsMsgFolder::~nsMsgFolder(void)
{
if(mSubFolders)
{
PRUint32 count = mSubFolders->Count();
for (int i = count - 1; i >= 0; i--)
mSubFolders->RemoveElementAt(i);
NS_RELEASE(mSubFolders);
}
if (mListeners) {
for (PRInt32 i = mListeners->Count() - 1; i >= 0; --i) {
mListeners->RemoveElementAt(i);
}
NS_RELEASE(mListeners);
}
}
NS_IMPL_ISUPPORTS_INHERITED(nsMsgFolder, nsRDFResource, nsIMsgFolder)
////////////////////////////////////////////////////////////////////////////////
typedef PRBool
(*nsArrayFilter)(nsISupports* element, void* data);
static nsresult
nsFilterBy(nsISupportsArray* array, nsArrayFilter filter, void* data,
nsISupportsArray* *result)
{
nsISupportsArray* f;
nsresult rv = NS_NewISupportsArray(&f);
if (NS_FAILED(rv)) return rv;
NS_ADDREF(f);
for (PRUint32 i = 0; i < array->Count(); i++) {
nsISupports* element = (*array)[i];
if (filter(element, data)) {
nsresult rv = f->AppendElement(element);
if (NS_FAILED(rv)) {
NS_RELEASE(f);
return rv;
}
}
}
*result = f;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsMsgFolder::AddUnique(nsISupports* element)
{
// XXX fix this
return mSubFolders->AppendElement(element);
}
NS_IMETHODIMP
nsMsgFolder::ReplaceElement(nsISupports* element, nsISupports* newElement)
{
PR_ASSERT(0);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMsgFolder::GetSubFolders(nsIEnumerator* *result)
{
return mSubFolders->Enumerate(result);
}
NS_IMETHODIMP nsMsgFolder::AddFolderListener(nsIFolderListener * listener)
{
nsresult rv;
if (! mListeners)
{
rv = NS_NewISupportsArray(&mListeners);
if (!NS_SUCCEEDED(rv))
return rv;
}
mListeners->AppendElement(listener);
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::RemoveFolderListener(nsIFolderListener * listener)
{
if (! mListeners)
return NS_OK;
mListeners->RemoveElement(listener);
return NS_OK;
}
NS_IMETHODIMP
nsMsgFolder::GetMessages(nsIEnumerator* *result)
{
// XXX should this return an empty enumeration?
return NS_ERROR_FAILURE;
}
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP nsMsgFolder::BuildFolderURL(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
#ifdef HAVE_MASTER
NS_IMETHODIMP nsMsgFolder::SetMaster(MSG_Master *master)
{
mMaster = master;
return NS_OK;
}
#endif
#ifdef DOES_FOLDEROPERATIONS
NS_IMETHODIMP
nsMsgFolder::StartAsyncCopyMessagesInto(MSG_FolderInfo *dstFolder,
MSG_Pane* sourcePane,
nsMsgDatabase *sourceDB,
nsMsgKeyArray *srcArray,
int32 srcCount,
MWContext *currentContext,
MSG_UrlQueue *urlQueue,
PRBool deleteAfterCopy,
nsMsgKey nextKey = nsMsgKey_None)
{
// General note: If either the source or destination folder is an IMAP folder then we add the copy info struct
// to the end of the current context's chain of copy info structs then fire off an IMAP URL.
// However, local folders don't work this way! We must add the copy info struct to the URL queue where it will be fired
// at its leisure.
MessageCopyInfo *copyInfo = (MessageCopyInfo *) XP_ALLOC(sizeof(MessageCopyInfo));
if (copyInfo)
{
XP_BZERO (copyInfo, sizeof(MessageCopyInfo));
copyInfo->srcFolder = this;
copyInfo->dstFolder = dstFolder;
copyInfo->nextCopyInfo = NULL;
copyInfo->dstIMAPfolderUpdated=FALSE;
copyInfo->offlineFolderPositionOfMostRecentMessage = 0;
copyInfo->srcDB = sourceDB;
copyInfo->srcArray = srcArray;
copyInfo->srcCount = srcCount;
copyInfo->moveState.ismove = deleteAfterCopy;
copyInfo->moveState.sourcePane = sourcePane;
copyInfo->moveState.nextKeyToLoad = nextKeyToLoad;
copyInfo->moveState.urlForNextKeyLoad = NULL;
copyInfo->moveState.moveCompleted = FALSE;
copyInfo->moveState.finalDownLoadMessageSize = 0;
copyInfo->moveState.imap_connection = 0;
copyInfo->moveState.haveUploadedMessageSize = FALSE;
nsresult openErr = NS_OK;
PRBool wasCreated;
if (dstFolder->GetType() == FOLDER_MAIL)
openErr = MailDB::Open (dstFolder->GetMailFolderInfo()->GetPathname(), FALSE, &copyInfo->moveState.destDB, FALSE);
else if (dstFolder->GetType() == FOLDER_IMAPMAIL && !IsNews())
openErr = ImapMailDB::Open (dstFolder->GetMailFolderInfo()->GetPathname(), FALSE, &copyInfo->moveState.destDB,
sourcePane->GetMaster(), &wasCreated);
if (!dstFolder->GetMailFolderInfo() || (openErr != NS_OK))
copyInfo->moveState.destDB = NULL;
// let the front end know that we are starting a long update
sourcePane->StartingUpdate(MSG_NotifyNone, 0, 0);
if ((this->GetType() == FOLDER_IMAPMAIL) || (dstFolder->GetType() == FOLDER_IMAPMAIL))
{
// add this copyinfo struct to the end
if (currentContext->msgCopyInfo != NULL)
{
MessageCopyInfo *endingNode = currentContext->msgCopyInfo;
while (endingNode->nextCopyInfo != NULL)
endingNode = endingNode->nextCopyInfo;
endingNode->nextCopyInfo = copyInfo;
}
else
currentContext->msgCopyInfo = copyInfo;
// BeginCopyMessages will fire an IMAP url. The IMAP
// module will call FinishCopyMessages so that the whole
// shebang is handled as one IMAP url. Previously the copy
// happened with a mailbox url and IMAP url running together
// in the same context. This worked on mac only.
nsresult copyErr = BeginCopyingMessages(dstFolder, sourceDB, srcArray,urlQueue,srcCount,copyInfo);
if (NS_OK != copyErr)
{
CleanupCopyMessagesInto(&currentContext->msgCopyInfo);
if (/* !NET_IsOffline() && */((int32) copyErr < -1) )
#if 0
FE_Alert (sourcePane->GetContext(), XP_GetString(copyErr));
#else
printf("%s\n", XP_GetString(copyErr));
#endif
}
}
else
{
// okay, add this URL to our URL queue.
URL_Struct *url_struct = NET_CreateURLStruct("mailbox:copymessages", NET_DONT_RELOAD);
if (url_struct)
{
MSG_UrlQueue::AddLocalMsgCopyUrlToPane(copyInfo, url_struct, PostMessageCopyUrlExitFunc, sourcePane, FALSE);
}
}
}
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::BeginCopyingMessages(MSG_FolderInfo *dstFolder,
nsMsgDatabase *sourceDB,
nsMsgKeyArray *srcArray,
MSG_UrlQueue *urlQueue,
int32 srcCount,
MessageCopyInfo *copyInfo)
{
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::FinishCopyingMessages(MWContext *context,
MSG_FolderInfo * srcFolder,
MSG_FolderInfo *dstFolder,
nsMsgDatabase *sourceDB,
nsMsgKeyArray **ppSrcArray,
int32 srcCount,
msg_move_state *state)
{
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::CleanupCopyMessagesInto (MessageCopyInfo **info)
{
if (!info || !*info)
return;
MSG_Pane *sourcePane = (*info)->moveState.sourcePane;
PRBool searchPane = sourcePane ? sourcePane->GetPaneType() == MSG_SEARCHPANE : FALSE;
if ((*info)->moveState.destDB != NULL)
{
(*info)->moveState.destDB->SafeClose((*info)->moveState.destDB);
(*info)->moveState.destDB = NULL;
}
if ((*info)->dstFolder->TestSemaphore(this))
(*info)->dstFolder->ReleaseSemaphore(this);
// if we were a search pane, and an error occurred, close the view on this action..
if (sourcePane != NULL && searchPane)
((MSG_SearchPane *) sourcePane)->CloseView((*info)->srcFolder);
// tell the fe that we are finished with
// out backend driven update. They can
// now do things like load the next message.
// now that an imap copy message is at most 2 urls, we can end the
// the update here. Now this is this only ending update and resource
// cleanup for message copying
sourcePane->EndingUpdate(MSG_NotifyNone, 0, 0);
// tell the FE that we're done copying so they can re-enable
// selection if they've decided to disable it during the copy
// I don't think we want to do this if we are a search pane but i haven't been able to
// justify why yet!!
if (!searchPane)
FE_PaneChanged(sourcePane, TRUE, MSG_PaneNotifyCopyFinished, 0);
// EndingUpdate may have caused an interruption of this context and cleaning up the
// url queue may have deleted this MessageCopyInfo already
if (*info)
{
MessageCopyInfo *deleteMe = *info;
*info = deleteMe->nextCopyInfo; // but nextCopyInfo == NULL. this causes the fault later on
XP_FREE(deleteMe);
}
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::SaveMessages(nsMsgKeyArray *, const char *fileName,
MSG_Pane *pane, nsMsgDatabase *msgDB,
int (*doneCB)(void *, int status) = NULL, void *state = NULL,
PRBool addMozillaStatus = TRUE)
{
DownloadArticlesToFolder::SaveMessages(array, fileName, pane, this, msgDB, doneCB, state, addMozillaStatus);
return NS_OK;
}
#endif
NS_IMETHODIMP nsMsgFolder::GetPrettyName(char ** name)
{
char *cmName = mName.ToNewCString();
if (name) *name = PL_strdup(cmName);
delete[] cmName;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::SetPrettyName(char *name)
{
mName = name;
return NS_OK;
}
NS_IMETHODIMP_(PRUint32) nsMsgFolder::Count(void) const
{
return mSubFolders->Count();
}
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::GetName(char **name)
{
char *cmName = mName.ToNewCString();
if (name) *name = PL_strdup(cmName);
delete[] cmName;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::SetName(char * name)
{
mName = name;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetChildNamed(const char* name, nsISupports* *result)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsMsgFolder::GetParent(nsIFolder* *parent)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsMsgFolder::GetPrettiestName(char **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;
nsISupportsArray* vFolders;
rv = nsFilterBy(mSubFolders, nsCanBeInFolderPane, nsnull, &vFolders);
if (NS_FAILED(rv)) return rv;
rv = vFolders->Enumerate(result);
NS_RELEASE(vFolders);
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::PropagateDelete(nsIMsgFolder **folder, PRBool deleteStorage)
{
nsresult status = NS_OK;
nsIMsgFolder *child = nsnull;
// first, find the folder we're looking to delete
for (PRUint32 i = 0; i < mSubFolders->Count() && *folder; i++)
{
nsISupports *supports = mSubFolders->ElementAt(i);
if(supports)
{
if(NS_SUCCEEDED(status = supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&child)))
{
if (*folder == child)
{
// maybe delete disk storage for it, and its subfolders
status = child->RecursiveDelete(deleteStorage);
if (status == NS_OK)
{
#ifdef HAVE_MASTER
PR_ASSERT(mMaster);
// Send out a broadcast message that this folder is going away.
// Many important things happen on this broadcast.
mMaster->BroadcastFolderDeleted (child);
#endif
mSubFolders->RemoveElement(child);
NS_RELEASE(*folder); // stop looking since will set *folder to nsnull
}
}
else
{
PRUint32 folderDepth, childDepth;
if(NS_SUCCEEDED((*folder)->GetDepth(&folderDepth)) &&
NS_SUCCEEDED(child->GetDepth(&childDepth)) &&
folderDepth > childDepth)
{
status = child->PropagateDelete (folder, deleteStorage);
}
}
NS_RELEASE(child);
}
NS_RELEASE(supports);
}
}
return status;
}
NS_IMETHODIMP nsMsgFolder::RecursiveDelete(PRBool deleteStorage)
{
// If deleteStorage is 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;
while (mSubFolders->Count() > 0)
{
nsISupports *supports = mSubFolders->ElementAt(0);
nsIMsgFolder *child = nsnull;
if(NS_SUCCEEDED(status = supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&child)))
{
status = child->RecursiveDelete(deleteStorage); // recur
#ifdef HAVE_MASTER
// Send out a broadcast message that this folder is going away.
// Many important things happen on this broadcast.
mMaster->BroadcastFolderDeleted (child);
#endif
mSubFolders->RemoveElement(child); // unlink it from this's child list
NS_RELEASE(child);
}
NS_RELEASE(supports); // free memory
}
// now delete the disk storage for _this_
if (deleteStorage && (status == NS_OK))
status = Delete();
return status;
}
NS_IMETHODIMP nsMsgFolder::CreateSubfolder(const char *, nsIMsgFolder**, PRUint32*)
{
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::Rename(const char *name)
{
nsresult status = NS_OK;
status = SetName((char*)name);
//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(const nsIMsgFolder *srcFolder, PRUint32* outPos)
{
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::ContainsChildNamed(const char *name, PRBool* containsChild)
{
nsIMsgFolder *child;
if(containsChild)
{
*containsChild = PR_FALSE;
if(NS_SUCCEEDED(GetChildNamed(name, (nsISupports**)&child)))
{
*containsChild = child != nsnull;
if(child)
NS_RELEASE(child);
}
return NS_OK;
}
else
return NS_ERROR_NULL_POINTER;
}
NS_IMETHODIMP nsMsgFolder::FindParentOf(nsIMsgFolder * aFolder, nsIMsgFolder ** aParent)
{
if(!aParent)
return NS_ERROR_NULL_POINTER;
*aParent = nsnull;
PRUint32 count = mSubFolders->Count();
for (PRUint32 i = 0; i < count && *aParent == NULL; i++)
{
nsISupports *supports = mSubFolders->ElementAt(i);
nsIMsgFolder *child = nsnull;
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&child)))
{
if (aFolder == child)
{
*aParent = this;
NS_ADDREF(*aParent);
}
NS_RELEASE(child);
}
NS_RELEASE(supports);
}
for (PRUint32 j = 0; j < count && *aParent == NULL; j++)
{
nsISupports *supports = mSubFolders->ElementAt(j);
nsIMsgFolder *child = nsnull;
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&child)))
{
child->FindParentOf(aFolder, aParent);
NS_RELEASE(child);
}
NS_RELEASE(supports);
}
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::IsParentOf(nsIMsgFolder *child, PRBool deep, PRBool *isParent)
{
if(!isParent)
return NS_ERROR_NULL_POINTER;
PRUint32 count = mSubFolders->Count();
for (PRUint32 i = 0; i < count; i++)
{
nsISupports *supports = mSubFolders->ElementAt(i);
nsIMsgFolder *folder;
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&folder)))
{
if (folder == child )
*isParent = PR_TRUE;
else if(deep)
{
folder->IsParentOf(child, deep, isParent);
}
}
NS_RELEASE(supports);
if(*isParent)
return NS_OK;
}
*isParent = PR_FALSE;
return NS_OK;
}
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);
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::GetDepth(PRUint32 *depth)
{
if(!depth)
return NS_ERROR_NULL_POINTER;
*depth = mDepth;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::SetDepth(PRUint32 depth)
{
mDepth = depth;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::UpdateSummaryTotals()
{
//We don't support this
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::SummaryChanged()
{
UpdateSummaryTotals();
#ifdef HAVE_MASTER
if (mMaster)
mMaster->BroadcastFolderChanged(this);
#endif
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetNumUnread(PRBool deep, PRUint32 *numUnread)
{
if(!numUnread)
return NS_ERROR_NULL_POINTER;
PRUint32 total = mNumUnreadMessages;
if (deep)
{
nsIMsgFolder *folder;
PRUint32 count = mSubFolders->Count();
for (PRUint32 i = 0; i < count; i++)
{
nsISupports *supports = mSubFolders->ElementAt(i);
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&folder)))
{
if (folder)
{
PRUint32 num;
folder->GetNumUnread(deep, &num);
if (num >= 0) // it's legal for counts to be negative if we don't know
total += num;
}
NS_RELEASE(folder);
}
NS_RELEASE(supports);
}
}
*numUnread = total;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetTotalMessages(PRBool deep, PRUint32 *totalMessages)
{
if(!totalMessages)
return NS_ERROR_NULL_POINTER;
PRUint32 total = mNumTotalMessages;
if (deep)
{
nsIMsgFolder *folder;
PRUint32 count = mSubFolders->Count();
for (PRUint32 i = 0; i < count; i++)
{
nsISupports *supports = mSubFolders->ElementAt(i);
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&folder)))
{
if (folder)
{
PRUint32 num;
folder->GetTotalMessages (deep, &num);
if (num >= 0) // it's legal for counts to be negative if we don't know
total += num;
NS_RELEASE(folder);
}
}
NS_RELEASE(supports);
}
}
*totalMessages = total;
return NS_OK;
}
#ifdef HAVE_DB
NS_IMETHOD GetTotalMessagesInDB(PRUint32 *totalMessages) const; // How many messages in database.
#endif
#ifdef HAVE_PANE
virtual void MarkAllRead(MSG_Pane *pane, PRBool deep);
#endif
#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
int32 GetNumPendingUnread(PRBool deep = FALSE) const;
int32 GetNumPendingTotalMessages(PRBool deep = FALSE) const;
void ChangeNumPendingUnread(int32 delta);
void ChangeNumPendingTotalMessages(int32 delta);
NS_IMETHODIMP nsMsgFolder::SetFolderPrefFlags(PRUint32 flags)
{
}
NS_IMETHODIMP nsMsgFolder::GetFolderPrefFlags(PRUint32 *flags)
{
}
NS_IMETHODIMP nsMsgFolder::SetFolderCSID(PRInt16 csid)
{
}
NS_IMETHODIMP nsMsgFolder::GetFolderCSID(PRInt16 *csid)
{
}
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, nsIMsgFolder **result,
PRUint32 resultsize, PRUint32 *numFolders)
{
PRUint32 num = 0;
if ((flags & mFlags) == flags) {
if (result && (num < resultsize)) {
result[num] = this;
}
num++;
}
nsIMsgFolder *folder = nsnull;
for (PRUint32 i=0; i < (PRUint32)mSubFolders->Count(); i++) {
nsISupports *supports = mSubFolders->ElementAt(i);
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&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, NULL, 0, &numSubFolders);
num += numSubFolders;
}
else if (num < resultsize)
{
folder->GetFoldersWithFlag(flags, result + num, resultsize - num, &numSubFolders);
num += numSubFolders;
}
else
{
NS_RELEASE(folder);
NS_RELEASE(supports);
break;
}
NS_RELEASE(folder);
}
NS_RELEASE(supports);
}
*numFolders = num;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetExpansionArray(nsISupportsArray *expansionArray)
{
// the application of flags in GetExpansionArray is subtly different
// than in GetFoldersWithFlag
for (PRUint32 i = 0; i < mSubFolders->Count(); i++)
{
nsISupports *supports = mSubFolders->ElementAt(i);
nsIMsgFolder *folder = nsnull;
if(NS_SUCCEEDED(supports->QueryInterface(nsIMsgFolder::GetIID(), (void**)&folder)))
{
((nsISupportsArray*)expansionArray)->InsertElementAt(folder, expansionArray->Count());
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::GetCanCreateChildren(PRBool *canCreateChildren)
{
if(!canCreateChildren)
return NS_ERROR_NULL_POINTER;
*canCreateChildren = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetCanBeRenamed(PRBool *canBeRenamed)
{
if(!canBeRenamed)
return NS_ERROR_NULL_POINTER;
*canBeRenamed = 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;
}
#ifdef HAVE_PANE
NS_IMETHODIMP nsMsgFolder::CanBeInFolderPane(PRBool *canBeInFolderPane)
{
}
#endif
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::DisplayRecipients(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 isParent;
if(NS_SUCCEEDED(rv = fccFolders[i]->IsParentOf(this, PR_TRUE, &isParent)))
{
if (isParent)
*displayRecipients = PR_TRUE;
}
NS_RELEASE(fccFolders[i]);
}
}
}
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::ReadDBFolderInfo(PRBool force)
{
// Since it turns out to be pretty expensive to open and close
// the DBs all the time, if we have to open it once, get everything
// we might need while we're here
nsresult result= NS_OK;
if (force || !(mPrefFlags & MSG_FOLDER_PREF_CACHED))
{
nsDBFolderInfo *folderInfo;
nsMsgDatabase *db;
if((result = NS_SUCCEEDED(GetDBFolderInfoAndDB(&folderInfo, &db))))
{
mIsCachable = TRUE;
if (folderInfo)
{
folderInfo->GetFlags(&mPrefFlags);
mPrefFlags |= MSG_FOLDER_PREF_CACHED;
folderInfo->SetFlags(mPrefFlags);
folderInfo->GetNumMessages(&mNumTotalMessages);
folderInfo->GetNumNewMessages(&mNumUnreadMessages);
mNumPendingTotalMessages = folderInfo->GetImapTotalPendingMessages();
mNumPendingUnreadMessages = folderInfo->GetImapUnreadPendingMessages();
mCsid = folderInfo->GetCSID();
if (db && !db->HasNew() && mNumPendingUnreadMessages <= 0)
ClearFlag(MSG_FOLDER_FLAG_GOT_NEW);
}
if (db)
db->Close(FALSE);
}
}
return result;
}
#ifdef HAVE_SEMAPHORE
NS_IMETHODIMP nsMsgFolder::AcquireSemaphore(void *semHolder)
{
}
NS_IMETHODIMP nsMsgFolder::ReleaseSemaphore(void *semHolder)
{
}
NS_IMETHODIMP nsMsgFolder::TestSemaphore(void *semHolder, PRBool *result)
{
}
NS_IMETHODIMP nsMsgFolder::IsLocked(PRBool *isLocked)
{
*isLocked = mSemaphoreHolder != NULL;
}
#endif
#ifdef HAVE_PANE
MWContext *GetFolderPaneContext();
#endif
#ifdef HAVE_MASTER
MSG_Master *GetMaster() {return m_master;}
#endif
#ifdef HAVE_CACHE
NS_IMETHODIMP nsMsgFolder::WriteToCache(XP_File)
{
}
NS_IMETHODIMP nsMsgFolder::ReadFromCache(char *)
{
}
NS_IMETHODIMP nsMsgFolder::IsCachable(PRBool *isCachable)
{
}
NS_IMETHODIMP nsMsgFolder::SkipCacheTokens(char **ppBuf, int numTokens)
{
}
#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::GetUsersName(char **userName)
{
if(!userName)
return NS_ERROR_NULL_POINTER;
*userName = "";
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetHostName(char **hostName)
{
if(!hostName)
return NS_ERROR_NULL_POINTER;
*hostName = "";
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
// URI Utilities for RDF
#include "prprf.h"
#include "prsystem.h"
static const char kMsgRootFolderPref[] = "mail.rootFolder";
char* gMailboxRoot = nsnull;
nsresult
nsGetMailboxRoot(nsFileSpec &result)
{
nsresult rv = NS_OK;
if (gMailboxRoot == nsnull) {
// get mailbox root preference and cache it permanently - this
// is extremely temporary...I'm waiting for hubie to check in the
// new preferences service stuff.
#if 1
nsIPref* prefs;
rv = nsServiceManager::GetService(kPrefCID, nsIPref::GetIID(), (nsISupports**)&prefs);
if (NS_FAILED(rv)) return rv;
if (prefs && NS_SUCCEEDED(rv)) {
rv = prefs->Startup("prefs.js");
if (NS_SUCCEEDED(rv)) {
rv = prefs->CopyPathPref(kMsgRootFolderPref, &gMailboxRoot);
}
}
(void)nsServiceManager::ReleaseService(kPrefCID, prefs);
if (NS_FAILED(rv)) return rv;
#else
gMailboxRoot = nsCRT::strdup("d:\\program files\\netscape\\users\\warren\\mail");
#endif
}
result = gMailboxRoot;
// XXX free gMailboxRoot somewhere (on shutdown?)
return rv;
}
nsresult
nsGetMailFolderSeparator(nsString& result)
{
static char* gMailFolderSep = nsnull; // never freed
if (gMailFolderSep == nsnull) {
gMailFolderSep = PR_smprintf(".sbd");
if (gMailFolderSep == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
}
result = gMailFolderSep;
return NS_OK;
}
nsresult
nsURI2Path(const char* rootURI, char* uriStr, nsFileSpec& pathResult)
{
nsresult rv;
nsAutoString sep;
sep += PR_GetDirectorySeparator();
nsAutoString sbdSep;
rv = nsGetMailFolderSeparator(sbdSep);
if (NS_FAILED(rv)) return rv;
nsAutoString uri = uriStr;
if (uri.Find(rootURI) != 0) // if doesn't start with rootURI
return NS_ERROR_FAILURE;
nsFileSpec root;
rv = nsGetMailboxRoot(root);
if (NS_FAILED(rv)) return rv;
nsAutoString path(root);
uri.Cut(0, nsCRT::strlen(rootURI));
PRInt32 uriLen = uri.Length();
PRInt32 pos;
while(uriLen > 0) {
nsAutoString folderName;
PRInt32 leadingPos;
// if it's the first character then remove it.
while ((leadingPos = uri.Find('/')) == 0) {
uri.Cut(0, 1);
uriLen--;
}
if (uriLen == 0)
break;
pos = uri.Find('/');
if (pos < 0)
pos = uriLen;
PRInt32 cnt = uri.Left(folderName, pos);
NS_ASSERTION(cnt == pos, "something wrong with nsString");
path += sep;
// the first time around the separator is special because
// the root mail folder doesn't end with .sbd
sep = sbdSep;
path += folderName;
uri.Cut(0, pos);
uriLen -= pos;
}
pathResult = path;
return NS_OK;
}
nsresult
nsPath2URI(const char* rootURI, nsFileSpec& spec, char* *uri)
{
nsresult rv;
nsAutoString sep;
rv = nsGetMailFolderSeparator(sep);
if (NS_FAILED(rv)) return rv;
PRUint32 sepLen = sep.Length();
nsFileSpec root;
rv = nsGetMailboxRoot(root);
if (NS_FAILED(rv)) return rv;
const char *path = spec;
nsAutoString pathStr(path);
path = root;
nsAutoString rootStr(path);
PRInt32 pos = pathStr.Find(rootStr);
if (pos != 0) // if doesn't start with root path
return NS_ERROR_FAILURE;
nsAutoString uriStr(rootURI);
PRUint32 rootStrLen = rootStr.Length();
pathStr.Cut(0, rootStrLen);
PRInt32 pathStrLen = pathStr.Length();
char dirSep = PR_GetDirectorySeparator();
while (pathStrLen > 0) {
nsAutoString folderName;
PRInt32 leadingPos;
// if it's the first character then remove it.
while ((leadingPos = pathStr.Find(dirSep)) == 0) {
pathStr.Cut(0, 1);
pathStrLen--;
}
if (pathStrLen == 0)
break;
pos = pathStr.Find(sep);
if (pos < 0)
pos = pathStrLen;
PRInt32 cnt = pathStr.Left(folderName, pos);
NS_ASSERTION(cnt == pos, "something wrong with nsString");
pathStr.Cut(0, pos + sepLen);
pathStrLen -= pos + sepLen;
uriStr += '/';
uriStr += folderName;
}
*uri = uriStr.ToNewCString();
return NS_OK;
}
nsresult
nsURI2Name(const char* rootURI, char* uriStr, nsString& name)
{
nsAutoString uri = uriStr;
if (uri.Find(rootURI) != 0) // if doesn't start with rootURI
return NS_ERROR_FAILURE;
PRInt32 pos = uri.RFind("/");
PRInt32 length = uri.Length();
PRInt32 count = length - (pos + 1);
return uri.Right(name, count);
}
nsresult nsParseLocalMessageURI(const char* uri, nsString& folderURI, PRUint32 *key)
{
if(!key)
return NS_ERROR_NULL_POINTER;
nsAutoString uriStr = uri;
PRInt32 keySeparator = uriStr.Find('#');
if(keySeparator != -1)
{
nsAutoString folderPath;
uriStr.Left(folderPath, keySeparator);
PRInt32 msfExtPos = folderPath.Find(".msf");
if(msfExtPos != -1)
folderPath.Left(folderURI, msfExtPos);
else
folderURI = folderPath;
nsAutoString keyStr;
uriStr.Right(keyStr, uriStr.Length() - (keySeparator + 1));
PRInt32 errorCode;
*key = keyStr.ToInteger(&errorCode);
return errorCode;
}
return NS_ERROR_FAILURE;
}
////////////////////////////////////////////////////////////////////////////////