/* -*- 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 "nsMsgUtils.h" #include "nsIMdbFactoryFactory.h" #include "nsSimpleEnumerator.h" #define kAddressCharSetColumn "AddrCharSet" #define kMailListName "ListName" #define kMailListNickName "ListNickName" #define kMailListDescription "ListDescription" #define kMailListTotalAddresses "ListTotalAddresses" // not shown in the UI #define kLowerPriEmailColumn "LowercasePrimaryEmail" #define kLower2ndEmailColumn "LowercaseSecondEmail" #define ID_PAB_TABLE 1 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 COLUMN_STR_MAX 16 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_mdbTokensInitialized(false), m_PabTableKind(0), m_DeletedCardsTableKind(0), m_CardRowScopeToken(0), m_UIDColumnToken(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_PopularityIndexColumnToken(0), m_AddressCharSetColumnToken(0) {} nsAddrDatabase::~nsAddrDatabase() { Close(false); // better have already been closed. // RemoveFromCache(this); // clean up after ourself! if (m_mdbPabTable) m_mdbPabTable->Release(); NS_IF_RELEASE(m_mdbStore); NS_IF_RELEASE(m_mdbEnv); } nsresult nsAddrDatabase::GetMDBFactory(nsIMdbFactory** aMdbFactory) { if (!mMdbFactory) { nsresult rv; nsCOMPtr mdbFactoryService = do_GetService("@mozilla.org/db/mork;1", &rv); if (NS_SUCCEEDED(rv) && mdbFactoryService) { rv = mdbFactoryService->GetMdbFactory(getter_AddRefs(mMdbFactory)); NS_ENSURE_SUCCESS(rv, rv); if (!mMdbFactory) return NS_ERROR_FAILURE; } } NS_ADDREF(*aMdbFactory = mMdbFactory); return NS_OK; } nsresult nsAddrDatabase::SetDbPath(nsIFile* aDbPath) { return aDbPath->Clone(getter_AddRefs(m_dbName)); } // 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. nsresult nsAddrDatabase::OpenMDB(nsIFile* dbName, bool create) { nsCOMPtr mdbFactory; nsresult ret = GetMDBFactory(getter_AddRefs(mdbFactory)); NS_ENSURE_SUCCESS(ret, ret); ret = mdbFactory->MakeEnv(NULL, &m_mdbEnv); if (NS_SUCCEEDED(ret)) { nsIMdbThumb* thumb = nullptr; PathString filePath = dbName->NativePath(); 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) { ret = NS_ERROR_NOT_IMPLEMENTED; } NS_IF_RELEASE(thumb); } return ret; } nsresult nsAddrDatabase::CloseMDB(bool commit) { if (commit) return NS_ERROR_NOT_IMPLEMENTED; //??? 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. nsresult nsAddrDatabase::ForceClosed() { nsresult err = NS_OK; // make sure someone has a reference so object won't get deleted out from // under us. // NS_ADDREF_THIS(); // 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); // NS_RELEASE_THIS(); return err; } nsresult nsAddrDatabase::Close(bool forceCommit /* = TRUE */) { return CloseMDB(forceCommit); } 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) { // This code has always run here. Removing it fails an assertion in the // Mork code which indicates a bad state. In the interest of saving // effort, and since this whole file is doomed after the next release, // I'm leaving it behind. nsIMdbTableRowCursor* rowCursor = nullptr; nsIMdbRow* findRow = nullptr; mdb_pos rowPos = 0; err = m_mdbPabTable->GetTableRowCursor(m_mdbEnv, -1, &rowCursor); if (NS_SUCCEEDED(err) && rowCursor) { do { err = rowCursor->NextRow(m_mdbEnv, &findRow, &rowPos); } while (NS_SUCCEEDED(err) && findRow); rowCursor->Release(); } } } return err; } // 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, kUIDProperty, &m_UIDColumnToken); 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, 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; } //////////////////////////////////////////////////////////////////////////////// uint32_t nsAddrDatabase::GetListAddressTotal(nsIMdbRow* listRow) { uint32_t count = 0; GetIntColumn(listRow, m_ListTotalColumnToken, &count, 0); return count; } 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); } 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::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_UIDColumnToken, tempString); if (NS_SUCCEEDED(err) && !tempString.IsEmpty()) { listCard->SetPropertyAsAString(kUIDProperty, 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; } class nsAddrDBEnumerator : public nsSimpleEnumerator { public: const nsID& DefaultInterface() override { return NS_GET_IID(nsIAbCard); } // nsISimpleEnumerator methods: NS_DECL_NSISIMPLEENUMERATOR // nsAddrDBEnumerator methods: explicit nsAddrDBEnumerator(nsAddrDatabase* aDb); void Clear(); protected: nsAddrDatabase* mDb; nsIMdbTable* mDbTable; nsCOMPtr mRowCursor; nsCOMPtr mCurrentRow; mdb_pos mRowPos; }; nsAddrDBEnumerator::nsAddrDBEnumerator(nsAddrDatabase* aDb) : mDb(aDb), mDbTable(aDb->GetPabTable()), mRowPos(-1) {} 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; } class nsListAddressEnumerator final : public nsSimpleEnumerator { public: const nsID& DefaultInterface() override { return NS_GET_IID(nsIAbCard); } // nsISimpleEnumerator methods: NS_DECL_NSISIMPLEENUMERATOR // nsListAddressEnumerator methods: nsListAddressEnumerator(nsAddrDatabase* aDb, mdb_id aRowID); protected: ~nsListAddressEnumerator() override = default; nsAddrDatabase* 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_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)); if (NS_SUCCEEDED(rv) && 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; } while (++mAddressPos <= mAddressTotal) { nsCOMPtr currentRow; nsresult rv = mDb->GetAddressRowByPos(mListRow, mAddressPos, getter_AddRefs(currentRow)); if (NS_SUCCEEDED(rv)) { nsCOMPtr resultCard; rv = mDb->CreateABCard(currentRow, mListRowID, getter_AddRefs(resultCard)); NS_ENSURE_SUCCESS(rv, rv); return CallQueryInterface(resultCard, aResult); } } return NS_ERROR_FAILURE; } //////////////////////////////////////////////////////////////////////////////// nsresult nsAddrDatabase::EnumerateCards(nsISimpleEnumerator** result) { NS_ADDREF(*result = new nsAddrDBEnumerator(this)); return NS_OK; } nsresult nsAddrDatabase::EnumerateListAddresses(uint32_t listRowID, nsISimpleEnumerator** result) { NS_ADDREF(*result = new nsListAddressEnumerator(this, listRowID)); return NS_OK; } 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("@mozilla.org/addressbook/cardproperty;1", &rv); NS_ENSURE_SUCCESS(rv, rv); InitCardFromRow(personCard, cardRow); personCard->SetPropertyAsUint32(kRowIDProperty, rowID); personCard.forget(result); } 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("MailList%ld", rowID); nsCOMPtr personCard; personCard = do_CreateInstance("@mozilla.org/addressbook/cardproperty;1", &rv); NS_ENSURE_SUCCESS(rv, rv); if (personCard) { GetListCardFromDB(personCard, listRow); personCard->SetPropertyAsUint32(kRowIDProperty, rowID); personCard->SetIsMailList(true); personCard->SetMailListURI(listURI); } personCard.forget(result); 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); }