/* -*- 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" #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 prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); prefs->GetIntPref("mail.displayname.version",¤tDisplayNameVersion); 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::m_dbCache = nullptr; nsTArray* nsAddrDatabase::GetDBCache() { if (!m_dbCache) m_dbCache = new AutoTArray; 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* 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 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 dummyBackupMabFile; nsCOMPtr 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 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 bundleService = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); nsCOMPtr 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 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 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 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 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 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 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 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 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 properties; rv = card->GetProperties(getter_AddRefs(properties)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; while (NS_SUCCEEDED(properties->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr next; rv = properties->GetNext(getter_AddRefs(next)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr prop = do_QueryInterface(next); nsAutoString name; prop->GetName(name); nsCOMPtr 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 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 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 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 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 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 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 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 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 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 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 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 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 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 rowCursor; m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor)); if (rowCursor) { nsCOMPtr 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 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 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 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 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 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 rowCursor; mdb_pos rowPos; bool done = false; nsCOMPtr 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 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 rowCursor; nsresult rv = m_mdbDeletedCardsTable->GetTableRowCursor(m_mdbEnv, -1, getter_AddRefs(rowCursor)); while(NS_SUCCEEDED(rv)) { nsCOMPtr 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 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 pListRow; mdbOid rowOid; rowOid.mOid_Scope = m_ListRowScopeToken; nsCOMPtr 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 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 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 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 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(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 cursor; nsCOMPtr 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(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(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 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 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 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: explicit nsAddrDBEnumerator(nsAddrDatabase* aDb); void Clear(); protected: virtual ~nsAddrDBEnumerator(); RefPtr mDb; nsIMdbTable *mDbTable; nsCOMPtr mRowCursor; nsCOMPtr 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 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 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 mDb; nsIMdbTable *mDbTable; nsCOMPtr 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 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 currentRow; nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos, getter_AddRefs(currentRow)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 resultList; nsIMdbTableRowCursor* rowCursor = nullptr; nsCOMPtr 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 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 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 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 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 personCard; nsCOMPtr 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 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 mailList; nsCOMPtr dbm_dbDirectory(do_QueryReferent(m_dbDirectory, &rv)); if (NS_SUCCEEDED(rv) && dbm_dbDirectory) { rv = dbm_dbDirectory->AddDirectory(listURI, getter_AddRefs(mailList)); nsCOMPtr 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 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 row; bool done = false; nsCOMArray list; nsCOMPtr 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 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 parentDir; rv = abManager->GetDirectory(NS_ConvertUTF16toUTF8(parentURI), getter_AddRefs(parentDir)); NS_ENSURE_SUCCESS(rv, rv); if (parentDir) { m_dbDirectory = do_GetWeakReference(parentDir); nsCOMPtr mailList; rv = CreateABList(listRow, getter_AddRefs(mailList)); if (mailList) { nsCOMPtr 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 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 rowCursor; mdb_pos rowPos = -1; bool done = false; nsCOMPtr 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; }