summaryrefslogtreecommitdiffstats
path: root/mailnews/addrbook/src/nsAddrDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/addrbook/src/nsAddrDatabase.cpp')
-rw-r--r--mailnews/addrbook/src/nsAddrDatabase.cpp3335
1 files changed, 3335 insertions, 0 deletions
diff --git a/mailnews/addrbook/src/nsAddrDatabase.cpp b/mailnews/addrbook/src/nsAddrDatabase.cpp
new file mode 100644
index 000000000..ea29ba8af
--- /dev/null
+++ b/mailnews/addrbook/src/nsAddrDatabase.cpp
@@ -0,0 +1,3335 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// this file implements the nsAddrDatabase interface using the MDB Interface.
+
+#include "nsAddrDatabase.h"
+#include "nsStringGlue.h"
+#include "nsAutoPtr.h"
+#include "nsUnicharUtils.h"
+#include "nsAbBaseCID.h"
+#include "nsIAbMDBDirectory.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMorkCID.h"
+#include "nsIMdbFactoryFactory.h"
+#include "prprf.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsIPromptService.h"
+#include "nsIStringBundle.h"
+#include "nsIFile.h"
+#include "nsEmbedCID.h"
+#include "nsIProperty.h"
+#include "nsIVariant.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIAbManager.h"
+#include "mozilla/Services.h"
+#include <algorithm>
+
+#define ID_PAB_TABLE 1
+#define ID_DELETEDCARDS_TABLE 2
+
+// There's two books by default, although Mac may have one more, so set this
+// to three. Its not going to affect much, but will save us a few reallocations
+// when the cache is allocated.
+const uint32_t kInitialAddrDBCacheSize = 3;
+
+static const char kPabTableKind[] = "ns:addrbk:db:table:kind:pab";
+static const char kDeletedCardsTableKind[] = "ns:addrbk:db:table:kind:deleted"; // this table is used to keep the deleted cards
+
+static const char kCardRowScope[] = "ns:addrbk:db:row:scope:card:all";
+static const char kListRowScope[] = "ns:addrbk:db:row:scope:list:all";
+static const char kDataRowScope[] = "ns:addrbk:db:row:scope:data:all";
+
+#define DATAROW_ROWID 1
+
+#define COLUMN_STR_MAX 16
+
+#define PURGE_CUTOFF_COUNT 50
+
+static const char kRecordKeyColumn[] = "RecordKey";
+static const char kLastRecordKeyColumn[] = "LastRecordKey";
+static const char kRowIDProperty[] = "DbRowID";
+
+static const char kLowerListNameColumn[] = "LowercaseListName";
+
+struct mdbOid gAddressBookTableOID;
+
+static const char kMailListAddressFormat[] = "Address%d";
+
+nsAddrDatabase::nsAddrDatabase()
+ : m_mdbEnv(nullptr), m_mdbStore(nullptr),
+ m_mdbPabTable(nullptr),
+ m_mdbDeletedCardsTable(nullptr),
+ m_mdbTokensInitialized(false),
+ m_PabTableKind(0),
+ m_MailListTableKind(0),
+ m_DeletedCardsTableKind(0),
+ m_CardRowScopeToken(0),
+ m_FirstNameColumnToken(0),
+ m_LastNameColumnToken(0),
+ m_PhoneticFirstNameColumnToken(0),
+ m_PhoneticLastNameColumnToken(0),
+ m_DisplayNameColumnToken(0),
+ m_NickNameColumnToken(0),
+ m_PriEmailColumnToken(0),
+ m_2ndEmailColumnToken(0),
+ m_WorkPhoneColumnToken(0),
+ m_HomePhoneColumnToken(0),
+ m_FaxColumnToken(0),
+ m_PagerColumnToken(0),
+ m_CellularColumnToken(0),
+ m_WorkPhoneTypeColumnToken(0),
+ m_HomePhoneTypeColumnToken(0),
+ m_FaxTypeColumnToken(0),
+ m_PagerTypeColumnToken(0),
+ m_CellularTypeColumnToken(0),
+ m_HomeAddressColumnToken(0),
+ m_HomeAddress2ColumnToken(0),
+ m_HomeCityColumnToken(0),
+ m_HomeStateColumnToken(0),
+ m_HomeZipCodeColumnToken(0),
+ m_HomeCountryColumnToken(0),
+ m_WorkAddressColumnToken(0),
+ m_WorkAddress2ColumnToken(0),
+ m_WorkCityColumnToken(0),
+ m_WorkStateColumnToken(0),
+ m_WorkZipCodeColumnToken(0),
+ m_WorkCountryColumnToken(0),
+ m_CompanyColumnToken(0),
+ m_AimScreenNameColumnToken(0),
+ m_AnniversaryYearColumnToken(0),
+ m_AnniversaryMonthColumnToken(0),
+ m_AnniversaryDayColumnToken(0),
+ m_SpouseNameColumnToken(0),
+ m_FamilyNameColumnToken(0),
+ m_DefaultAddressColumnToken(0),
+ m_CategoryColumnToken(0),
+ m_WebPage1ColumnToken(0),
+ m_WebPage2ColumnToken(0),
+ m_BirthYearColumnToken(0),
+ m_BirthMonthColumnToken(0),
+ m_BirthDayColumnToken(0),
+ m_Custom1ColumnToken(0),
+ m_Custom2ColumnToken(0),
+ m_Custom3ColumnToken(0),
+ m_Custom4ColumnToken(0),
+ m_NotesColumnToken(0),
+ m_LastModDateColumnToken(0),
+ m_MailFormatColumnToken(0),
+ m_PopularityIndexColumnToken(0),
+ m_AddressCharSetColumnToken(0),
+ m_LastRecordKey(0),
+ m_dbDirectory(nullptr)
+{
+}
+
+nsAddrDatabase::~nsAddrDatabase()
+{
+ Close(false); // better have already been closed.
+
+ // better not be any listeners, because we're going away.
+ NS_ASSERTION(m_ChangeListeners.Length() == 0, "shouldn't have any listeners");
+
+ RemoveFromCache(this);
+ // clean up after ourself!
+ if (m_mdbPabTable)
+ m_mdbPabTable->Release();
+ if (m_mdbDeletedCardsTable)
+ m_mdbDeletedCardsTable->Release();
+ NS_IF_RELEASE(m_mdbStore);
+ NS_IF_RELEASE(m_mdbEnv);
+}
+
+NS_IMPL_ISUPPORTS(nsAddrDatabase, nsIAddrDatabase, nsIAddrDBAnnouncer)
+
+NS_IMETHODIMP nsAddrDatabase::AddListener(nsIAddrDBListener *listener)
+{
+ NS_ENSURE_ARG_POINTER(listener);
+ return m_ChangeListeners.AppendElement(listener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::RemoveListener(nsIAddrDBListener *listener)
+{
+ NS_ENSURE_ARG_POINTER(listener);
+ return m_ChangeListeners.RemoveElement(listener) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::NotifyCardAttribChange(uint32_t abCode)
+{
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnCardAttribChange, (abCode));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::NotifyCardEntryChange(uint32_t aAbCode, nsIAbCard *aCard, nsIAbDirectory *aParent)
+{
+ int32_t currentDisplayNameVersion = 0;
+
+ //Update "mail.displayname.version" prefernce
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ prefs->GetIntPref("mail.displayname.version",&currentDisplayNameVersion);
+
+ prefs->SetIntPref("mail.displayname.version",++currentDisplayNameVersion);
+
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnCardEntryChange, (aAbCode, aCard, aParent));
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::NotifyListEntryChange(uint32_t abCode, nsIAbDirectory *dir)
+{
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnListEntryChange, (abCode, dir));
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsAddrDatabase::NotifyAnnouncerGoingAway(void)
+{
+ NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(m_ChangeListeners, nsIAddrDBListener,
+ OnAnnouncerGoingAway, ());
+ return NS_OK;
+}
+
+
+// Apparently its not good for nsTArray to be allocated as static. Don't know
+// why it isn't but its not, so don't think about making it a static variable.
+// Maybe bz knows.
+nsTArray<nsAddrDatabase*>* nsAddrDatabase::m_dbCache = nullptr;
+
+nsTArray<nsAddrDatabase*>*
+nsAddrDatabase::GetDBCache()
+{
+ if (!m_dbCache)
+ m_dbCache = new AutoTArray<nsAddrDatabase*, kInitialAddrDBCacheSize>;
+
+ return m_dbCache;
+}
+
+void
+nsAddrDatabase::CleanupCache()
+{
+ if (m_dbCache)
+ {
+ for (int32_t i = m_dbCache->Length() - 1; i >= 0; --i)
+ {
+ nsAddrDatabase* pAddrDB = m_dbCache->ElementAt(i);
+ if (pAddrDB)
+ pAddrDB->ForceClosed();
+ }
+ // NS_ASSERTION(m_dbCache.Length() == 0, "some msg dbs left open"); // better not be any open db's.
+ delete m_dbCache;
+ m_dbCache = nullptr;
+ }
+}
+
+//----------------------------------------------------------------------
+// FindInCache - this addrefs the db it finds.
+//----------------------------------------------------------------------
+nsAddrDatabase* nsAddrDatabase::FindInCache(nsIFile *dbName)
+{
+ nsTArray<nsAddrDatabase*>* dbCache = GetDBCache();
+ uint32_t length = dbCache->Length();
+ for (uint32_t i = 0; i < length; ++i)
+ {
+ nsAddrDatabase* pAddrDB = dbCache->ElementAt(i);
+ if (pAddrDB->MatchDbName(dbName))
+ {
+ NS_ADDREF(pAddrDB);
+ return pAddrDB;
+ }
+ }
+ return nullptr;
+}
+
+bool nsAddrDatabase::MatchDbName(nsIFile* dbName) // returns true if they match
+{
+ bool dbMatches = false;
+
+ nsresult rv = m_dbName->Equals(dbName, &dbMatches);
+ if (NS_FAILED(rv))
+ return false;
+
+ return dbMatches;
+}
+
+//----------------------------------------------------------------------
+// RemoveFromCache
+//----------------------------------------------------------------------
+void nsAddrDatabase::RemoveFromCache(nsAddrDatabase* pAddrDB)
+{
+ if (m_dbCache)
+ m_dbCache->RemoveElement(pAddrDB);
+}
+
+void nsAddrDatabase::GetMDBFactory(nsIMdbFactory ** aMdbFactory)
+{
+ if (!mMdbFactory)
+ {
+ nsresult rv;
+ nsCOMPtr <nsIMdbFactoryService> mdbFactoryService = do_GetService(NS_MORK_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && mdbFactoryService)
+ rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory));
+ }
+ NS_IF_ADDREF(*aMdbFactory = mMdbFactory);
+}
+
+/* caller need to delete *aDbPath */
+NS_IMETHODIMP nsAddrDatabase::GetDbPath(nsIFile* *aDbPath)
+{
+ if (!aDbPath)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_dbName->Clone(aDbPath);
+}
+
+NS_IMETHODIMP nsAddrDatabase::SetDbPath(nsIFile* aDbPath)
+{
+ return aDbPath->Clone(getter_AddRefs(m_dbName));
+}
+
+NS_IMETHODIMP nsAddrDatabase::Open
+(nsIFile *aMabFile, bool aCreate, bool upgrading /* unused */, nsIAddrDatabase** pAddrDB)
+{
+ *pAddrDB = nullptr;
+
+ nsAddrDatabase *pAddressBookDB = FindInCache(aMabFile);
+
+ if (pAddressBookDB) {
+ *pAddrDB = pAddressBookDB;
+ return NS_OK;
+ }
+
+ nsresult rv = OpenInternal(aMabFile, aCreate, pAddrDB);
+ if (NS_SUCCEEDED(rv))
+ return NS_OK;
+
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ static bool gAlreadyAlerted;
+ // only do this once per session to avoid annoying the user
+ if (!gAlreadyAlerted)
+ {
+ gAlreadyAlerted = true;
+ nsAutoString mabFileName;
+ rv = aMabFile->GetLeafName(mabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ AlertAboutLockedMabFile(mabFileName.get());
+
+ // We just overwrote rv, so return the proper value here.
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ }
+ // try one more time
+ // but first rename corrupt mab file
+ // and prompt the user
+ else if (aCreate)
+ {
+ nsCOMPtr<nsIFile> dummyBackupMabFile;
+ nsCOMPtr<nsIFile> actualBackupMabFile;
+
+ // First create a clone of the corrupt mab file that we'll
+ // use to generate the name for the backup file that we are
+ // going to move it to.
+ rv = aMabFile->Clone(getter_AddRefs(dummyBackupMabFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now create a second clone that we'll use to do the move
+ // (this allows us to leave the original name intact)
+ rv = aMabFile->Clone(getter_AddRefs(actualBackupMabFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now we try and generate a new name for the corrupt mab
+ // file using the dummy backup mab file
+
+ // First append .bak - we have to do this the long way as
+ // AppendNative is to the path, not the LeafName.
+ nsAutoCString dummyBackupMabFileName;
+ rv = dummyBackupMabFile->GetNativeLeafName(dummyBackupMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dummyBackupMabFileName.Append(NS_LITERAL_CSTRING(".bak"));
+
+ rv = dummyBackupMabFile->SetNativeLeafName(dummyBackupMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now see if we can create it unique
+ rv = dummyBackupMabFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now get the new name
+ nsAutoCString backupMabFileName;
+ rv = dummyBackupMabFile->GetNativeLeafName(backupMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // And the parent directory
+ nsCOMPtr<nsIFile> parentDir;
+ rv = dummyBackupMabFile->GetParent(getter_AddRefs(parentDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now move the corrupt file to its backup location
+ rv = actualBackupMabFile->MoveToNative(parentDir, backupMabFileName);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to rename corrupt mab file");
+
+ if (NS_SUCCEEDED(rv)) {
+ // now we can try to recreate the original mab file
+ rv = OpenInternal(aMabFile, aCreate, pAddrDB);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create .mab file, after rename");
+
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoString originalMabFileName;
+ rv = aMabFile->GetLeafName(originalMabFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if this fails, we don't care
+ (void)AlertAboutCorruptMabFile(originalMabFileName.get(),
+ NS_ConvertASCIItoUTF16(backupMabFileName).get());
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult nsAddrDatabase::DisplayAlert(const char16_t *titleName, const char16_t *alertStringName, const char16_t **formatStrings, int32_t numFormatStrings)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString alertMessage;
+ rv = bundle->FormatStringFromName(alertStringName, formatStrings, numFormatStrings,
+ getter_Copies(alertMessage));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString alertTitle;
+ rv = bundle->GetStringFromName(titleName, getter_Copies(alertTitle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPromptService> prompter =
+ do_GetService(NS_PROMPTSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return prompter->Alert(nullptr /* we don't know the parent window */, alertTitle.get(), alertMessage.get());
+}
+
+nsresult nsAddrDatabase::AlertAboutCorruptMabFile(const char16_t *aOldFileName, const char16_t *aNewFileName)
+{
+ const char16_t *formatStrings[] = { aOldFileName, aOldFileName, aNewFileName };
+ return DisplayAlert(u"corruptMabFileTitle",
+ u"corruptMabFileAlert", formatStrings, 3);
+}
+
+nsresult nsAddrDatabase::AlertAboutLockedMabFile(const char16_t *aFileName)
+{
+ const char16_t *formatStrings[] = { aFileName };
+ return DisplayAlert(u"lockedMabFileTitle",
+ u"lockedMabFileAlert", formatStrings, 1);
+}
+
+nsresult
+nsAddrDatabase::OpenInternal(nsIFile *aMabFile, bool aCreate, nsIAddrDatabase** pAddrDB)
+{
+ nsAddrDatabase *pAddressBookDB = new nsAddrDatabase();
+ if (!pAddressBookDB) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(pAddressBookDB);
+
+ nsresult rv = pAddressBookDB->OpenMDB(aMabFile, aCreate);
+ if (NS_SUCCEEDED(rv))
+ {
+ pAddressBookDB->SetDbPath(aMabFile);
+ GetDBCache()->AppendElement(pAddressBookDB);
+ *pAddrDB = pAddressBookDB;
+ }
+ else
+ {
+ *pAddrDB = nullptr;
+ pAddressBookDB->ForceClosed();
+ NS_IF_RELEASE(pAddressBookDB);
+ pAddressBookDB = nullptr;
+ }
+ return rv;
+}
+
+// Open the MDB database synchronously. If successful, this routine
+// will set up the m_mdbStore and m_mdbEnv of the database object
+// so other database calls can work.
+NS_IMETHODIMP nsAddrDatabase::OpenMDB(nsIFile *dbName, bool create)
+{
+ nsresult ret;
+ nsCOMPtr<nsIMdbFactory> mdbFactory;
+ GetMDBFactory(getter_AddRefs(mdbFactory));
+ NS_ENSURE_TRUE(mdbFactory, NS_ERROR_FAILURE);
+
+ ret = mdbFactory->MakeEnv(NULL, &m_mdbEnv);
+ if (NS_SUCCEEDED(ret))
+ {
+ nsIMdbThumb *thumb = nullptr;
+ nsAutoCString filePath;
+
+ ret = dbName->GetNativePath(filePath);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ nsIMdbHeap* dbHeap = nullptr;
+
+ if (m_mdbEnv)
+ m_mdbEnv->SetAutoClear(true);
+
+ bool dbNameExists = false;
+ ret = dbName->Exists(&dbNameExists);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ if (!dbNameExists)
+ ret = NS_ERROR_FILE_NOT_FOUND;
+ else
+ {
+ mdbOpenPolicy inOpenPolicy;
+ mdb_bool canOpen;
+ mdbYarn outFormatVersion;
+ nsIMdbFile* oldFile = nullptr;
+ int64_t fileSize;
+ ret = dbName->GetFileSize(&fileSize);
+ NS_ENSURE_SUCCESS(ret, ret);
+
+ ret = mdbFactory->OpenOldFile(m_mdbEnv, dbHeap, filePath.get(),
+ mdbBool_kFalse, // not readonly, we want modifiable
+ &oldFile);
+ if ( oldFile )
+ {
+ if (NS_SUCCEEDED(ret))
+ {
+ ret = mdbFactory->CanOpenFilePort(m_mdbEnv, oldFile, // the file to investigate
+ &canOpen, &outFormatVersion);
+ if (NS_SUCCEEDED(ret) && canOpen)
+ {
+ inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
+ inOpenPolicy.mOpenPolicy_MinMemory = 0;
+ inOpenPolicy.mOpenPolicy_MaxLazy = 0;
+
+ ret = mdbFactory->OpenFileStore(m_mdbEnv, dbHeap,
+ oldFile, &inOpenPolicy, &thumb);
+ }
+ else if (fileSize != 0)
+ ret = NS_ERROR_FILE_ACCESS_DENIED;
+ }
+ NS_RELEASE(oldFile); // always release our file ref, store has own
+ }
+ if (NS_FAILED(ret))
+ ret = NS_ERROR_FILE_ACCESS_DENIED;
+ }
+
+ 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 = false; // is operation finished?
+ mdb_bool outBroken; // is operation irreparably dead and broken?
+ do
+ {
+ ret = thumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
+ if (NS_FAILED(ret))
+ {
+ outDone = true;
+ break;
+ }
+ }
+ while (NS_SUCCEEDED(ret) && !outBroken && !outDone);
+ if (NS_SUCCEEDED(ret) && outDone)
+ {
+ ret = mdbFactory->ThumbToOpenStore(m_mdbEnv, thumb, &m_mdbStore);
+ if (NS_SUCCEEDED(ret) && m_mdbStore)
+ {
+ ret = InitExistingDB();
+ create = false;
+ }
+ }
+ }
+ else if (create && ret != NS_ERROR_FILE_ACCESS_DENIED)
+ {
+ nsIMdbFile* newFile = 0;
+ ret = mdbFactory->CreateNewFile(m_mdbEnv, dbHeap, filePath.get(), &newFile);
+ if ( newFile )
+ {
+ if (NS_SUCCEEDED(ret))
+ {
+ mdbOpenPolicy inOpenPolicy;
+
+ inOpenPolicy.mOpenPolicy_ScopePlan.mScopeStringSet_Count = 0;
+ inOpenPolicy.mOpenPolicy_MinMemory = 0;
+ inOpenPolicy.mOpenPolicy_MaxLazy = 0;
+
+ ret = mdbFactory->CreateNewFileStore(m_mdbEnv, dbHeap,
+ newFile, &inOpenPolicy,
+ &m_mdbStore);
+ if (NS_SUCCEEDED(ret))
+ ret = InitNewDB();
+ }
+ NS_RELEASE(newFile); // always release our file ref, store has own
+ }
+ }
+ NS_IF_RELEASE(thumb);
+ }
+ return ret;
+}
+
+NS_IMETHODIMP nsAddrDatabase::CloseMDB(bool commit)
+{
+ if (commit)
+ Commit(nsAddrDBCommitType::kSessionCommit);
+//??? RemoveFromCache(this); // if we've closed it, better not leave it in the cache.
+ return NS_OK;
+}
+
+// force the database to close - this'll flush out anybody holding onto
+// a database without having a listener!
+// This is evil in the com world, but there are times we need to delete the file.
+NS_IMETHODIMP nsAddrDatabase::ForceClosed()
+{
+ nsresult err = NS_OK;
+ nsCOMPtr<nsIAddrDatabase> aDb(do_QueryInterface(this, &err));
+
+ // make sure someone has a reference so object won't get deleted out from under us.
+ AddRef();
+ NotifyAnnouncerGoingAway();
+ // OK, remove from cache first and close the store.
+ RemoveFromCache(this);
+
+ err = CloseMDB(false); // since we're about to delete it, no need to commit.
+ NS_IF_RELEASE(m_mdbStore);
+ Release();
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::Commit(uint32_t commitType)
+{
+ nsresult err = NS_OK;
+ nsIMdbThumb *commitThumb = nullptr;
+
+ if (commitType == nsAddrDBCommitType::kLargeCommit ||
+ commitType == nsAddrDBCommitType::kSessionCommit)
+ {
+ mdb_percent outActualWaste = 0;
+ mdb_bool outShould;
+ if (m_mdbStore && m_mdbEnv)
+ {
+ // check how much space would be saved by doing a compress commit.
+ // If it's more than 30%, go for it.
+ // N.B. - I'm not sure this calls works in Mork for all cases.
+ err = m_mdbStore->ShouldCompress(m_mdbEnv, 30, &outActualWaste, &outShould);
+ if (NS_SUCCEEDED(err) && outShould)
+ {
+ commitType = nsAddrDBCommitType::kCompressCommit;
+ }
+ }
+ }
+
+ if (m_mdbStore && m_mdbEnv)
+ {
+ switch (commitType)
+ {
+ case nsAddrDBCommitType::kLargeCommit:
+ err = m_mdbStore->LargeCommit(m_mdbEnv, &commitThumb);
+ break;
+ case nsAddrDBCommitType::kSessionCommit:
+ // comment out until persistence works.
+ err = m_mdbStore->SessionCommit(m_mdbEnv, &commitThumb);
+ break;
+ case nsAddrDBCommitType::kCompressCommit:
+ err = m_mdbStore->CompressCommit(m_mdbEnv, &commitThumb);
+ break;
+ }
+ }
+ if (commitThumb && m_mdbEnv)
+ {
+ mdb_count outTotal = 0; // total somethings to do in operation
+ mdb_count outCurrent = 0; // subportion of total completed so far
+ mdb_bool outDone = false; // is operation finished?
+ mdb_bool outBroken = false; // is operation irreparably dead and broken?
+ while (!outDone && !outBroken && NS_SUCCEEDED(err))
+ {
+ err = commitThumb->DoMore(m_mdbEnv, &outTotal, &outCurrent, &outDone, &outBroken);
+ }
+ NS_RELEASE(commitThumb);
+ }
+ // ### do something with error, but clear it now because mork errors out on commits.
+ if (m_mdbEnv)
+ m_mdbEnv->ClearErrors();
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::Close(bool forceCommit /* = TRUE */)
+{
+ return CloseMDB(forceCommit);
+}
+
+// set up empty tablesetc.
+nsresult nsAddrDatabase::InitNewDB()
+{
+ nsresult err = InitMDBInfo();
+ if (NS_SUCCEEDED(err))
+ {
+ err = InitPabTable();
+ err = InitLastRecorKey();
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::AddRowToDeletedCardsTable(nsIAbCard *card, nsIMdbRow **pCardRow)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+ if (!m_mdbDeletedCardsTable)
+ rv = InitDeletedCardsTable(true);
+
+ if (NS_SUCCEEDED(rv)) {
+ // lets first purge old records if there are more than PURGE_CUTOFF_COUNT records
+ PurgeDeletedCardTable();
+ nsCOMPtr<nsIMdbRow> cardRow;
+ rv = GetNewRow(getter_AddRefs(cardRow));
+ if (NS_SUCCEEDED(rv) && cardRow) {
+ nsresult merror = m_mdbDeletedCardsTable->AddRow(m_mdbEnv, cardRow);
+ NS_ENSURE_SUCCESS(merror, NS_ERROR_FAILURE);
+ nsString unicodeStr;
+ card->GetFirstName(unicodeStr);
+ AddFirstName(cardRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ card->GetLastName(unicodeStr);
+ AddLastName(cardRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ card->GetDisplayName(unicodeStr);
+ AddDisplayName(cardRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ card->GetPrimaryEmail(unicodeStr);
+ if (!unicodeStr.IsEmpty())
+ AddUnicodeToColumn(cardRow, m_PriEmailColumnToken, m_LowerPriEmailColumnToken, unicodeStr.get());
+
+ card->GetPropertyAsAString(k2ndEmailProperty, unicodeStr);
+ if (!unicodeStr.IsEmpty())
+ AddUnicodeToColumn(cardRow, m_2ndEmailColumnToken, m_Lower2ndEmailColumnToken, unicodeStr.get());
+
+ uint32_t nowInSeconds;
+ PRTime now = PR_Now();
+ PRTime2Seconds(now, &nowInSeconds);
+ AddIntColumn(cardRow, m_LastModDateColumnToken, nowInSeconds);
+
+ nsString value;
+ GetCardValue(card, CARD_ATTRIB_PALMID, getter_Copies(value));
+ if (!value.IsEmpty())
+ {
+ nsCOMPtr<nsIAbCard> addedCard;
+ rv = CreateCardFromDeletedCardsTable(cardRow, 0, getter_AddRefs(addedCard));
+ if (NS_SUCCEEDED(rv))
+ SetCardValue(addedCard, CARD_ATTRIB_PALMID, value.get(), false);
+ }
+ NS_IF_ADDREF(*pCardRow = cardRow);
+ }
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ }
+ return rv;
+}
+
+nsresult nsAddrDatabase::DeleteRowFromDeletedCardsTable(nsIMdbRow *pCardRow)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult merror = NS_OK;
+ if (m_mdbDeletedCardsTable) {
+ pCardRow->CutAllColumns(m_mdbEnv);
+ merror = m_mdbDeletedCardsTable->CutRow(m_mdbEnv, pCardRow);
+ }
+ return merror;
+}
+
+
+nsresult nsAddrDatabase::InitDeletedCardsTable(bool aCreate)
+{
+ nsresult mdberr = NS_OK;
+ if (!m_mdbDeletedCardsTable)
+ {
+ struct mdbOid deletedCardsTableOID;
+ deletedCardsTableOID.mOid_Scope = m_CardRowScopeToken;
+ deletedCardsTableOID.mOid_Id = ID_DELETEDCARDS_TABLE;
+ if (m_mdbStore && m_mdbEnv)
+ {
+ m_mdbStore->GetTable(m_mdbEnv, &deletedCardsTableOID, &m_mdbDeletedCardsTable);
+ // if deletedCardsTable does not exist and bCreate is set, create a new one
+ if (!m_mdbDeletedCardsTable && aCreate)
+ {
+ mdberr = (nsresult) m_mdbStore->NewTableWithOid(m_mdbEnv, &deletedCardsTableOID,
+ m_DeletedCardsTableKind,
+ true, (const mdbOid*)nullptr,
+ &m_mdbDeletedCardsTable);
+ }
+ }
+ }
+ return mdberr;
+}
+
+nsresult nsAddrDatabase::InitPabTable()
+{
+ return m_mdbStore && m_mdbEnv ? m_mdbStore->NewTableWithOid(m_mdbEnv,
+ &gAddressBookTableOID,
+ m_PabTableKind,
+ false,
+ (const mdbOid*)nullptr,
+ &m_mdbPabTable)
+ : NS_ERROR_NULL_POINTER;
+}
+
+//save the last record number, store in m_DataRowScopeToken, row 1
+nsresult nsAddrDatabase::InitLastRecorKey()
+{
+ if (!m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow *pDataRow = nullptr;
+ mdbOid dataRowOid;
+ dataRowOid.mOid_Scope = m_DataRowScopeToken;
+ dataRowOid.mOid_Id = DATAROW_ROWID;
+ nsresult err = m_mdbStore->NewRowWithOid(m_mdbEnv, &dataRowOid, &pDataRow);
+
+ if (NS_SUCCEEDED(err) && pDataRow)
+ {
+ m_LastRecordKey = 0;
+ err = AddIntColumn(pDataRow, m_LastRecordKeyColumnToken, 0);
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pDataRow);
+ NS_RELEASE(pDataRow);
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::GetDataRow(nsIMdbRow **pDataRow)
+{
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow *pRow = nullptr;
+ mdbOid dataRowOid;
+ dataRowOid.mOid_Scope = m_DataRowScopeToken;
+ dataRowOid.mOid_Id = DATAROW_ROWID;
+ m_mdbStore->GetRow(m_mdbEnv, &dataRowOid, &pRow);
+ *pDataRow = pRow;
+
+ return pRow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::GetLastRecordKey()
+{
+ if (!m_mdbPabTable)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr <nsIMdbRow> pDataRow;
+ nsresult err = GetDataRow(getter_AddRefs(pDataRow));
+
+ if (NS_SUCCEEDED(err) && pDataRow)
+ {
+ m_LastRecordKey = 0;
+ err = GetIntColumn(pDataRow, m_LastRecordKeyColumnToken, &m_LastRecordKey, 0);
+ if (NS_FAILED(err))
+ err = NS_ERROR_NOT_AVAILABLE;
+ return NS_OK;
+ }
+
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsresult nsAddrDatabase::UpdateLastRecordKey()
+{
+ if (!m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr <nsIMdbRow> pDataRow;
+ nsresult err = GetDataRow(getter_AddRefs(pDataRow));
+
+ if (NS_SUCCEEDED(err) && pDataRow)
+ {
+ err = AddIntColumn(pDataRow, m_LastRecordKeyColumnToken, m_LastRecordKey);
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pDataRow);
+ return NS_OK;
+ }
+ else if (!pDataRow)
+ err = InitLastRecorKey();
+ else
+ return NS_ERROR_NOT_AVAILABLE;
+ return err;
+}
+
+nsresult nsAddrDatabase::InitExistingDB()
+{
+ nsresult err = InitMDBInfo();
+ if (NS_SUCCEEDED(err))
+ {
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ err = m_mdbStore->GetTable(m_mdbEnv, &gAddressBookTableOID, &m_mdbPabTable);
+ if (NS_SUCCEEDED(err) && m_mdbPabTable)
+ {
+ err = GetLastRecordKey();
+ if (err == NS_ERROR_NOT_AVAILABLE)
+ CheckAndUpdateRecordKey();
+ UpdateLowercaseEmailListName();
+ }
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::CheckAndUpdateRecordKey()
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsIMdbRow* findRow = nullptr;
+ mdb_pos rowPos = 0;
+
+ nsresult merror = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+
+ NS_ENSURE_TRUE(NS_SUCCEEDED(merror) && rowCursor, NS_ERROR_FAILURE);
+
+ nsCOMPtr <nsIMdbRow> pDataRow;
+ err = GetDataRow(getter_AddRefs(pDataRow));
+ if (NS_FAILED(err))
+ InitLastRecorKey();
+
+ do
+ { //add key to each card and mailing list row
+ merror = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
+ if (NS_SUCCEEDED(merror) && findRow)
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(findRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ if (!IsDataRowScopeToken(rowOid.mOid_Scope))
+ {
+ m_LastRecordKey++;
+ err = AddIntColumn(findRow, m_RecordKeyColumnToken, m_LastRecordKey);
+ }
+ }
+ }
+ } while (findRow);
+
+ UpdateLastRecordKey();
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::UpdateLowercaseEmailListName()
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsIMdbRow* findRow = nullptr;
+ mdb_pos rowPos = 0;
+ bool commitRequired = false;
+
+ nsresult merror = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+
+ NS_ENSURE_TRUE(NS_SUCCEEDED(merror) && rowCursor, NS_ERROR_FAILURE);
+
+ do
+ { // Add lowercase primary+secondary email to each card and mailing list row.
+ merror = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos);
+ if (NS_SUCCEEDED(merror) && findRow)
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(findRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ nsAutoString tempString;
+ if (IsCardRowScopeToken(rowOid.mOid_Scope))
+ {
+ err = GetStringColumn(findRow, m_LowerPriEmailColumnToken, tempString);
+ if (NS_FAILED(err)) // not set yet
+ {
+ err = ConvertAndAddLowercaseColumn(findRow, m_PriEmailColumnToken,
+ m_LowerPriEmailColumnToken);
+ commitRequired = commitRequired || NS_SUCCEEDED(err);
+ }
+
+ err = GetStringColumn(findRow, m_Lower2ndEmailColumnToken, tempString);
+ if (NS_FAILED(err)) // not set yet
+ {
+ err = ConvertAndAddLowercaseColumn(findRow, m_2ndEmailColumnToken,
+ m_Lower2ndEmailColumnToken);
+ commitRequired = commitRequired || NS_SUCCEEDED(err);
+ }
+ }
+ else if (IsListRowScopeToken(rowOid.mOid_Scope))
+ {
+ err = GetStringColumn(findRow, m_LowerListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err)) // already set up
+ continue;
+
+ err = ConvertAndAddLowercaseColumn(findRow, m_ListNameColumnToken,
+ m_LowerListNameColumnToken);
+ commitRequired = commitRequired || NS_SUCCEEDED(err);
+ }
+ }
+ findRow->Release();
+ }
+ } while (findRow);
+
+ if (findRow)
+ findRow->Release();
+ rowCursor->Release();
+ if (commitRequired)
+ Commit(nsAddrDBCommitType::kLargeCommit);
+ return NS_OK;
+}
+
+/*
+We store UTF8 strings in the database. We need to convert the UTF8
+string into unicode string, then convert to lower case. Before storing
+back into the database, we need to convert the lowercase unicode string
+into UTF8 string.
+*/
+nsresult nsAddrDatabase::ConvertAndAddLowercaseColumn
+(nsIMdbRow * row, mdb_token fromCol, mdb_token toCol)
+{
+ nsAutoString colString;
+
+ nsresult rv = GetStringColumn(row, fromCol, colString);
+ if (!colString.IsEmpty())
+ {
+ rv = AddLowercaseColumn(row, toCol, NS_ConvertUTF16toUTF8(colString).get());
+ }
+ return rv;
+}
+
+// Change the unicode string to lowercase, then convert to UTF8 string to store in db
+nsresult nsAddrDatabase::AddUnicodeToColumn(nsIMdbRow * row, mdb_token aColToken, mdb_token aLowerCaseColToken, const char16_t* aUnicodeStr)
+{
+ nsresult rv = AddCharStringColumn(row, aColToken, NS_ConvertUTF16toUTF8(aUnicodeStr).get());
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(row, aLowerCaseColToken, NS_ConvertUTF16toUTF8(aUnicodeStr).get());
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+// initialize the various tokens and tables in our db's env
+nsresult nsAddrDatabase::InitMDBInfo()
+{
+ nsresult err = NS_OK;
+
+ if (!m_mdbTokensInitialized && m_mdbStore && m_mdbEnv)
+ {
+ m_mdbTokensInitialized = true;
+ err = m_mdbStore->StringToToken(m_mdbEnv, kCardRowScope, &m_CardRowScopeToken);
+ err = m_mdbStore->StringToToken(m_mdbEnv, kListRowScope, &m_ListRowScopeToken);
+ err = m_mdbStore->StringToToken(m_mdbEnv, kDataRowScope, &m_DataRowScopeToken);
+ gAddressBookTableOID.mOid_Scope = m_CardRowScopeToken;
+ gAddressBookTableOID.mOid_Id = ID_PAB_TABLE;
+ if (NS_SUCCEEDED(err))
+ {
+ m_mdbStore->StringToToken(m_mdbEnv, kFirstNameProperty, &m_FirstNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastNameProperty, &m_LastNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPhoneticFirstNameProperty, &m_PhoneticFirstNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPhoneticLastNameProperty, &m_PhoneticLastNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDisplayNameProperty, &m_DisplayNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kNicknameProperty, &m_NickNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPriEmailProperty, &m_PriEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLowerPriEmailColumn, &m_LowerPriEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, k2ndEmailProperty, &m_2ndEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLower2ndEmailColumn, &m_Lower2ndEmailColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPreferMailFormatProperty, &m_MailFormatColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPopularityIndexProperty, &m_PopularityIndexColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneProperty, &m_WorkPhoneColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneProperty, &m_HomePhoneColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFaxProperty, &m_FaxColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPagerProperty, &m_PagerColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCellularProperty, &m_CellularColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkPhoneTypeProperty, &m_WorkPhoneTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomePhoneTypeProperty, &m_HomePhoneTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFaxTypeProperty, &m_FaxTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kPagerTypeProperty, &m_PagerTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCellularTypeProperty, &m_CellularTypeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeAddressProperty, &m_HomeAddressColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeAddress2Property, &m_HomeAddress2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeCityProperty, &m_HomeCityColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeStateProperty, &m_HomeStateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeZipCodeProperty, &m_HomeZipCodeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeCountryProperty, &m_HomeCountryColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkAddressProperty, &m_WorkAddressColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkAddress2Property, &m_WorkAddress2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkCityProperty, &m_WorkCityColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkStateProperty, &m_WorkStateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkZipCodeProperty, &m_WorkZipCodeColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkCountryProperty, &m_WorkCountryColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kJobTitleProperty, &m_JobTitleColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDepartmentProperty, &m_DepartmentColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCompanyProperty, &m_CompanyColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kScreenNameProperty, &m_AimScreenNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryYearProperty, &m_AnniversaryYearColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryMonthProperty, &m_AnniversaryMonthColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAnniversaryDayProperty, &m_AnniversaryDayColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kSpouseNameProperty, &m_SpouseNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kFamilyNameProperty, &m_FamilyNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kWorkWebPageProperty, &m_WebPage1ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kHomeWebPageProperty, &m_WebPage2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthYearProperty, &m_BirthYearColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthMonthProperty, &m_BirthMonthColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kBirthDayProperty, &m_BirthDayColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom1Property, &m_Custom1ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom2Property, &m_Custom2ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom3Property, &m_Custom3ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kCustom4Property, &m_Custom4ColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kNotesProperty, &m_NotesColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastModifiedDateProperty, &m_LastModDateColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kRecordKeyColumn, &m_RecordKeyColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kAddressCharSetColumn, &m_AddressCharSetColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLastRecordKeyColumn, &m_LastRecordKeyColumnToken);
+
+ err = m_mdbStore->StringToToken(m_mdbEnv, kPabTableKind, &m_PabTableKind);
+
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListName, &m_ListNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListNickName, &m_ListNickNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListDescription, &m_ListDescriptionColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kMailListTotalAddresses, &m_ListTotalColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kLowerListNameColumn, &m_LowerListNameColumnToken);
+ m_mdbStore->StringToToken(m_mdbEnv, kDeletedCardsTableKind, &m_DeletedCardsTableKind);
+ }
+ }
+ return err;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsAddrDatabase::AddRecordKeyColumnToRow(nsIMdbRow *pRow)
+{
+ if (pRow && m_mdbEnv)
+ {
+ m_LastRecordKey++;
+ nsresult err = AddIntColumn(pRow, m_RecordKeyColumnToken, m_LastRecordKey);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pRow);
+ UpdateLastRecordKey();
+ return err;
+ }
+ return NS_ERROR_NULL_POINTER;
+}
+
+nsresult nsAddrDatabase::AddAttributeColumnsToRow(nsIAbCard *card, nsIMdbRow *cardRow)
+{
+ nsresult rv = NS_OK;
+
+ if ((!card && !cardRow) || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ cardRow->GetOid(m_mdbEnv, &rowOid);
+
+ card->SetPropertyAsUint32(kRowIDProperty, rowOid.mOid_Id);
+
+ // add the row to the singleton table.
+ if (card && cardRow)
+ {
+ nsCOMPtr<nsISimpleEnumerator> properties;
+ rv = card->GetProperties(getter_AddRefs(properties));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(properties->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> next;
+ rv = properties->GetNext(getter_AddRefs(next));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIProperty> prop = do_QueryInterface(next);
+ nsAutoString name;
+ prop->GetName(name);
+
+ nsCOMPtr<nsIVariant> variant;
+ prop->GetValue(getter_AddRefs(variant));
+
+ // We can't get as a char * because that messes up UTF8 stuff
+ nsAutoCString value;
+ variant->GetAsAUTF8String(value);
+
+ mdb_token token;
+ rv = m_mdbStore->StringToToken(m_mdbEnv, NS_ConvertUTF16toUTF8(name).get(), &token);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AddCharStringColumn(cardRow, token, value.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Primary email is special: it is stored lowercase as well as in its
+ // original format.
+ nsAutoString primaryEmail;
+ card->GetPrimaryEmail(primaryEmail);
+ AddPrimaryEmail(cardRow, NS_ConvertUTF16toUTF8(primaryEmail).get());
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::CreateNewCardAndAddToDB(nsIAbCard *aNewCard, bool aNotify /* = FALSE */, nsIAbDirectory *aParent)
+{
+ nsCOMPtr <nsIMdbRow> cardRow;
+
+ if (!aNewCard || !m_mdbPabTable || !m_mdbEnv || !m_mdbStore)
+ return NS_ERROR_NULL_POINTER;
+
+ // Per the UUID requirements, we want to try to reuse the local id if at all
+ // possible. nsACString::ToInteger probably won't fail if the local id looks
+ // like "23bozo" (returning 23 instead), but it's okay since we aren't going
+ // to overwrite anything with 23 if it already exists and the id for the row
+ // doesn't matter otherwise.
+ nsresult rv;
+
+ nsAutoCString id;
+ aNewCard->GetLocalId(id);
+
+ mdbOid rowId;
+ rowId.mOid_Scope = m_CardRowScopeToken;
+ rowId.mOid_Id = id.ToInteger(&rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ // Mork is being very naughty here. If the table does not have the oid, we
+ // should be able to reuse it. To be on the safe side, however, we're going
+ // to reference the store's reference count.
+ mdb_count rowCount = 1;
+ m_mdbStore->GetRowRefCount(m_mdbEnv, &rowId, &rowCount);
+ if (rowCount == 0)
+ {
+ // So apparently, the row can have a count of 0 yet still exist (probably
+ // meaning we haven't flushed it out of memory). In this case, we need to
+ // get the row and cut its cells.
+ rv = m_mdbStore->GetRow(m_mdbEnv, &rowId, getter_AddRefs(cardRow));
+ if (NS_SUCCEEDED(rv) && cardRow)
+ cardRow->CutAllColumns(m_mdbEnv);
+ else
+ rv = m_mdbStore->NewRowWithOid(m_mdbEnv, &rowId, getter_AddRefs(cardRow));
+ }
+ }
+
+ // If we don't have a cardRow yet, just get one with any ol' id.
+ if (!cardRow)
+ rv = GetNewRow(getter_AddRefs(cardRow));
+
+ if (NS_SUCCEEDED(rv) && cardRow)
+ {
+ AddAttributeColumnsToRow(aNewCard, cardRow);
+ AddRecordKeyColumnToRow(cardRow);
+
+ // we need to do this for dnd
+ uint32_t key = 0;
+ rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(rv))
+ aNewCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+
+ aNewCard->GetPropertyAsAUTF8String(kRowIDProperty, id);
+ aNewCard->SetLocalId(id);
+
+ nsCOMPtr<nsIAbDirectory> abDir = do_QueryReferent(m_dbDirectory);
+ if (abDir)
+ abDir->GetUuid(id);
+
+ aNewCard->SetDirectoryId(id);
+
+ nsresult merror = m_mdbPabTable->AddRow(m_mdbEnv, cardRow);
+ NS_ENSURE_SUCCESS(merror, NS_ERROR_FAILURE);
+ }
+ else
+ return rv;
+
+ // do notification
+ if (aNotify)
+ {
+ NotifyCardEntryChange(AB_NotifyInserted, aNewCard, aParent);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::CreateNewListCardAndAddToDB(nsIAbDirectory *aList, uint32_t listRowID, nsIAbCard *newCard, bool notify /* = FALSE */)
+{
+ if (!newCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow* pListRow = nullptr;
+ mdbOid listRowOid;
+ listRowOid.mOid_Scope = m_ListRowScopeToken;
+ listRowOid.mOid_Id = listRowID;
+ nsresult rv = m_mdbStore->GetRow(m_mdbEnv, &listRowOid, &pListRow);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (!pListRow)
+ return NS_OK;
+
+ nsCOMPtr<nsIMutableArray> addressList;
+ rv = aList->GetAddressLists(getter_AddRefs(addressList));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ uint32_t count;
+ addressList->GetLength(&count);
+
+ nsAutoString newEmail;
+ rv = newCard->GetPrimaryEmail(newEmail);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ uint32_t i;
+ for (i = 0; i < count; i++) {
+ nsCOMPtr<nsIAbCard> currentCard = do_QueryElementAt(addressList, i, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool equals;
+ rv = newCard->Equals(currentCard, &equals);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (equals) {
+ // card is already in list, bail out.
+ // this can happen when dropping a card on a mailing list from the directory that contains the mailing list
+ return NS_OK;
+ }
+
+ nsAutoString currentEmail;
+ rv = currentCard->GetPrimaryEmail(currentEmail);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (newEmail.Equals(currentEmail)) {
+ // card is already in list, bail out
+ // this can happen when dropping a card on a mailing list from another directory (not the one that contains the mailing list
+ // or if you have multiple cards on a directory, with the same primary email address.
+ return NS_OK;
+ }
+ }
+
+ // start from 1
+ uint32_t totalAddress = GetListAddressTotal(pListRow) + 1;
+ SetListAddressTotal(pListRow, totalAddress);
+ nsCOMPtr<nsIAbCard> pNewCard;
+ rv = AddListCardColumnsToRow(newCard, pListRow, totalAddress, getter_AddRefs(pNewCard), true /* aInMailingList */, aList, nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ addressList->AppendElement(newCard, false);
+
+ if (notify)
+ NotifyCardEntryChange(AB_NotifyInserted, newCard, aList);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddListCardColumnsToRow
+(nsIAbCard *aPCard, nsIMdbRow *aPListRow, uint32_t aPos, nsIAbCard** aPNewCard, bool aInMailingList, nsIAbDirectory *aParent, nsIAbDirectory *aRoot)
+{
+ if (!aPCard || !aPListRow || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ nsString email;
+ aPCard->GetPrimaryEmail(email);
+ if (!email.IsEmpty())
+ {
+ nsIMdbRow *pCardRow = nullptr;
+ // Please DO NOT change the 3rd param of GetRowFromAttribute() call to
+ // true (ie, case insensitive) without reading bugs #128535 and #121478.
+ err = GetRowFromAttribute(kPriEmailProperty, NS_ConvertUTF16toUTF8(email),
+ false /* retain case */, &pCardRow, nullptr);
+ bool cardWasAdded = false;
+ if (NS_FAILED(err) || !pCardRow)
+ {
+ //New Email, then add a new row with this email
+ err = GetNewRow(&pCardRow);
+
+ if (NS_SUCCEEDED(err) && pCardRow)
+ {
+ AddPrimaryEmail(pCardRow, NS_ConvertUTF16toUTF8(email).get());
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pCardRow);
+ // Create a key for this row as well.
+ if (NS_SUCCEEDED(err))
+ AddRecordKeyColumnToRow(pCardRow);
+ }
+
+ cardWasAdded = true;
+ }
+
+ NS_ENSURE_TRUE(pCardRow, NS_ERROR_NULL_POINTER);
+
+ nsString name;
+ aPCard->GetDisplayName(name);
+ if (!name.IsEmpty()) {
+ AddDisplayName(pCardRow, NS_ConvertUTF16toUTF8(name).get());
+ err = m_mdbPabTable->AddRow(m_mdbEnv, pCardRow);
+ }
+
+ nsCOMPtr<nsIAbCard> newCard;
+ CreateABCard(pCardRow, 0, getter_AddRefs(newCard));
+ NS_IF_ADDREF(*aPNewCard = newCard);
+
+ if (cardWasAdded) {
+ NotifyCardEntryChange(AB_NotifyInserted, newCard, aParent);
+ if (aRoot)
+ NotifyCardEntryChange(AB_NotifyInserted, newCard, aRoot);
+ }
+ else if (!aInMailingList) {
+ nsresult rv;
+ nsCOMPtr<nsIAddrDBListener> parentListener(do_QueryInterface(aParent, &rv));
+
+ // Ensure the parent is in the listener list (and hence wants to be notified)
+ if (NS_SUCCEEDED(rv) && m_ChangeListeners.Contains(parentListener))
+ parentListener->OnCardEntryChange(AB_NotifyInserted, aPCard, aParent);
+ }
+ else {
+ NotifyCardEntryChange(AB_NotifyPropertyChanged, aPCard, aParent);
+ }
+
+ //add a column with address row id to the list row
+ mdb_token listAddressColumnToken;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, aPos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ mdbOid outOid;
+
+ if (NS_SUCCEEDED(pCardRow->GetOid(m_mdbEnv, &outOid)))
+ {
+ //save address row ID to the list row
+ err = AddIntColumn(aPListRow, listAddressColumnToken, outOid.mOid_Id);
+ }
+ NS_RELEASE(pCardRow);
+
+ }
+
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::AddListAttributeColumnsToRow(nsIAbDirectory *list, nsIMdbRow *listRow, nsIAbDirectory *aParent)
+{
+ nsresult err = NS_OK;
+
+ if ((!list && !listRow) || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid, tableOid;
+ m_mdbPabTable->GetOid(m_mdbEnv, &tableOid);
+ listRow->GetOid(m_mdbEnv, &rowOid);
+
+ nsCOMPtr<nsIAbMDBDirectory> dblist(do_QueryInterface(list,&err));
+ if (NS_SUCCEEDED(err))
+ dblist->SetDbRowID(rowOid.mOid_Id);
+
+ // add the row to the singleton table.
+ if (NS_SUCCEEDED(err) && listRow)
+ {
+ nsString unicodeStr;
+
+ list->GetDirName(unicodeStr);
+ if (!unicodeStr.IsEmpty())
+ AddUnicodeToColumn(listRow, m_ListNameColumnToken, m_LowerListNameColumnToken, unicodeStr.get());
+
+ list->GetListNickName(unicodeStr);
+ AddListNickName(listRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ list->GetDescription(unicodeStr);
+ AddListDescription(listRow, NS_ConvertUTF16toUTF8(unicodeStr).get());
+
+ // XXX todo, this code has problems if you manually enter duplicate emails.
+ nsCOMPtr<nsIMutableArray> pAddressLists;
+ list->GetAddressLists(getter_AddRefs(pAddressLists));
+
+ uint32_t count;
+ pAddressLists->GetLength(&count);
+
+ nsAutoString email;
+ uint32_t i, total;
+ total = 0;
+ for (i = 0; i < count; i++)
+ {
+ nsCOMPtr<nsIAbCard> pCard(do_QueryElementAt(pAddressLists, i, &err));
+
+ if (NS_FAILED(err))
+ continue;
+
+ pCard->GetPrimaryEmail(email);
+ if (!email.IsEmpty())
+ total++;
+ }
+ SetListAddressTotal(listRow, total);
+
+ uint32_t pos;
+ for (i = 0; i < count; i++)
+ {
+ nsCOMPtr<nsIAbCard> pCard(do_QueryElementAt(pAddressLists, i, &err));
+
+ if (NS_FAILED(err))
+ continue;
+
+ bool listHasCard = false;
+ err = list->HasCard(pCard, &listHasCard);
+
+ // start from 1
+ pos = i + 1;
+ pCard->GetPrimaryEmail(email);
+ if (!email.IsEmpty())
+ {
+ nsCOMPtr<nsIAbCard> pNewCard;
+ err = AddListCardColumnsToRow(pCard, listRow, pos, getter_AddRefs(pNewCard), listHasCard, list, aParent);
+ if (pNewCard)
+ pAddressLists->ReplaceElementAt(pNewCard, i, false);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+uint32_t nsAddrDatabase::GetListAddressTotal(nsIMdbRow* listRow)
+{
+ uint32_t count = 0;
+ GetIntColumn(listRow, m_ListTotalColumnToken, &count, 0);
+ return count;
+}
+
+NS_IMETHODIMP nsAddrDatabase::SetListAddressTotal(nsIMdbRow* aListRow, uint32_t aTotal)
+{
+ return AddIntColumn(aListRow, m_ListTotalColumnToken, aTotal);
+}
+
+NS_IMETHODIMP nsAddrDatabase::FindRowByCard(nsIAbCard * aCard,nsIMdbRow **aRow)
+{
+ nsString primaryEmail;
+ aCard->GetPrimaryEmail(primaryEmail);
+ return GetRowForCharColumn(primaryEmail.get(), m_PriEmailColumnToken,
+ true, true, aRow, nullptr);
+}
+
+nsresult nsAddrDatabase::GetAddressRowByPos(nsIMdbRow* listRow, uint16_t pos, nsIMdbRow** cardRow)
+{
+ if (!m_mdbStore || !listRow || !cardRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdb_token listAddressColumnToken;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ nsAutoString tempString;
+ mdb_id rowID;
+ nsresult err = GetIntColumn(listRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+
+ return GetCardRowByRowID(rowID, cardRow);
+}
+
+NS_IMETHODIMP nsAddrDatabase::CreateMailListAndAddToDB(nsIAbDirectory *aNewList, bool aNotify /* = FALSE */, nsIAbDirectory *aParent)
+{
+ if (!aNewList || !m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsIMdbRow *listRow;
+ nsresult err = GetNewListRow(&listRow);
+
+ if (NS_SUCCEEDED(err) && listRow)
+ {
+ AddListAttributeColumnsToRow(aNewList, listRow, aParent);
+ AddRecordKeyColumnToRow(listRow);
+ nsresult merror = m_mdbPabTable->AddRow(m_mdbEnv, listRow);
+ NS_ENSURE_SUCCESS(merror, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIAbCard> listCard;
+ CreateABListCard(listRow, getter_AddRefs(listCard));
+ NotifyCardEntryChange(AB_NotifyInserted, listCard, aParent);
+
+ NS_RELEASE(listRow);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+void nsAddrDatabase::DeleteCardFromAllMailLists(mdb_id cardRowID)
+{
+ if (!m_mdbEnv)
+ return;
+
+ nsCOMPtr <nsIMdbTableRowCursor> rowCursor;
+ m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
+
+ if (rowCursor)
+ {
+ nsCOMPtr <nsIMdbRow> pListRow;
+ mdb_pos rowPos;
+ do
+ {
+ nsresult err = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(pListRow), &rowPos);
+
+ if (NS_SUCCEEDED(err) && pListRow)
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(pListRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ if (IsListRowScopeToken(rowOid.mOid_Scope))
+ DeleteCardFromListRow(pListRow, cardRowID);
+ }
+ }
+ } while (pListRow);
+ }
+}
+
+NS_IMETHODIMP nsAddrDatabase::DeleteCard(nsIAbCard *aCard, bool aNotify, nsIAbDirectory *aParent)
+{
+ if (!aCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ bool bIsMailList = false;
+ aCard->GetIsMailList(&bIsMailList);
+
+ // get the right row
+ nsIMdbRow* pCardRow = nullptr;
+ mdbOid rowOid;
+
+ rowOid.mOid_Scope = bIsMailList ? m_ListRowScopeToken : m_CardRowScopeToken;
+
+ err = aCard->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pCardRow);
+ NS_ENSURE_SUCCESS(err,err);
+ if (!pCardRow)
+ return NS_OK;
+
+ // Reset the directory id
+ aCard->SetDirectoryId(EmptyCString());
+
+ // Add the deleted card to the deletedcards table
+ nsCOMPtr <nsIMdbRow> cardRow;
+ AddRowToDeletedCardsTable(aCard, getter_AddRefs(cardRow));
+ err = DeleteRow(m_mdbPabTable, pCardRow);
+
+ //delete the person card from all mailing list
+ if (!bIsMailList)
+ DeleteCardFromAllMailLists(rowOid.mOid_Id);
+
+ if (NS_SUCCEEDED(err)) {
+ if (aNotify)
+ NotifyCardEntryChange(AB_NotifyDeleted, aCard, aParent);
+ }
+ else
+ DeleteRowFromDeletedCardsTable(cardRow);
+
+ NS_RELEASE(pCardRow);
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::DeleteCardFromListRow(nsIMdbRow* pListRow, mdb_id cardRowID)
+{
+ NS_ENSURE_ARG_POINTER(pListRow);
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ uint32_t totalAddress = GetListAddressTotal(pListRow);
+
+ uint32_t pos;
+ for (pos = 1; pos <= totalAddress; pos++)
+ {
+ mdb_token listAddressColumnToken;
+ mdb_id rowID;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ err = GetIntColumn(pListRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+
+ if (cardRowID == rowID)
+ {
+ if (pos == totalAddress)
+ err = pListRow->CutColumn(m_mdbEnv, listAddressColumnToken);
+ else
+ {
+ //replace the deleted one with the last one and delete the last one
+ mdb_id lastRowID;
+ mdb_token lastAddressColumnToken;
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, totalAddress);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &lastAddressColumnToken);
+
+ err = GetIntColumn(pListRow, lastAddressColumnToken, (uint32_t*)&lastRowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = AddIntColumn(pListRow, listAddressColumnToken, lastRowID);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = pListRow->CutColumn(m_mdbEnv, lastAddressColumnToken);
+ NS_ENSURE_SUCCESS(err, err);
+ }
+
+ // Reset total count after the card has been deleted.
+ SetListAddressTotal(pListRow, totalAddress-1);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::DeleteCardFromMailList(nsIAbDirectory *mailList, nsIAbCard *card, bool aNotify)
+{
+ if (!card || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ // get the right row
+ nsIMdbRow* pListRow = nullptr;
+ mdbOid listRowOid;
+ listRowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
+ NS_ENSURE_SUCCESS(err, err);
+
+ dbmailList->GetDbRowID((uint32_t*)&listRowOid.mOid_Id);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &listRowOid, &pListRow);
+ NS_ENSURE_SUCCESS(err,err);
+ if (!pListRow)
+ return NS_OK;
+
+ uint32_t cardRowID;
+
+ err = card->GetPropertyAsUint32(kRowIDProperty, &cardRowID);
+ if (NS_FAILED(err))
+ return NS_ERROR_NULL_POINTER;
+
+ err = DeleteCardFromListRow(pListRow, cardRowID);
+ if (NS_SUCCEEDED(err) && aNotify) {
+ NotifyCardEntryChange(AB_NotifyDeleted, card, mailList);
+ }
+ NS_RELEASE(pListRow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::SetCardValue(nsIAbCard *card, const char *name, const char16_t *value, bool notify)
+{
+ NS_ENSURE_ARG_POINTER(card);
+ NS_ENSURE_ARG_POINTER(name);
+ NS_ENSURE_ARG_POINTER(value);
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ // it might be that the caller always has a nsAbMDBCard
+ rv = card->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cardRow)
+ return NS_OK;
+
+ mdb_token token;
+ rv = m_mdbStore->StringToToken(m_mdbEnv, name, &token);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return AddCharStringColumn(cardRow, token, NS_ConvertUTF16toUTF8(value).get());
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardValue(nsIAbCard *card, const char *name, char16_t **value)
+{
+ if (!m_mdbStore || !card || !name || !value || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ // it might be that the caller always has a nsAbMDBCard
+ rv = card->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cardRow) {
+ *value = nullptr;
+ // this can happen when adding cards when editing a mailing list
+ return NS_OK;
+ }
+
+ mdb_token token;
+ m_mdbStore->StringToToken(m_mdbEnv, name, &token);
+
+ // XXX fix me
+ // avoid extra copying and allocations (did dmb already do this on the trunk?)
+ nsAutoString tempString;
+ rv = GetStringColumn(cardRow, token, tempString);
+ if (NS_FAILED(rv)) {
+ // not all cards are going this column
+ *value = nullptr;
+ return NS_OK;
+ }
+
+ *value = NS_strdup(tempString.get());
+ if (!*value)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetDeletedCardList(nsIArray **aResult)
+{
+ if (!m_mdbEnv || !aResult)
+ return NS_ERROR_NULL_POINTER;
+
+ *aResult = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> resultCardArray =
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // make sure the member is set properly
+ InitDeletedCardsTable(false);
+ if (m_mdbDeletedCardsTable)
+ {
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mdb_pos rowPos;
+ bool done = false;
+ nsCOMPtr<nsIMdbRow> currentRow;
+
+ m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
+ if (!rowCursor)
+ return NS_ERROR_FAILURE;
+ while (!done)
+ {
+ rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if (currentRow && NS_SUCCEEDED(rv))
+ {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(currentRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ nsCOMPtr<nsIAbCard> card;
+ rv = CreateCardFromDeletedCardsTable(currentRow, 0, getter_AddRefs(card));
+ if (NS_SUCCEEDED(rv)) {
+ resultCardArray->AppendElement(card, false);
+ }
+ }
+ }
+ else
+ done = true;
+ }
+ }
+
+ NS_IF_ADDREF(*aResult = resultCardArray);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetDeletedCardCount(uint32_t *aCount)
+{
+ // initialize count first
+ *aCount = 0;
+ InitDeletedCardsTable(false);
+ if (m_mdbDeletedCardsTable)
+ return m_mdbDeletedCardsTable->GetCount(m_mdbEnv, aCount);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::PurgeDeletedCardTable()
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ if (m_mdbDeletedCardsTable) {
+ mdb_count cardCount=0;
+ // if not too many cards let it be
+ m_mdbDeletedCardsTable->GetCount(m_mdbEnv, &cardCount);
+ if(cardCount < PURGE_CUTOFF_COUNT)
+ return NS_OK;
+ uint32_t purgeTimeInSec;
+ PRTime2Seconds(PR_Now(), &purgeTimeInSec);
+ purgeTimeInSec -= (182*24*60*60); // six months in seconds
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ nsresult rv = m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor));
+ while(NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ mdb_pos rowPos;
+ rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if(currentRow) {
+ uint32_t deletedTimeStamp = 0;
+ GetIntColumn(currentRow, m_LastModDateColumnToken, &deletedTimeStamp, 0);
+ // if record was deleted more than six months earlier, purge it
+ if(deletedTimeStamp && (deletedTimeStamp < purgeTimeInSec)) {
+ if(NS_SUCCEEDED(currentRow->CutAllColumns(m_mdbEnv)))
+ m_mdbDeletedCardsTable->CutRow(m_mdbEnv, currentRow);
+ }
+ else
+ // since the ordering in Mork is maintained and thus
+ // the cards added later appear on the top when retrieved
+ break;
+ }
+ else
+ break; // no more row
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::EditCard(nsIAbCard *aCard, bool aNotify, nsIAbDirectory *aParent)
+{
+ // XXX make sure this isn't getting called when we're just editing one or two well known fields
+ if (!aCard || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ uint32_t nowInSeconds;
+ PRTime now = PR_Now();
+ PRTime2Seconds(now, &nowInSeconds);
+ aCard->SetPropertyAsUint32(kLastModifiedDateProperty, nowInSeconds);
+ err = aCard->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (!cardRow)
+ return NS_OK;
+
+ err = AddAttributeColumnsToRow(aCard, cardRow);
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (aNotify)
+ NotifyCardEntryChange(AB_NotifyPropertyChanged, aCard, aParent);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::ContainsCard(nsIAbCard *card, bool *hasCard)
+{
+ if (!card || !m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ mdb_bool hasOid;
+ mdbOid rowOid;
+ bool bIsMailList;
+
+ card->GetIsMailList(&bIsMailList);
+
+ if (bIsMailList)
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+ else
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+
+ err = card->GetPropertyAsUint32(kRowIDProperty, &rowOid.mOid_Id);
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = m_mdbPabTable->HasOid(m_mdbEnv, &rowOid, &hasOid);
+ if (NS_SUCCEEDED(err))
+ {
+ *hasCard = hasOid;
+ }
+
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::DeleteMailList(nsIAbDirectory *aMailList,
+ nsIAbDirectory *aParent)
+{
+ if (!aMailList || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ // get the row
+ nsCOMPtr<nsIMdbRow> pListRow;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(aMailList, &err));
+ NS_ENSURE_SUCCESS(err, err);
+ dbmailList->GetDbRowID((uint32_t*)&rowOid.mOid_Id);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, getter_AddRefs(pListRow));
+ NS_ENSURE_SUCCESS(err,err);
+
+ if (!pListRow)
+ return NS_OK;
+
+ nsCOMPtr<nsIAbCard> card;
+ err = CreateABListCard(pListRow, getter_AddRefs(card));
+ NS_ENSURE_SUCCESS(err, err);
+
+ err = DeleteRow(m_mdbPabTable, pListRow);
+
+ if (NS_SUCCEEDED(err) && aParent)
+ NotifyCardEntryChange(AB_NotifyDeleted, card, aParent);
+
+ return err;
+}
+
+NS_IMETHODIMP nsAddrDatabase::EditMailList(nsIAbDirectory *mailList, nsIAbCard *listCard, bool notify)
+{
+ if (!mailList || !m_mdbPabTable || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+
+ nsIMdbRow* pListRow = nullptr;
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList, &err));
+ NS_ENSURE_SUCCESS(err, err);
+ dbmailList->GetDbRowID((uint32_t*)&rowOid.mOid_Id);
+
+ err = m_mdbStore->GetRow(m_mdbEnv, &rowOid, &pListRow);
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (!pListRow)
+ return NS_OK;
+
+ err = AddListAttributeColumnsToRow(mailList, pListRow, mailList);
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (notify)
+ {
+ NotifyListEntryChange(AB_NotifyPropertyChanged, mailList);
+
+ if (listCard)
+ {
+ NotifyCardEntryChange(AB_NotifyPropertyChanged, listCard, mailList);
+ }
+ }
+
+ NS_RELEASE(pListRow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::ContainsMailList(nsIAbDirectory *mailList, bool *hasList)
+{
+ if (!mailList || !m_mdbPabTable || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = NS_OK;
+ mdb_bool hasOid;
+ mdbOid rowOid;
+
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList(do_QueryInterface(mailList,&err));
+ NS_ENSURE_SUCCESS(err, err);
+ dbmailList->GetDbRowID((uint32_t*)&rowOid.mOid_Id);
+
+ err = m_mdbPabTable->HasOid(m_mdbEnv, &rowOid, &hasOid);
+ if (NS_SUCCEEDED(err))
+ *hasList = hasOid;
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetNewRow(nsIMdbRow * *newRow)
+{
+ if (!m_mdbStore || !newRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_mdbStore->NewRow(m_mdbEnv, m_CardRowScopeToken, newRow);
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetNewListRow(nsIMdbRow * *newRow)
+{
+ if (!m_mdbStore || !newRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ return m_mdbStore->NewRow(m_mdbEnv, m_ListRowScopeToken, newRow);
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddCardRowToDB(nsIMdbRow *newRow)
+{
+ if (m_mdbPabTable && m_mdbEnv)
+ {
+ if (NS_SUCCEEDED(m_mdbPabTable->AddRow(m_mdbEnv, newRow)))
+ {
+ AddRecordKeyColumnToRow(newRow);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddLdifListMember(nsIMdbRow* listRow, const char* value)
+{
+ if (!m_mdbStore || !listRow || !value || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ uint32_t total = GetListAddressTotal(listRow);
+ //add member
+ nsAutoCString valueString(value);
+ nsAutoCString email;
+ int32_t emailPos = valueString.Find("mail=");
+ emailPos += strlen("mail=");
+ email = Substring(valueString, emailPos);
+ nsCOMPtr <nsIMdbRow> cardRow;
+ // Please DO NOT change the 3rd param of GetRowFromAttribute() call to
+ // true (ie, case insensitive) without reading bugs #128535 and #121478.
+ nsresult rv = GetRowFromAttribute(kPriEmailProperty, email, false /* retain case */,
+ getter_AddRefs(cardRow), nullptr);
+ if (NS_SUCCEEDED(rv) && cardRow)
+ {
+ mdbOid outOid;
+ mdb_id rowID = 0;
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ // start from 1
+ total += 1;
+ mdb_token listAddressColumnToken;
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, total);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ rv = AddIntColumn(listRow, listAddressColumnToken, rowID);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ SetListAddressTotal(listRow, total);
+ }
+ return NS_OK;
+}
+
+
+void nsAddrDatabase::GetCharStringYarn(char* str, struct mdbYarn* strYarn)
+{
+ strYarn->mYarn_Grow = nullptr;
+ strYarn->mYarn_Buf = str;
+ strYarn->mYarn_Size = PL_strlen((const char *) strYarn->mYarn_Buf) + 1;
+ strYarn->mYarn_Fill = strYarn->mYarn_Size - 1;
+ strYarn->mYarn_Form = 0;
+}
+
+void nsAddrDatabase::GetStringYarn(const nsAString & aStr, struct mdbYarn* strYarn)
+{
+ strYarn->mYarn_Buf = ToNewUTF8String(aStr);
+ strYarn->mYarn_Size = PL_strlen((const char *) strYarn->mYarn_Buf) + 1;
+ strYarn->mYarn_Fill = strYarn->mYarn_Size - 1;
+ strYarn->mYarn_Form = 0;
+}
+
+void nsAddrDatabase::GetIntYarn(uint32_t nValue, struct mdbYarn* intYarn)
+{
+ intYarn->mYarn_Fill = intYarn->mYarn_Size;
+ intYarn->mYarn_Form = 0;
+ intYarn->mYarn_Grow = nullptr;
+
+ PR_snprintf((char*)intYarn->mYarn_Buf, intYarn->mYarn_Size, "%lx", nValue);
+ intYarn->mYarn_Fill = PL_strlen((const char *) intYarn->mYarn_Buf);
+}
+
+nsresult nsAddrDatabase::AddCharStringColumn(nsIMdbRow* cardRow, mdb_column inColumn, const char* str)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+
+ GetCharStringYarn((char *) str, &yarn);
+ nsresult err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::AddStringColumn(nsIMdbRow* aCardRow, mdb_column aInColumn, const nsAString & aStr)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+
+ GetStringYarn(aStr, &yarn);
+ nsresult err = aCardRow->AddColumn(m_mdbEnv, aInColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::AddIntColumn(nsIMdbRow* cardRow, mdb_column inColumn, uint32_t nValue)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+ char yarnBuf[100];
+
+ yarn.mYarn_Buf = (void *) yarnBuf;
+ yarn.mYarn_Size = sizeof(yarnBuf);
+ GetIntYarn(nValue, &yarn);
+ nsresult err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::AddBoolColumn(nsIMdbRow* cardRow, mdb_column inColumn, bool bValue)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ struct mdbYarn yarn;
+ char yarnBuf[100];
+
+ yarn.mYarn_Buf = (void *) yarnBuf;
+ yarn.mYarn_Size = sizeof(yarnBuf);
+
+ GetIntYarn(bValue ? 1 : 0, &yarn);
+
+ nsresult err = cardRow->AddColumn(m_mdbEnv, inColumn, &yarn);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::GetStringColumn(nsIMdbRow *cardRow, mdb_token outToken, nsString& str)
+{
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell *cardCell;
+
+ if (cardRow && m_mdbEnv)
+ {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell)
+ {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ NS_ConvertUTF8toUTF16 uniStr((const char*) yarn.mYarn_Buf, yarn.mYarn_Fill);
+ if (!uniStr.IsEmpty())
+ str.Assign(uniStr);
+ else
+ err = NS_ERROR_FAILURE;
+ cardCell->Release(); // always release ref
+ }
+ else
+ err = NS_ERROR_FAILURE;
+ }
+ return err;
+}
+
+void nsAddrDatabase::YarnToUInt32(struct mdbYarn *yarn, uint32_t *pResult)
+{
+ uint8_t numChars = std::min<mdb_fill>(8, yarn->mYarn_Fill);
+ *pResult = MsgUnhex((char *) yarn->mYarn_Buf, numChars);
+}
+
+nsresult nsAddrDatabase::GetIntColumn
+(nsIMdbRow *cardRow, mdb_token outToken, uint32_t* pValue, uint32_t defaultValue)
+{
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell *cardCell;
+
+ if (pValue)
+ *pValue = defaultValue;
+ if (cardRow && m_mdbEnv)
+ {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell)
+ {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ YarnToUInt32(&yarn, pValue);
+ cardCell->Release();
+ }
+ else
+ err = NS_ERROR_FAILURE;
+ }
+ return err;
+}
+
+nsresult nsAddrDatabase::GetBoolColumn(nsIMdbRow *cardRow, mdb_token outToken, bool* pValue)
+{
+ NS_ENSURE_ARG_POINTER(pValue);
+
+ nsresult err = NS_ERROR_NULL_POINTER;
+ nsIMdbCell *cardCell;
+ uint32_t nValue = 0;
+
+ if (cardRow && m_mdbEnv)
+ {
+ err = cardRow->GetCell(m_mdbEnv, outToken, &cardCell);
+ if (NS_SUCCEEDED(err) && cardCell)
+ {
+ struct mdbYarn yarn;
+ cardCell->AliasYarn(m_mdbEnv, &yarn);
+ YarnToUInt32(&yarn, &nValue);
+ cardCell->Release();
+ }
+ else
+ err = NS_ERROR_FAILURE;
+ }
+
+ *pValue = nValue ? true : false;
+ return err;
+}
+
+/* value is UTF8 string */
+NS_IMETHODIMP nsAddrDatabase::AddPrimaryEmail(nsIMdbRow *aRow, const char *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ nsresult rv = AddCharStringColumn(aRow, m_PriEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(aRow, m_LowerPriEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/* value is UTF8 string */
+NS_IMETHODIMP nsAddrDatabase::Add2ndEmail(nsIMdbRow *aRow, const char *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ nsresult rv = AddCharStringColumn(aRow, m_2ndEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(aRow, m_Lower2ndEmailColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/* value is UTF8 string */
+NS_IMETHODIMP nsAddrDatabase::AddListName(nsIMdbRow *aRow, const char *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ nsresult rv = AddCharStringColumn(aRow, m_ListNameColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = AddLowercaseColumn(aRow, m_LowerListNameColumnToken, aValue);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return rv;
+}
+
+/*
+columnValue is UTF8 string, need to convert back to lowercase unicode then
+back to UTF8 string
+*/
+nsresult nsAddrDatabase::AddLowercaseColumn
+(nsIMdbRow * row, mdb_token columnToken, const char* columnValue)
+{
+ nsresult rv = NS_OK;
+ if (columnValue)
+ {
+ NS_ConvertUTF8toUTF16 newUnicodeString(columnValue);
+ ToLowerCase(newUnicodeString);
+ rv = AddCharStringColumn(row, columnToken, NS_ConvertUTF16toUTF8(newUnicodeString).get());
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::InitCardFromRow(nsIAbCard *newCard, nsIMdbRow* cardRow)
+{
+ nsresult rv = NS_OK;
+ if (!newCard || !cardRow || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsIMdbRowCellCursor> cursor;
+ nsCOMPtr<nsIMdbCell> cell;
+
+ rv = cardRow->GetRowCellCursor(m_mdbEnv, -1, getter_AddRefs(cursor));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mdb_column columnNumber;
+ char columnName[100];
+ struct mdbYarn colYarn = {columnName, 0, sizeof(columnName), 0, 0, nullptr};
+ struct mdbYarn cellYarn;
+
+ do
+ {
+ rv = cursor->NextCell(m_mdbEnv, getter_AddRefs(cell), &columnNumber, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!cell)
+ break;
+
+ // Get the value of the cell
+ cell->AliasYarn(m_mdbEnv, &cellYarn);
+ NS_ConvertUTF8toUTF16 value(static_cast<const char*>(cellYarn.mYarn_Buf),
+ cellYarn.mYarn_Fill);
+
+ if (!value.IsEmpty())
+ {
+ // Get the column of the cell
+ // Mork makes this so hard...
+ rv = m_mdbStore->TokenToString(m_mdbEnv, columnNumber, &colYarn);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char *name = PL_strndup(static_cast<char *>(colYarn.mYarn_Buf),
+ colYarn.mYarn_Fill);
+ newCard->SetPropertyAsAString(name, value);
+ PL_strfree(name);
+ }
+ } while (true);
+
+ uint32_t key = 0;
+ rv = GetIntColumn(cardRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(rv))
+ newCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+
+ return NS_OK;
+}
+
+nsresult nsAddrDatabase::GetListCardFromDB(nsIAbCard *listCard, nsIMdbRow* listRow)
+{
+ nsresult err = NS_OK;
+ if (!listCard || !listRow)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoString tempString;
+
+ err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ listCard->SetDisplayName(tempString);
+ listCard->SetLastName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ listCard->SetPropertyAsAString(kNicknameProperty, tempString);
+ }
+ err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ listCard->SetPropertyAsAString(kNotesProperty, tempString);
+ }
+ uint32_t key = 0;
+ err = GetIntColumn(listRow, m_RecordKeyColumnToken, &key, 0);
+ if (NS_SUCCEEDED(err))
+ listCard->SetPropertyAsUint32(kRecordKeyColumn, key);
+ return err;
+}
+
+nsresult nsAddrDatabase::GetListFromDB(nsIAbDirectory *newList, nsIMdbRow* listRow)
+{
+ nsresult err = NS_OK;
+ if (!newList || !listRow || !m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoString tempString;
+
+ err = GetStringColumn(listRow, m_ListNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ newList->SetDirName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListNickNameColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ newList->SetListNickName(tempString);
+ }
+ err = GetStringColumn(listRow, m_ListDescriptionColumnToken, tempString);
+ if (NS_SUCCEEDED(err) && !tempString.IsEmpty())
+ {
+ newList->SetDescription(tempString);
+ }
+
+ nsCOMPtr<nsIAbMDBDirectory> dbnewList(do_QueryInterface(newList, &err));
+ NS_ENSURE_SUCCESS(err, err);
+
+ uint32_t totalAddress = GetListAddressTotal(listRow);
+ uint32_t pos;
+ for (pos = 1; pos <= totalAddress; ++pos)
+ {
+ mdb_token listAddressColumnToken;
+ mdb_id rowID;
+
+ char columnStr[COLUMN_STR_MAX];
+ PR_snprintf(columnStr, COLUMN_STR_MAX, kMailListAddressFormat, pos);
+ m_mdbStore->StringToToken(m_mdbEnv, columnStr, &listAddressColumnToken);
+
+ nsCOMPtr <nsIMdbRow> cardRow;
+ err = GetIntColumn(listRow, listAddressColumnToken, (uint32_t*)&rowID, 0);
+ NS_ENSURE_SUCCESS(err, err);
+ err = GetCardRowByRowID(rowID, getter_AddRefs(cardRow));
+ NS_ENSURE_SUCCESS(err, err);
+
+ if (cardRow)
+ {
+ nsCOMPtr<nsIAbCard> card;
+ err = CreateABCard(cardRow, 0, getter_AddRefs(card));
+
+ if(NS_SUCCEEDED(err))
+ dbnewList->AddAddressToList(card);
+ }
+// NS_IF_ADDREF(card);
+ }
+
+ return err;
+}
+
+class nsAddrDBEnumerator : public nsISimpleEnumerator, public nsIAddrDBListener
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+ NS_DECL_NSIADDRDBLISTENER
+ // nsAddrDBEnumerator methods:
+
+ nsAddrDBEnumerator(nsAddrDatabase* aDb);
+ void Clear();
+protected:
+ virtual ~nsAddrDBEnumerator();
+ RefPtr<nsAddrDatabase> mDb;
+ nsIMdbTable *mDbTable;
+ nsCOMPtr<nsIMdbTableRowCursor> mRowCursor;
+ nsCOMPtr<nsIMdbRow> mCurrentRow;
+ mdb_pos mRowPos;
+};
+
+nsAddrDBEnumerator::nsAddrDBEnumerator(nsAddrDatabase* aDb)
+ : mDb(aDb),
+ mDbTable(aDb->GetPabTable()),
+ mRowPos(-1)
+{
+ if (aDb)
+ aDb->AddListener(this);
+}
+
+nsAddrDBEnumerator::~nsAddrDBEnumerator()
+{
+ Clear();
+}
+
+void nsAddrDBEnumerator::Clear()
+{
+ mRowCursor = nullptr;
+ mCurrentRow = nullptr;
+ mDbTable = nullptr;
+ if (mDb)
+ mDb->RemoveListener(this);
+}
+
+NS_IMPL_ISUPPORTS(nsAddrDBEnumerator, nsISimpleEnumerator, nsIAddrDBListener)
+
+NS_IMETHODIMP
+nsAddrDBEnumerator::HasMoreElements(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mDbTable->GetTableRowCursor(mDb->GetEnv(), mRowPos,
+ getter_AddRefs(rowCursor));
+ NS_ENSURE_TRUE(rowCursor, NS_ERROR_FAILURE);
+
+ mdbOid rowOid;
+ rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
+ while (rowOid.mOid_Id != (mdb_id)-1)
+ {
+ if (mDb->IsListRowScopeToken(rowOid.mOid_Scope) ||
+ mDb->IsCardRowScopeToken(rowOid.mOid_Scope))
+ {
+ *aResult = true;
+
+ return NS_OK;
+ }
+
+ if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope))
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ rowCursor->NextRowOid(mDb->GetEnv(), &rowOid, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAddrDBEnumerator::GetNext(nsISupports **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mRowCursor)
+ {
+ mDbTable->GetTableRowCursor(mDb->GetEnv(), -1,
+ getter_AddRefs(mRowCursor));
+ NS_ENSURE_TRUE(mRowCursor, NS_ERROR_FAILURE);
+ }
+
+ nsCOMPtr<nsIAbCard> resultCard;
+ mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow), &mRowPos);
+ while (mCurrentRow)
+ {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(mCurrentRow->GetOid(mDb->GetEnv(), &rowOid)))
+ {
+ nsresult rv;
+ if (mDb->IsListRowScopeToken(rowOid.mOid_Scope))
+ {
+ rv = mDb->CreateABListCard(mCurrentRow,
+ getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (mDb->IsCardRowScopeToken(rowOid.mOid_Scope))
+ {
+ rv = mDb->CreateABCard(mCurrentRow, 0,
+ getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else if (!mDb->IsDataRowScopeToken(rowOid.mOid_Scope))
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (resultCard)
+ {
+ return CallQueryInterface(resultCard, aResult);
+ }
+ }
+
+ mRowCursor->NextRow(mDb->GetEnv(), getter_AddRefs(mCurrentRow),
+ &mRowPos);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsAddrDBEnumerator::OnCardAttribChange(uint32_t abCode)
+{
+ return NS_OK;
+}
+
+/* void onCardEntryChange (in unsigned long aAbCode, in nsIAbCard aCard, in nsIAbDirectory aParent); */
+NS_IMETHODIMP nsAddrDBEnumerator::OnCardEntryChange(uint32_t aAbCode, nsIAbCard *aCard, nsIAbDirectory *aParent)
+{
+ return NS_OK;
+}
+
+/* void onListEntryChange (in unsigned long abCode, in nsIAbDirectory list); */
+NS_IMETHODIMP nsAddrDBEnumerator::OnListEntryChange(uint32_t abCode, nsIAbDirectory *list)
+{
+ return NS_OK;
+}
+
+/* void onAnnouncerGoingAway (); */
+NS_IMETHODIMP nsAddrDBEnumerator::OnAnnouncerGoingAway()
+{
+ Clear();
+ return NS_OK;
+}
+
+class nsListAddressEnumerator final : public nsISimpleEnumerator
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // nsISimpleEnumerator methods:
+ NS_DECL_NSISIMPLEENUMERATOR
+
+ // nsListAddressEnumerator methods:
+
+ nsListAddressEnumerator(nsAddrDatabase* aDb, mdb_id aRowID);
+
+protected:
+ ~nsListAddressEnumerator() {}
+ RefPtr<nsAddrDatabase> mDb;
+ nsIMdbTable *mDbTable;
+ nsCOMPtr<nsIMdbRow> mListRow;
+ mdb_id mListRowID;
+ uint32_t mAddressTotal;
+ uint16_t mAddressPos;
+};
+
+nsListAddressEnumerator::nsListAddressEnumerator(nsAddrDatabase* aDb,
+ mdb_id aRowID)
+ : mDb(aDb),
+ mDbTable(aDb->GetPabTable()),
+ mListRowID(aRowID),
+ mAddressPos(0)
+{
+ mDb->GetListRowByRowID(mListRowID, getter_AddRefs(mListRow));
+ mAddressTotal = aDb->GetListAddressTotal(mListRow);
+}
+
+NS_IMPL_ISUPPORTS(nsListAddressEnumerator, nsISimpleEnumerator)
+
+NS_IMETHODIMP
+nsListAddressEnumerator::HasMoreElements(bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = false;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // In some cases it is possible that GetAddressRowByPos returns success,
+ // but currentRow is null. This is typically due to the fact that a card
+ // has been deleted from the parent and not the list. Whilst we have fixed
+ // that there are still a few dbs around there that we need to support
+ // correctly. Therefore, whilst processing lists ensure that we don't return
+ // false if the only thing stopping us is a blank row, just skip it and try
+ // the next one.
+ while (mAddressPos < mAddressTotal)
+ {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos + 1,
+ getter_AddRefs(currentRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (currentRow)
+ {
+ *aResult = true;
+ break;
+ }
+
+ ++mAddressPos;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsListAddressEnumerator::GetNext(nsISupports **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aResult = nullptr;
+
+ if (!mDbTable || !mDb->GetEnv())
+ {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (++mAddressPos <= mAddressTotal)
+ {
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos,
+ getter_AddRefs(currentRow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIAbCard> resultCard;
+ rv = mDb->CreateABCard(currentRow, mListRowID,
+ getter_AddRefs(resultCard));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(resultCard, aResult);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsAddrDatabase::EnumerateCards(nsIAbDirectory *directory, nsISimpleEnumerator **result)
+{
+ nsAddrDBEnumerator* e = new nsAddrDBEnumerator(this);
+ m_dbDirectory = do_GetWeakReference(directory);
+ if (!e)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(e);
+ *result = e;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetMailingListsFromDB(nsIAbDirectory *parentDir)
+{
+ nsCOMPtr<nsIAbDirectory> resultList;
+ nsIMdbTableRowCursor* rowCursor = nullptr;
+ nsCOMPtr<nsIMdbRow> currentRow;
+ mdb_pos rowPos;
+ bool done = false;
+
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ m_dbDirectory = do_GetWeakReference(parentDir);
+
+ nsIMdbTable* dbTable = GetPabTable();
+
+ if (!dbTable)
+ return NS_ERROR_FAILURE;
+
+ dbTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor);
+ if (!rowCursor)
+ return NS_ERROR_FAILURE;
+
+ while (!done)
+ {
+ nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if (currentRow && NS_SUCCEEDED(rv))
+ {
+ mdbOid rowOid;
+
+ if (NS_SUCCEEDED(currentRow->GetOid(m_mdbEnv, &rowOid)))
+ {
+ if (IsListRowScopeToken(rowOid.mOid_Scope))
+ rv = CreateABList(currentRow, getter_AddRefs(resultList));
+ }
+ }
+ else
+ done = true;
+ }
+ NS_IF_RELEASE(rowCursor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::EnumerateListAddresses(nsIAbDirectory *directory, nsISimpleEnumerator **result)
+{
+ nsresult rv = NS_OK;
+ mdb_id rowID;
+
+ nsCOMPtr<nsIAbMDBDirectory> dbdirectory(do_QueryInterface(directory,&rv));
+
+ if(NS_SUCCEEDED(rv))
+ {
+ dbdirectory->GetDbRowID((uint32_t*)&rowID);
+
+ nsListAddressEnumerator* e = new nsListAddressEnumerator(this, rowID);
+ m_dbDirectory = do_GetWeakReference(directory);
+ if (!e)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(e);
+ *result = e;
+ }
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateCardFromDeletedCardsTable(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
+{
+ if (!cardRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ if(NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIAbCard> personCard;
+ personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ InitCardFromRow(personCard, cardRow);
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+
+ NS_IF_ADDREF(*result = personCard);
+ }
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
+{
+ if (!cardRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(cardRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ if(NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIAbCard> personCard;
+ personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ InitCardFromRow(personCard, cardRow);
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+
+ nsAutoCString id;
+ id.AppendInt(rowID);
+ personCard->SetLocalId(id);
+
+ nsCOMPtr<nsIAbDirectory> abDir(do_QueryReferent(m_dbDirectory));
+ if (abDir)
+ abDir->GetUuid(id);
+
+ personCard->SetDirectoryId(id);
+
+ NS_IF_ADDREF(*result = personCard);
+ }
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::CreateABCard(nsIMdbRow* cardRow, mdb_id listRowID, nsIAbCard **result)
+{
+ return CreateCard(cardRow, listRowID, result);
+}
+
+/* create a card for mailing list in the address book */
+nsresult nsAddrDatabase::CreateABListCard(nsIMdbRow* listRow, nsIAbCard **result)
+{
+ if (!listRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv = NS_OK;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(listRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ char* listURI = nullptr;
+
+ nsAutoString fileName;
+ rv = m_dbName->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ listURI = PR_smprintf("%s%s/MailList%ld", kMDBDirectoryRoot, NS_ConvertUTF16toUTF8(fileName).get(), rowID);
+
+ nsCOMPtr<nsIAbCard> personCard;
+ nsCOMPtr<nsIAbMDBDirectory> dbm_dbDirectory(do_QueryReferent(m_dbDirectory,
+ &rv));
+ if (NS_SUCCEEDED(rv) && dbm_dbDirectory)
+ {
+ personCard = do_CreateInstance(NS_ABMDBCARD_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (personCard)
+ {
+ GetListCardFromDB(personCard, listRow);
+
+ personCard->SetPropertyAsUint32(kRowIDProperty, rowID);
+ personCard->SetIsMailList(true);
+ personCard->SetMailListURI(listURI);
+
+ nsAutoCString id;
+ id.AppendInt(rowID);
+ personCard->SetLocalId(id);
+
+ nsCOMPtr<nsIAbDirectory> abDir(do_QueryReferent(m_dbDirectory));
+ if (abDir)
+ abDir->GetUuid(id);
+ personCard->SetDirectoryId(id);
+ }
+
+ NS_IF_ADDREF(*result = personCard);
+ }
+ if (listURI)
+ PR_smprintf_free(listURI);
+
+ return rv;
+}
+
+/* create a sub directory for mailing list in the address book left pane */
+nsresult nsAddrDatabase::CreateABList(nsIMdbRow* listRow, nsIAbDirectory **result)
+{
+ nsresult rv = NS_OK;
+
+ if (!listRow || !m_mdbEnv || !result)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid outOid;
+ mdb_id rowID = 0;
+
+ if (NS_SUCCEEDED(listRow->GetOid(m_mdbEnv, &outOid)))
+ rowID = outOid.mOid_Id;
+
+ char* listURI = nullptr;
+
+ nsAutoString fileName;
+ m_dbName->GetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ listURI = PR_smprintf("%s%s/MailList%ld", kMDBDirectoryRoot, NS_ConvertUTF16toUTF8(fileName).get(), rowID);
+
+ nsCOMPtr<nsIAbDirectory> mailList;
+ nsCOMPtr<nsIAbMDBDirectory> dbm_dbDirectory(do_QueryReferent(m_dbDirectory,
+ &rv));
+ if (NS_SUCCEEDED(rv) && dbm_dbDirectory)
+ {
+ rv = dbm_dbDirectory->AddDirectory(listURI, getter_AddRefs(mailList));
+
+ nsCOMPtr<nsIAbMDBDirectory> dbmailList (do_QueryInterface(mailList, &rv));
+
+ if (mailList)
+ {
+ // if we are using turbo, and we "exit" and restart with the same profile
+ // the current mailing list will still be in memory, so when we do
+ // GetResource() and QI, we'll get it again.
+ // in that scenario, the mailList that we pass in will already be
+ // be a mailing list, with a valid row and all the entries
+ // in that scenario, we can skip GetListFromDB(), which would have
+ // have added all the cards to the list again.
+ // see bug #134743
+ mdb_id existingID;
+ dbmailList->GetDbRowID(&existingID);
+ if (existingID != rowID) {
+ // Ensure IsMailList is set up first.
+ mailList->SetIsMailList(true);
+ GetListFromDB(mailList, listRow);
+ dbmailList->SetDbRowID(rowID);
+ }
+
+ dbm_dbDirectory->AddMailListToDirectory(mailList);
+ NS_IF_ADDREF(*result = mailList);
+ }
+ }
+
+ if (listURI)
+ PR_smprintf_free(listURI);
+
+ return rv;
+}
+
+nsresult nsAddrDatabase::GetCardRowByRowID(mdb_id rowID, nsIMdbRow **dbRow)
+{
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_CardRowScopeToken;
+ rowOid.mOid_Id = rowID;
+
+ return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
+}
+
+nsresult nsAddrDatabase::GetListRowByRowID(mdb_id rowID, nsIMdbRow **dbRow)
+{
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdbOid rowOid;
+ rowOid.mOid_Scope = m_ListRowScopeToken;
+ rowOid.mOid_Id = rowID;
+
+ return m_mdbStore->GetRow(m_mdbEnv, &rowOid, dbRow);
+}
+
+nsresult nsAddrDatabase::GetRowFromAttribute(const char *aName,
+ const nsACString &aUTF8Value,
+ bool aCaseInsensitive,
+ nsIMdbRow **aCardRow,
+ mdb_pos *aRowPos)
+{
+ NS_ENSURE_ARG_POINTER(aName);
+ NS_ENSURE_ARG_POINTER(aCardRow);
+ if (!m_mdbStore || !m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ mdb_token token;
+ m_mdbStore->StringToToken(m_mdbEnv, aName, &token);
+ NS_ConvertUTF8toUTF16 newUnicodeString(aUTF8Value);
+
+ return GetRowForCharColumn(newUnicodeString.get(), token, true,
+ aCaseInsensitive, aCardRow, aRowPos);
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardFromAttribute(nsIAbDirectory *aDirectory,
+ const char *aName,
+ const nsACString &aUTF8Value,
+ bool aCaseInsensitive,
+ nsIAbCard **aCardResult)
+{
+ NS_ENSURE_ARG_POINTER(aCardResult);
+
+ m_dbDirectory = do_GetWeakReference(aDirectory);
+ nsCOMPtr<nsIMdbRow> cardRow;
+ if (NS_SUCCEEDED(GetRowFromAttribute(aName, aUTF8Value, aCaseInsensitive,
+ getter_AddRefs(cardRow), nullptr)) && cardRow)
+ return CreateABCard(cardRow, 0, aCardResult);
+
+ *aCardResult = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardsFromAttribute(nsIAbDirectory *aDirectory,
+ const char *aName,
+ const nsACString & aUTF8Value,
+ bool aCaseInsensitive,
+ nsISimpleEnumerator **cards)
+{
+ NS_ENSURE_ARG_POINTER(cards);
+
+ m_dbDirectory = do_GetWeakReference(aDirectory);
+ nsCOMPtr<nsIMdbRow> row;
+ bool done = false;
+ nsCOMArray<nsIAbCard> list;
+ nsCOMPtr<nsIAbCard> card;
+ mdb_pos rowPos = -1;
+
+ do
+ {
+ if (NS_SUCCEEDED(GetRowFromAttribute(aName, aUTF8Value, aCaseInsensitive,
+ getter_AddRefs(row), &rowPos)) && row)
+ {
+ if (NS_FAILED(CreateABCard(row, 0, getter_AddRefs(card))))
+ continue;
+ list.AppendObject(card);
+ }
+ else
+ done = true;
+ } while (!done);
+
+ return NS_NewArrayEnumerator(cards, list);
+}
+
+NS_IMETHODIMP nsAddrDatabase::AddListDirNode(nsIMdbRow * listRow)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoString parentURI;
+ rv = m_dbName->GetLeafName(parentURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ parentURI.Replace(0, 0, NS_LITERAL_STRING(kMDBDirectoryRoot));
+
+ nsCOMPtr<nsIAbDirectory> parentDir;
+ rv = abManager->GetDirectory(NS_ConvertUTF16toUTF8(parentURI),
+ getter_AddRefs(parentDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (parentDir)
+ {
+ m_dbDirectory = do_GetWeakReference(parentDir);
+ nsCOMPtr<nsIAbDirectory> mailList;
+ rv = CreateABList(listRow, getter_AddRefs(mailList));
+ if (mailList)
+ {
+ nsCOMPtr<nsIAbMDBDirectory> dbparentDir(do_QueryInterface(parentDir, &rv));
+ if(NS_SUCCEEDED(rv))
+ dbparentDir->NotifyDirItemAdded(mailList);
+ }
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::FindMailListbyUnicodeName(const char16_t *listName, bool *exist)
+{
+ nsAutoString unicodeString(listName);
+ ToLowerCase(unicodeString);
+
+ nsCOMPtr <nsIMdbRow> listRow;
+ nsresult rv = GetRowForCharColumn(unicodeString.get(),
+ m_LowerListNameColumnToken, false,
+ false, getter_AddRefs(listRow), nullptr);
+ *exist = (NS_SUCCEEDED(rv) && listRow);
+ return rv;
+}
+
+NS_IMETHODIMP nsAddrDatabase::GetCardCount(uint32_t *count)
+{
+ nsresult rv;
+ mdb_count c;
+ rv = m_mdbPabTable->GetCount(m_mdbEnv, &c);
+ if (NS_SUCCEEDED(rv))
+ *count = c - 1; // Don't count LastRecordKey
+
+ return rv;
+}
+
+bool
+nsAddrDatabase::HasRowButDeletedForCharColumn(const char16_t *unicodeStr, mdb_column findColumn, bool aIsCard, nsIMdbRow **aFindRow)
+{
+ if (!m_mdbStore || !aFindRow || !m_mdbEnv)
+ return false;
+
+ mdbYarn sourceYarn;
+
+ NS_ConvertUTF16toUTF8 UTF8String(unicodeStr);
+ sourceYarn.mYarn_Buf = (void *) UTF8String.get();
+ sourceYarn.mYarn_Fill = UTF8String.Length();
+ sourceYarn.mYarn_Form = 0;
+ sourceYarn.mYarn_Size = sourceYarn.mYarn_Fill;
+
+ mdbOid outRowId;
+ nsresult rv;
+
+ if (aIsCard)
+ {
+ rv = m_mdbStore->FindRow(m_mdbEnv, m_CardRowScopeToken,
+ findColumn, &sourceYarn, &outRowId, aFindRow);
+
+ // no such card, so bail out early
+ if (NS_FAILED(rv) || !*aFindRow)
+ return false;
+
+ // we might not have loaded the "delete cards" table yet
+ // so do that (but don't create it, if we don't have one),
+ // so we can see if the row is really a delete card.
+ if (!m_mdbDeletedCardsTable)
+ rv = InitDeletedCardsTable(false);
+
+ // if still no deleted cards table, there are no deleted cards
+ if (!m_mdbDeletedCardsTable)
+ return false;
+
+ mdb_bool hasRow = false;
+ rv = m_mdbDeletedCardsTable->HasRow(m_mdbEnv, *aFindRow, &hasRow);
+ return (NS_SUCCEEDED(rv) && hasRow);
+ }
+
+ rv = m_mdbStore->FindRow(m_mdbEnv, m_ListRowScopeToken,
+ findColumn, &sourceYarn, &outRowId, aFindRow);
+ return (NS_SUCCEEDED(rv) && *aFindRow);
+}
+
+/* @param aRowPos Contains the row position for multiple calls. Should be
+ * instantiated to -1 on the first call. Or can be null
+ * if you are not making multiple calls.
+ */
+nsresult
+nsAddrDatabase::GetRowForCharColumn(const char16_t *unicodeStr,
+ mdb_column findColumn, bool aIsCard,
+ bool aCaseInsensitive,
+ nsIMdbRow **aFindRow,
+ mdb_pos *aRowPos)
+{
+ NS_ENSURE_ARG_POINTER(unicodeStr);
+ NS_ENSURE_ARG_POINTER(aFindRow);
+ NS_ENSURE_TRUE(m_mdbEnv && m_mdbPabTable, NS_ERROR_NULL_POINTER);
+
+ *aFindRow = nullptr;
+
+ // see bug #198303
+ // the addition of the m_mdbDeletedCardsTable table has complicated life in the addressbook
+ // (it was added for palm sync). until we fix the underlying problem, we have to jump through hoops
+ // in order to know if we have a row (think card) for a given column value (think email=foo@bar.com)
+ // there are 4 scenarios:
+ // 1) no cards with a match
+ // 2) at least one deleted card with a match, but no non-deleted cards
+ // 3) at least one non-deleted card with a match, but no deleted cards
+ // 4) at least one deleted card, and one non-deleted card with a match.
+ //
+ // if we have no cards that match (FindRow() returns nothing), we can bail early
+ // but if FindRow() returns something, we have to check if it is in the deleted table
+ // if not in the deleted table we can return the row (we found a non-deleted card)
+ // but if so, we have to search through the table of non-deleted cards
+ // for a match. If we find one, we return it. but if not, we report that there are no
+ // non-deleted cards. This is the expensive part. The worse case scenario is to have
+ // deleted lots of cards, and then have a lot of non-deleted cards.
+ // we'd have to call FindRow(), HasRow(), and then search the list of non-deleted cards
+ // each time we call GetRowForCharColumn().
+ if (!aRowPos && !HasRowButDeletedForCharColumn(unicodeStr, findColumn, aIsCard, aFindRow))
+ {
+ // If we have a row, it's the row for the non-delete card, so return NS_OK.
+ // If we don't have a row, there are two possible conditions: either the
+ // card does not exist, or we are doing case-insensitive searching and the
+ // value isn't lowercase.
+
+ // Valid result, return.
+ if (*aFindRow)
+ return NS_OK;
+
+ // We definitely don't have anything at this point if case-sensitive.
+ if (!aCaseInsensitive)
+ return NS_ERROR_FAILURE;
+ }
+
+ // check if there is a non-deleted card
+ nsCOMPtr<nsIMdbTableRowCursor> rowCursor;
+ mdb_pos rowPos = -1;
+ bool done = false;
+ nsCOMPtr<nsIMdbRow> currentRow;
+ nsAutoString columnValue;
+
+ if (aRowPos)
+ rowPos = *aRowPos;
+
+ mdb_scope targetScope = aIsCard ? m_CardRowScopeToken : m_ListRowScopeToken;
+
+ m_mdbPabTable->GetTableRowCursor(m_mdbEnv, rowPos, getter_AddRefs(rowCursor));
+ if (!rowCursor)
+ return NS_ERROR_FAILURE;
+
+ while (!done)
+ {
+ nsresult rv = rowCursor->NextRow(m_mdbEnv, getter_AddRefs(currentRow), &rowPos);
+ if (currentRow && NS_SUCCEEDED(rv))
+ {
+ mdbOid rowOid;
+ if (NS_SUCCEEDED(currentRow->GetOid(m_mdbEnv, &rowOid)) && (rowOid.mOid_Scope == targetScope))
+ {
+ rv = GetStringColumn(currentRow, findColumn, columnValue);
+
+ bool equals = aCaseInsensitive ?
+ columnValue.Equals(unicodeStr, nsCaseInsensitiveStringComparator()) :
+ columnValue.Equals(unicodeStr);
+
+ if (NS_SUCCEEDED(rv) && equals)
+ {
+ NS_IF_ADDREF(*aFindRow = currentRow);
+ if (aRowPos)
+ *aRowPos = rowPos;
+ return NS_OK;
+ }
+ }
+ }
+ else
+ done = true;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsAddrDatabase::DeleteRow(nsIMdbTable* dbTable, nsIMdbRow* dbRow)
+{
+ if (!m_mdbEnv)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult err = dbRow->CutAllColumns(m_mdbEnv);
+ err = dbTable->CutRow(m_mdbEnv, dbRow);
+
+ return (NS_SUCCEEDED(err)) ? NS_OK : NS_ERROR_FAILURE;
+}