Mozilla/mozilla/mailnews/base/src/nsMsgFolderCache.cpp
cathleen%netscape.com b8cedf8e73 eliminate nsCRT::strlen for char* strings (part 3), bug 124536 r=dp sr=brendan
git-svn-id: svn://10.0.0.236/trunk@114908 18797224-902f-48f8-a5cc-f745e15eee43
2002-02-19 08:42:48 +00:00

489 lines
13 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "msgCore.h"
#include "nsIMsgAccountManager.h"
#include "nsMsgFolderCacheElement.h"
#include "nsMsgFolderCache.h"
#include "nsMorkCID.h"
#include "nsIMdbFactoryFactory.h"
#include "nsFileStream.h"
#include "nsIFileSpec.h"
#include "nsXPIDLString.h"
#include "nsMsgBaseCID.h"
static NS_DEFINE_CID(kMorkCID, NS_MORK_CID);
static NS_DEFINE_CID(kCMsgMailSessionCID, NS_MSGMAILSESSION_CID);
const char *kFoldersScope = "ns:msg:db:row:scope:folders:all"; // scope for all folders table
const char *kFoldersTableKind = "ns:msg:db:table:kind:folders";
nsMsgFolderCache::nsMsgFolderCache()
{
m_mdbEnv = nsnull;
m_mdbStore = nsnull;
NS_INIT_REFCNT();
m_mdbAllFoldersTable = nsnull;
}
// should this, could this be an nsCOMPtr ?
static nsIMdbFactory *gMDBFactory = nsnull;
nsMsgFolderCache::~nsMsgFolderCache()
{
delete m_cacheElements;
if (m_mdbAllFoldersTable)
m_mdbAllFoldersTable->Release();
if (m_mdbStore)
m_mdbStore->Release();
if (gMDBFactory)
{
NS_RELEASE(gMDBFactory);
}
gMDBFactory = nsnull;
if (m_mdbEnv)
{
m_mdbEnv->Release();
}
}
NS_IMPL_ADDREF(nsMsgFolderCache)
NS_IMPL_RELEASE(nsMsgFolderCache)
nsresult
nsMsgFolderCache::QueryInterface(const nsIID& iid, void **result)
{
nsresult rv = NS_NOINTERFACE;
if (! result)
return NS_ERROR_NULL_POINTER;
void *res = nsnull;
if (iid.Equals(NS_GET_IID(nsIMsgFolderCache)) ||
iid.Equals(NS_GET_IID(nsISupports)))
res = NS_STATIC_CAST(nsIMsgFolderCache*, this);
if (res)
{
NS_ADDREF(this);
*result = res;
rv = NS_OK;
}
return rv;
}
/* static */ nsIMdbFactory *nsMsgFolderCache::GetMDBFactory()
{
if (!gMDBFactory)
{
nsresult rv;
nsCOMPtr <nsIMdbFactoryFactory> factoryfactory;
rv = nsComponentManager::CreateInstance(kMorkCID,
nsnull,
NS_GET_IID(nsIMdbFactoryFactory),
(void **) getter_AddRefs(factoryfactory));
if (NS_SUCCEEDED(rv) && factoryfactory)
rv = factoryfactory->GetMdbFactory(&gMDBFactory);
}
return gMDBFactory;
}
// initialize the various tokens and tables in our db's env
nsresult nsMsgFolderCache::InitMDBInfo()
{
nsresult err = NS_OK;
if (GetStore())
{
err = GetStore()->StringToToken(GetEnv(), kFoldersScope, &m_folderRowScopeToken);
if (err == NS_OK)
{
err = GetStore()->StringToToken(GetEnv(), kFoldersTableKind, &m_folderTableKindToken);
if (err == NS_OK)
{
// The table of all message hdrs will have table id 1.
m_allFoldersTableOID.mOid_Scope = m_folderRowScopeToken;
m_allFoldersTableOID.mOid_Id = 1;
}
}
}
return err;
}
// set up empty tables, dbFolderInfo, etc.
nsresult nsMsgFolderCache::InitNewDB()
{
nsresult err = NS_OK;
err = InitMDBInfo();
if (err == NS_OK)
{
nsIMdbStore *store = GetStore();
// create the unique table for the dbFolderInfo.
mdb_err mdberr;
mdberr = (nsresult) store->NewTable(GetEnv(), m_folderRowScopeToken,
m_folderTableKindToken, PR_FALSE, nsnull, &m_mdbAllFoldersTable);
}
return err;
}
nsresult nsMsgFolderCache::InitExistingDB()
{
nsresult err = NS_OK;
err = InitMDBInfo();
if (err == NS_OK)
{
err = GetStore()->GetTable(GetEnv(), &m_allFoldersTableOID, &m_mdbAllFoldersTable);
if (NS_SUCCEEDED(err) && m_mdbAllFoldersTable)
{
nsIMdbTableRowCursor* rowCursor = nsnull;
err = m_mdbAllFoldersTable->GetTableRowCursor(GetEnv(), -1, &rowCursor);
if (NS_SUCCEEDED(err) && rowCursor)
{
// iterate over the table rows and create nsMsgFolderCacheElements for each.
while (PR_TRUE)
{
nsresult rv;
nsIMdbRow* hdrRow;
mdb_pos rowPos;
rv = rowCursor->NextRow(GetEnv(), &hdrRow, &rowPos);
if (NS_FAILED(rv) || !hdrRow)
break;
rv = AddCacheElement(nsnull, hdrRow, nsnull);
hdrRow->Release();
if (NS_FAILED(rv))
return rv;
}
rowCursor->Release();
}
}
else
err = NS_ERROR_FAILURE;
}
return err;
}
nsresult nsMsgFolderCache::OpenMDB(const char *dbName, PRBool exists)
{
nsresult ret=NS_OK;
nsIMdbFactory *myMDBFactory = GetMDBFactory();
if (myMDBFactory)
{
ret = myMDBFactory->MakeEnv(nsnull, &m_mdbEnv);
if (NS_SUCCEEDED(ret))
{
nsIMdbThumb *thumb = nsnull;
char *nativeFileName = nsCRT::strdup(dbName);
nsIMdbHeap* dbHeap = 0;
mdb_bool dbFrozen = mdbBool_kFalse; // not readonly, we want modifiable
if (!nativeFileName)
return NS_ERROR_OUT_OF_MEMORY;
if (m_mdbEnv)
m_mdbEnv->SetAutoClear(PR_TRUE);
#if defined(XP_PC) || defined(XP_MAC)
// UnixToNative(nativeFileName);
#endif
if (exists)
{
mdbOpenPolicy inOpenPolicy;
mdb_bool canOpen;
mdbYarn outFormatVersion;
nsIMdbFile* oldFile = 0;
ret = myMDBFactory->OpenOldFile(m_mdbEnv, dbHeap, nativeFileName,
dbFrozen, &oldFile);
if ( oldFile )
{
if ( ret == NS_OK )
{
ret = myMDBFactory->CanOpenFilePort(m_mdbEnv, oldFile, // file to investigate
&canOpen, &outFormatVersion);
if (ret == 0 && canOpen)
{
inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
inOpenPolicy.mOpenPolicy_MinMemory = 0;
inOpenPolicy.mOpenPolicy_MaxLazy = 0;
ret = myMDBFactory->OpenFileStore(m_mdbEnv, NULL, oldFile, &inOpenPolicy,
&thumb);
}
else
ret = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
}
NS_RELEASE(oldFile); // always release our file ref, store has own
}
}
if (NS_SUCCEEDED(ret) && thumb)
{
mdb_count outTotal; // total somethings to do in operation
mdb_count outCurrent; // subportion of total completed so far
mdb_bool outDone = PR_FALSE; // is operation finished?
mdb_bool outBroken; // is operation irreparably dead and broken?
do
{
ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
if (ret != 0)
{// mork isn't really doing NS erorrs yet.
outDone = PR_TRUE;
break;
}
}
while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
// m_mdbEnv->ClearErrors(); // ### temporary...
if (NS_SUCCEEDED(ret) && outDone)
{
ret = myMDBFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
if (ret == NS_OK && m_mdbStore)
ret = InitExistingDB();
}
#ifdef DEBUG_bienvenu1
DumpContents();
#endif
}
else // ### need error code saying why open file store failed
{
nsIMdbFile* newFile = 0;
ret = myMDBFactory->CreateNewFile(m_mdbEnv, dbHeap, dbName, &newFile);
if ( newFile )
{
if (ret == NS_OK)
{
mdbOpenPolicy inOpenPolicy;
inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
inOpenPolicy.mOpenPolicy_MinMemory = 0;
inOpenPolicy.mOpenPolicy_MaxLazy = 0;
ret = myMDBFactory->CreateNewFileStore(m_mdbEnv, dbHeap,
newFile, &inOpenPolicy, &m_mdbStore);
if (ret == NS_OK)
ret = InitNewDB();
}
NS_RELEASE(newFile); // always release our file ref, store has own
}
}
NS_IF_RELEASE(thumb);
nsCRT::free(nativeFileName);
}
}
return ret;
}
NS_IMETHODIMP nsMsgFolderCache::Init(nsIFileSpec *dbFileSpec)
{
if (!dbFileSpec)
return NS_ERROR_NULL_POINTER;
nsresult rv = NS_ERROR_OUT_OF_MEMORY;
m_cacheElements = new nsSupportsHashtable;
if (m_cacheElements)
{
rv = dbFileSpec->GetFileSpec(&m_dbFileSpec);
if (NS_SUCCEEDED(rv))
{
PRBool exists = m_dbFileSpec.Exists();
// ### evil cast until MDB supports file streams.
rv = OpenMDB((const char *) m_dbFileSpec, exists);
// if this fails and panacea.dat exists, try blowing away the db and recreating it
if (NS_FAILED(rv) && exists)
{
if (m_mdbStore)
m_mdbStore->Release();
m_dbFileSpec.Delete(PR_FALSE);
rv = OpenMDB((const char *) m_dbFileSpec, PR_FALSE);
}
}
}
return rv;
}
NS_IMETHODIMP nsMsgFolderCache::GetCacheElement(const char *pathKey, PRBool createIfMissing,
nsIMsgFolderCacheElement **result)
{
if (!result || !pathKey)
return NS_ERROR_NULL_POINTER;
if (strlen(pathKey) == 0) {
return NS_ERROR_FAILURE;
}
nsCStringKey hashKey(pathKey);
*result = (nsIMsgFolderCacheElement *) m_cacheElements->Get(&hashKey);
// nsHashTable already does an address on *result
if (*result)
{
return NS_OK;
}
else if (createIfMissing)
{
nsIMdbRow* hdrRow;
if (GetStore())
{
mdb_err err = GetStore()->NewRow(GetEnv(), m_folderRowScopeToken, // row scope for row ids
&hdrRow);
if (NS_SUCCEEDED(err) && hdrRow)
{
m_mdbAllFoldersTable->AddRow(GetEnv(), hdrRow);
nsresult ret = AddCacheElement(pathKey, hdrRow, result);
if (*result)
(*result)->SetStringProperty("key", pathKey);
hdrRow->Release();
return ret;
}
}
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsMsgFolderCache::Close()
{
return Commit(PR_TRUE);
}
NS_IMETHODIMP nsMsgFolderCache::Commit(PRBool compress)
{
nsresult ret = NS_OK;
nsIMdbThumb *commitThumb = nsnull;
if (m_mdbStore)
if (compress)
ret = m_mdbStore->CompressCommit(GetEnv(), &commitThumb);
else
ret = m_mdbStore->LargeCommit(GetEnv(), &commitThumb);
if (commitThumb)
{
mdb_count outTotal = 0; // total somethings to do in operation
mdb_count outCurrent = 0; // subportion of total completed so far
mdb_bool outDone = PR_FALSE; // is operation finished?
mdb_bool outBroken = PR_FALSE; // is operation irreparably dead and broken?
while (!outDone && !outBroken && ret == NS_OK)
{
ret = commitThumb->DoMore(GetEnv(), &outTotal, &outCurrent, &outDone, &outBroken);
}
NS_IF_RELEASE(commitThumb);
}
// ### do something with error, but clear it now because mork errors out on commits.
if (GetEnv())
GetEnv()->ClearErrors();
return ret;
}
nsresult nsMsgFolderCache::AddCacheElement(const char *key, nsIMdbRow *row, nsIMsgFolderCacheElement **result)
{
nsMsgFolderCacheElement *cacheElement = new nsMsgFolderCacheElement;
if (cacheElement)
{
cacheElement->SetMDBRow(row);
cacheElement->SetOwningCache(this);
nsCAutoString hashStrKey(key);
// if caller didn't pass in key, try to get it from row.
if (!key)
{
char *existingKey = nsnull;
cacheElement->GetStringProperty("key", &existingKey);
cacheElement->SetKey(existingKey);
hashStrKey = existingKey;
PR_Free(existingKey);
}
else
cacheElement->SetKey((char *) key);
nsCOMPtr<nsISupports> supports(do_QueryInterface(cacheElement));
if(supports)
{
nsCStringKey hashKey(hashStrKey);
m_cacheElements->Put(&hashKey, supports);
}
if (result)
{
*result = cacheElement;
NS_ADDREF(*result);
}
return NS_OK;
}
else
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult nsMsgFolderCache::RowCellColumnToCharPtr(nsIMdbRow *hdrRow, mdb_token columnToken, char **resultPtr)
{
nsresult err = NS_OK;
nsIMdbCell *hdrCell;
if (hdrRow) // ### probably should be an error if hdrRow is NULL...
{
err = hdrRow->GetCell(GetEnv(), columnToken, &hdrCell);
if (err == NS_OK && hdrCell)
{
struct mdbYarn yarn;
hdrCell->AliasYarn(GetEnv(), &yarn);
char *result = (char *) PR_Malloc(yarn.mYarn_Fill + 1);
if (result)
{
memcpy(result, yarn.mYarn_Buf, yarn.mYarn_Fill);
result[yarn.mYarn_Fill] = '\0';
}
else
err = NS_ERROR_OUT_OF_MEMORY;
*resultPtr = result;
hdrCell->Release(); // always release ref
}
}
return err;
}