/* -*- 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/. */ #include "nsAbOSXDirectory.h" #include "nsAbOSXCard.h" #include "nsAbOSXUtils.h" #include "nsAbQueryStringToExpression.h" #include "nsArrayEnumerator.h" #include "nsAutoPtr.h" #include "nsCOMArray.h" #include "nsEnumeratorUtils.h" #include "nsIAbDirectoryQueryProxy.h" #include "nsIAbManager.h" #include "nsObjCExceptions.h" #include "nsServiceManagerUtils.h" #include "nsIMutableArray.h" #include "nsArrayUtils.h" #include "nsIAbBooleanExpression.h" #include "nsComponentManagerUtils.h" #include "nsISimpleEnumerator.h" #include #define kABDeletedRecords (kABDeletedRecords? kABDeletedRecords : @"ABDeletedRecords") #define kABUpdatedRecords (kABUpdatedRecords ? kABUpdatedRecords : @"ABUpdatedRecords") #define kABInsertedRecords (kABInsertedRecords ? kABInsertedRecords : @"ABInsertedRecords") static nsresult GetOrCreateGroup(NSString *aUid, nsIAbDirectory **aResult) { NS_ASSERTION(aUid, "No UID for group!."); nsAutoCString uri(NS_ABOSXDIRECTORY_URI_PREFIX); AppendToCString(aUid, uri); nsresult rv; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr directory; rv = abManager->GetDirectory(uri, getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); NS_IF_ADDREF(*aResult = directory); return NS_OK; } static nsresult GetCard(ABRecord *aRecord, nsIAbCard **aResult, nsIAbOSXDirectory *osxDirectory) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NSString *uid = [aRecord uniqueId]; NS_ASSERTION(uid, "No UID for card!."); if (!uid) return NS_ERROR_FAILURE; nsAutoCString uri(NS_ABOSXCARD_URI_PREFIX); AppendToCString(uid, uri); nsCOMPtr osxCard; nsresult rv = osxDirectory->GetCardByUri(uri, getter_AddRefs(osxCard)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr card = do_QueryInterface(osxCard, &rv); NS_ENSURE_SUCCESS(rv, rv); NS_IF_ADDREF(*aResult = card); return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } static nsresult CreateCard(ABRecord *aRecord, nsIAbCard **aResult) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NSString *uid = [aRecord uniqueId]; NS_ASSERTION(uid, "No UID for card!."); if (!uid) return NS_ERROR_FAILURE; nsresult rv; nsCOMPtr osxCard = do_CreateInstance(NS_ABOSXCARD_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString uri(NS_ABOSXCARD_URI_PREFIX); AppendToCString(uid, uri); rv = osxCard->Init(uri.get()); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr card = do_QueryInterface(osxCard, &rv); NS_ENSURE_SUCCESS(rv, rv); NS_IF_ADDREF(*aResult = card); return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } static nsresult Sync(NSString *aUid) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; ABAddressBook *addressBook = [ABAddressBook sharedAddressBook]; ABRecord *card = [addressBook recordForUniqueId:aUid]; if ([card isKindOfClass:[ABGroup class]]) { nsCOMPtr directory; GetOrCreateGroup(aUid, getter_AddRefs(directory)); nsCOMPtr osxDirectory = do_QueryInterface(directory); if (osxDirectory) { osxDirectory->Update(); } } else { nsCOMPtr abCard; nsresult rv; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr directory; rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"), getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr osxDirectory = do_QueryInterface(directory, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = GetCard(card, getter_AddRefs(abCard), osxDirectory); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr osxCard = do_QueryInterface(abCard); osxCard->Update(true); } return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } @interface ABChangedMonitor : NSObject -(void)ABChanged:(NSNotification *)aNotification; @end @implementation ABChangedMonitor -(void)ABChanged:(NSNotification *)aNotification { NSDictionary *changes = [aNotification userInfo]; nsresult rv; NSArray *inserted = [changes objectForKey:kABInsertedRecords]; if (inserted) { nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr directory; rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"), getter_AddRefs(directory)); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr osxDirectory = do_QueryInterface(directory, &rv); NS_ENSURE_SUCCESS_VOID(rv); unsigned int i, count = [inserted count]; for (i = 0; i < count; ++i) { ABAddressBook *addressBook = [ABAddressBook sharedAddressBook]; ABRecord *card = [addressBook recordForUniqueId:[inserted objectAtIndex:i]]; if ([card isKindOfClass:[ABGroup class]]) { nsCOMPtr directory; GetOrCreateGroup([inserted objectAtIndex:i], getter_AddRefs(directory)); rv = osxDirectory->AssertDirectory(abManager, directory); NS_ENSURE_SUCCESS_VOID(rv); } else { nsCOMPtr abCard; // Construct a card nsresult rv = CreateCard(card, getter_AddRefs(abCard)); NS_ENSURE_SUCCESS_VOID(rv); rv = osxDirectory->AssertCard(abManager, abCard); NS_ENSURE_SUCCESS_VOID(rv); } } } NSArray *updated = [changes objectForKey:kABUpdatedRecords]; if (updated) { unsigned int i, count = [updated count]; for (i = 0; i < count; ++i) { NSString *uid = [updated objectAtIndex:i]; Sync(uid); } } NSArray *deleted = [changes objectForKey:kABDeletedRecords]; if (deleted) { nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr directory; rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"), getter_AddRefs(directory)); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr osxDirectory = do_QueryInterface(directory, &rv); NS_ENSURE_SUCCESS_VOID(rv); unsigned int i, count = [deleted count]; for (i = 0; i < count; ++i) { NSString *deletedUid = [deleted objectAtIndex:i]; nsAutoCString uid; AppendToCString(deletedUid, uid); rv = osxDirectory->DeleteUid(uid); NS_ENSURE_SUCCESS_VOID(rv); } } if (!inserted && !updated && !deleted) { // XXX This is supposed to mean "everything was updated", but we get // this whenever something has changed, so not sure what to do. } } @end static nsresult MapConditionString(nsIAbBooleanConditionString *aCondition, bool aNegate, bool &aCanHandle, ABSearchElement **aResult) { aCanHandle = false; nsAbBooleanConditionType conditionType = 0; nsresult rv = aCondition->GetCondition(&conditionType); NS_ENSURE_SUCCESS(rv, rv); ABSearchComparison comparison; switch (conditionType) { case nsIAbBooleanConditionTypes::Contains: { if (!aNegate) { comparison = kABContainsSubString; aCanHandle = true; } break; } case nsIAbBooleanConditionTypes::DoesNotContain: { if (aNegate) { comparison = kABContainsSubString; aCanHandle = true; } break; } case nsIAbBooleanConditionTypes::Is: { comparison = aNegate ? kABNotEqual : kABEqual; aCanHandle = true; break; } case nsIAbBooleanConditionTypes::IsNot: { comparison = aNegate ? kABEqual : kABNotEqual; aCanHandle = true; break; } case nsIAbBooleanConditionTypes::BeginsWith: { if (!aNegate) { comparison = kABPrefixMatch; aCanHandle = true; } break; } case nsIAbBooleanConditionTypes::EndsWith: { //comparison = kABSuffixMatch; break; } case nsIAbBooleanConditionTypes::LessThan: { comparison = aNegate ? kABGreaterThanOrEqual : kABLessThan; aCanHandle = true; break; } case nsIAbBooleanConditionTypes::GreaterThan: { comparison = aNegate ? kABLessThanOrEqual : kABGreaterThan; aCanHandle = true; break; } } if (!aCanHandle) return NS_OK; nsCString name; rv = aCondition->GetName(getter_Copies(name)); NS_ENSURE_SUCCESS(rv, rv); nsString value; rv = aCondition->GetValue(getter_Copies(value)); NS_ENSURE_SUCCESS(rv, rv); uint32_t length = value.Length(); uint32_t i; for (i = 0; i < nsAbOSXUtils::kPropertyMapSize; ++i) { if (name.Equals(nsAbOSXUtils::kPropertyMap[i].mPropertyName)) { *aResult = [ABPerson searchElementForProperty:nsAbOSXUtils::kPropertyMap[i].mOSXProperty label:nsAbOSXUtils::kPropertyMap[i].mOSXLabel key:nsAbOSXUtils::kPropertyMap[i].mOSXKey value:[NSString stringWithCharacters:reinterpret_cast(value.get()) length:length] comparison:comparison]; return NS_OK; } } if (name.EqualsLiteral("DisplayName") && comparison == kABContainsSubString) { ABSearchElement *first = [ABPerson searchElementForProperty:kABFirstNameProperty label:nil key:nil value:[NSString stringWithCharacters:reinterpret_cast(value.get()) length:length] comparison:comparison]; ABSearchElement *second = [ABPerson searchElementForProperty:kABLastNameProperty label:nil key:nil value:[NSString stringWithCharacters:reinterpret_cast(value.get()) length:length] comparison:comparison]; ABSearchElement *third = [ABGroup searchElementForProperty:kABGroupNameProperty label:nil key:nil value:[NSString stringWithCharacters:reinterpret_cast(value.get()) length:length] comparison:comparison]; *aResult = [ABSearchElement searchElementForConjunction:kABSearchOr children:[NSArray arrayWithObjects:first, second, third, nil]]; return NS_OK; } aCanHandle = false; return NS_OK; } static nsresult BuildSearchElements(nsIAbBooleanExpression *aExpression, bool &aCanHandle, ABSearchElement **aResult) { aCanHandle = true; nsCOMPtr expressions; nsresult rv = aExpression->GetExpressions(getter_AddRefs(expressions)); NS_ENSURE_SUCCESS(rv, rv); nsAbBooleanOperationType operation; rv = aExpression->GetOperation(&operation); NS_ENSURE_SUCCESS(rv, rv); uint32_t count; rv = expressions->GetLength(&count); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(count > 1 && operation != nsIAbBooleanOperationTypes::NOT, "This doesn't make sense!"); NSMutableArray *array = nullptr; if (count > 1) array = [[NSMutableArray alloc] init]; uint32_t i; nsCOMPtr condition; nsCOMPtr subExpression; for (i = 0; i < count; ++i) { ABSearchElement *element = nullptr; condition = do_QueryElementAt(expressions, i); if (condition) { rv = MapConditionString(condition, operation == nsIAbBooleanOperationTypes::NOT, aCanHandle, &element); if (NS_FAILED(rv)) break; } else { subExpression = do_QueryElementAt(expressions, i); if (subExpression) { rv = BuildSearchElements(subExpression, aCanHandle, &element); if (NS_FAILED(rv)) break; } } if (!aCanHandle) { // remember to free the array when returning early [array release]; return NS_OK; } if (element) { if (array) [array addObject:element]; else *aResult = element; } } if (array) { if (NS_SUCCEEDED(rv)) { ABSearchConjunction conjunction = operation == nsIAbBooleanOperationTypes::AND ? kABSearchAnd : kABSearchOr; *aResult = [ABSearchElement searchElementForConjunction:conjunction children:array]; } [array release]; } return rv; } static bool Search(nsIAbBooleanExpression *aExpression, NSArray **aResult) { bool canHandle = false; ABSearchElement *searchElement; nsresult rv = BuildSearchElements(aExpression, canHandle, &searchElement); NS_ENSURE_SUCCESS(rv, false); if (canHandle) *aResult = [[ABAddressBook sharedAddressBook] recordsMatchingSearchElement:searchElement]; return canHandle; } static uint32_t sObserverCount = 0; static ABChangedMonitor *sObserver = nullptr; nsAbOSXDirectory::nsAbOSXDirectory() { } nsAbOSXDirectory::~nsAbOSXDirectory() { if (--sObserverCount == 0) { [[NSNotificationCenter defaultCenter] removeObserver:sObserver]; [sObserver release]; } } NS_IMPL_ISUPPORTS_INHERITED(nsAbOSXDirectory, nsAbDirProperty, nsIAbOSXDirectory, nsIAbDirSearchListener) NS_IMETHODIMP nsAbOSXDirectory::Init(const char *aUri) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; nsresult rv; rv = nsAbDirProperty::Init(aUri); NS_ENSURE_SUCCESS(rv, rv); ABAddressBook *addressBook = [ABAddressBook sharedAddressBook]; if (sObserverCount == 0) { sObserver = [[ABChangedMonitor alloc] init]; [[NSNotificationCenter defaultCenter] addObserver:(ABChangedMonitor*)sObserver selector:@selector(ABChanged:) name:kABDatabaseChangedExternallyNotification object:nil]; } ++sObserverCount; NSArray *cards; nsCOMPtr cardList; bool isRootOSXDirectory = false; if (!mIsQueryURI && mURINoQuery.Length() <= sizeof(NS_ABOSXDIRECTORY_URI_PREFIX)) isRootOSXDirectory = true; if (mIsQueryURI || isRootOSXDirectory) { m_DirPrefId.AssignLiteral("ldap_2.servers.osx"); cards = [[addressBook people] arrayByAddingObjectsFromArray:[addressBook groups]]; if (!mCardList) mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); else rv = mCardList->Clear(); NS_ENSURE_SUCCESS(rv, rv); cardList = mCardList; } else { nsAutoCString uid(Substring(mURINoQuery, sizeof(NS_ABOSXDIRECTORY_URI_PREFIX) - 1)); ABRecord *card = [addressBook recordForUniqueId:[NSString stringWithUTF8String:uid.get()]]; NS_ASSERTION([card isKindOfClass:[ABGroup class]], "Huh."); m_IsMailList = true; AppendToString([card valueForProperty:kABGroupNameProperty], m_ListDirName); ABGroup *group = (ABGroup*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsAutoCString(Substring(mURINoQuery, 21)).get()]]; cards = [[group members] arrayByAddingObjectsFromArray:[group subgroups]]; if (!m_AddressList) m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); else rv = m_AddressList->Clear(); NS_ENSURE_SUCCESS(rv, rv); cardList = m_AddressList; } nsAutoCString ourUuid; GetUuid(ourUuid); unsigned int nbCards = [cards count]; nsCOMPtr card; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr rootOSXDirectory; if (!isRootOSXDirectory) { rv = GetRootOSXDirectory(getter_AddRefs(rootOSXDirectory)); NS_ENSURE_SUCCESS(rv, rv); } for (unsigned int i = 0; i < nbCards; ++i) { // If we're a Group, it's likely that the cards we're going // to create were already created in the root nsAbOSXDirectory, if (!isRootOSXDirectory) rv = GetCard([cards objectAtIndex:i], getter_AddRefs(card), rootOSXDirectory); else { // If we're not a Group, that means we're the root nsAbOSXDirectory, // which means we have to create the cards from scratch. rv = CreateCard([cards objectAtIndex:i], getter_AddRefs(card)); } NS_ENSURE_SUCCESS(rv, rv); // If we're not a query directory, we're going to want to // tell the AB Manager that we've added some cards so that they // show up in the address book views. if (!mIsQueryURI) AssertCard(abManager, card); } return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP nsAbOSXDirectory::GetURI(nsACString &aURI) { if (mURI.IsEmpty()) return NS_ERROR_NOT_INITIALIZED; aURI = mURI; return NS_OK; } NS_IMETHODIMP nsAbOSXDirectory::GetReadOnly(bool *aReadOnly) { NS_ENSURE_ARG_POINTER(aReadOnly); *aReadOnly = true; return NS_OK; } static bool CheckRedundantCards(nsIAbManager *aManager, nsIAbDirectory *aDirectory, nsIAbCard *aCard, NSMutableArray *aCardList) { nsresult rv; nsCOMPtr osxCard = do_QueryInterface(aCard, &rv); NS_ENSURE_SUCCESS(rv, false); nsAutoCString uri; rv = osxCard->GetURI(uri); NS_ENSURE_SUCCESS(rv, false); NSString *uid = [NSString stringWithUTF8String:(uri.get() + 21)]; unsigned int i, count = [aCardList count]; for (i = 0; i < count; ++i) { if ([[[aCardList objectAtIndex:i] uniqueId] isEqualToString:uid]) { [aCardList removeObjectAtIndex:i]; break; } } if (i == count) { aManager->NotifyDirectoryItemDeleted(aDirectory, aCard); return true; } return false; } nsresult nsAbOSXDirectory::GetRootOSXDirectory(nsIAbOSXDirectory **aResult) { if (!mCacheTopLevelOSXAb) { // Attempt to get card from the toplevel directories nsresult rv; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr directory; rv = abManager->GetDirectory(NS_LITERAL_CSTRING(NS_ABOSXDIRECTORY_URI_PREFIX"/"), getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr osxDirectory = do_QueryInterface(directory, &rv); NS_ENSURE_SUCCESS(rv, rv); mCacheTopLevelOSXAb = osxDirectory; } NS_IF_ADDREF(*aResult = mCacheTopLevelOSXAb); return NS_OK; } nsresult nsAbOSXDirectory::Update() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; nsresult rv; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); if (mIsQueryURI) { return NS_OK; } ABAddressBook *addressBook = [ABAddressBook sharedAddressBook]; // Due to the horrible way the address book code works wrt mailing lists // we have to use a different list depending on what we are. This pointer // holds a reference to that list. nsIMutableArray* cardList; NSArray *groups, *cards; if (m_IsMailList) { ABGroup *group = (ABGroup*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsAutoCString(Substring(mURINoQuery, 21)).get()]]; groups = nil; cards = [[group members] arrayByAddingObjectsFromArray:[group subgroups]]; if (!m_AddressList) { m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } // For mailing lists, store the cards in m_AddressList cardList = m_AddressList; } else { groups = [addressBook groups]; cards = [[addressBook people] arrayByAddingObjectsFromArray:groups]; if (!mCardList) { mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } // For directories, store the cards in mCardList cardList = mCardList; } NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:cards]; uint32_t addressCount; rv = cardList->GetLength(&addressCount); NS_ENSURE_SUCCESS(rv, rv); while (addressCount--) { nsCOMPtr card(do_QueryElementAt(cardList, addressCount, &rv)); if (NS_FAILED(rv)) break; if (CheckRedundantCards(abManager, this, card, mutableArray)) cardList->RemoveElementAt(addressCount); } NSEnumerator *enumerator = [mutableArray objectEnumerator]; ABRecord *card; nsCOMPtr abCard; nsCOMPtr rootOSXDirectory; rv = GetRootOSXDirectory(getter_AddRefs(rootOSXDirectory)); NS_ENSURE_SUCCESS(rv, rv); while ((card = [enumerator nextObject])) { rv = GetCard(card, getter_AddRefs(abCard), rootOSXDirectory); if (NS_FAILED(rv)) rv = CreateCard(card, getter_AddRefs(abCard)); NS_ENSURE_SUCCESS(rv, rv); AssertCard(abManager, abCard); } card = (ABRecord*)[addressBook recordForUniqueId:[NSString stringWithUTF8String:nsAutoCString(Substring(mURINoQuery, 21)).get()]]; NSString * stringValue = [card valueForProperty:kABGroupNameProperty]; if (![stringValue isEqualToString:WrapString(m_ListDirName)]) { nsAutoString oldValue(m_ListDirName); AssignToString(stringValue, m_ListDirName); nsCOMPtr supports = do_QueryInterface(static_cast(this), &rv); NS_ENSURE_SUCCESS(rv, rv); abManager->NotifyItemPropertyChanged(supports, "DirName", oldValue.get(), m_ListDirName.get()); } if (groups) { mutableArray = [NSMutableArray arrayWithArray:groups]; nsCOMPtr directory; // It is ok to use m_AddressList here as only top-level directories have // groups, and they will be in m_AddressList if (m_AddressList) { rv = m_AddressList->GetLength(&addressCount); NS_ENSURE_SUCCESS(rv, rv); while (addressCount--) { directory = do_QueryElementAt(m_AddressList, addressCount, &rv); if (NS_FAILED(rv)) continue; nsAutoCString uri; directory->GetURI(uri); uri.Cut(0, 21); NSString *uid = [NSString stringWithUTF8String:uri.get()]; unsigned int j, arrayCount = [mutableArray count]; for (j = 0; j < arrayCount; ++j) { if ([[[mutableArray objectAtIndex:j] uniqueId] isEqualToString:uid]) { [mutableArray removeObjectAtIndex:j]; break; } } if (j == arrayCount) { UnassertDirectory(abManager, directory); } } } enumerator = [mutableArray objectEnumerator]; while ((card = [enumerator nextObject])) { rv = GetOrCreateGroup([card uniqueId], getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); AssertDirectory(abManager, directory); } } return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } nsresult nsAbOSXDirectory::AssertChildNodes() { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; // Queries and mailing lists can't have childnodes. if (mIsQueryURI || m_IsMailList) { return NS_OK; } nsresult rv; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); NSArray *groups = [[ABAddressBook sharedAddressBook] groups]; unsigned int i, count = [groups count]; if (count > 0 && !m_AddressList) { m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr directory; for (i = 0; i < count; ++i) { rv = GetOrCreateGroup([[groups objectAtIndex:i] uniqueId], getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); rv = AssertDirectory(abManager, directory); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } nsresult nsAbOSXDirectory::AssertDirectory(nsIAbManager *aManager, nsIAbDirectory *aDirectory) { uint32_t pos; if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos))) // We already have this directory, so no point in adding it again. return NS_OK; nsresult rv; if (!m_AddressList) { m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } rv = m_AddressList->AppendElement(aDirectory, false); NS_ENSURE_SUCCESS(rv, rv); return aManager->NotifyDirectoryItemAdded(this, aDirectory); } nsresult nsAbOSXDirectory::AssertCard(nsIAbManager *aManager, nsIAbCard *aCard) { nsAutoCString ourUuid; GetUuid(ourUuid); aCard->SetDirectoryId(ourUuid); nsresult rv = m_IsMailList ? m_AddressList->AppendElement(aCard, false) : mCardList->AppendElement(aCard, false); NS_ENSURE_SUCCESS(rv, rv); // Get the card's URI and add it to our card store nsCOMPtr osxCard = do_QueryInterface(aCard, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCString uri; rv = osxCard->GetURI(uri); nsCOMPtr retrievedCard; if (!mCardStore.Get(uri, getter_AddRefs(retrievedCard))) mCardStore.Put(uri, osxCard); return aManager->NotifyDirectoryItemAdded(this, aCard); } nsresult nsAbOSXDirectory::UnassertCard(nsIAbManager *aManager, nsIAbCard *aCard, nsIMutableArray *aCardList) { nsresult rv; uint32_t pos; if (NS_SUCCEEDED(aCardList->IndexOf(0, aCard, &pos))) rv = aCardList->RemoveElementAt(pos); return aManager->NotifyDirectoryItemDeleted(this, aCard); } nsresult nsAbOSXDirectory::UnassertDirectory(nsIAbManager *aManager, nsIAbDirectory *aDirectory) { NS_ENSURE_TRUE(m_AddressList, NS_ERROR_NULL_POINTER); uint32_t pos; if (NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos))) { nsresult rv = m_AddressList->RemoveElementAt(pos); NS_ENSURE_SUCCESS(rv, rv); } return aManager->NotifyDirectoryItemDeleted(this, aDirectory); } NS_IMETHODIMP nsAbOSXDirectory::GetChildNodes(nsISimpleEnumerator **aNodes) { NS_ENSURE_ARG_POINTER(aNodes); // Queries don't have childnodes. if (mIsQueryURI || m_IsMailList || !m_AddressList) return NS_NewEmptyEnumerator(aNodes); return NS_NewArrayEnumerator(aNodes, m_AddressList); } NS_IMETHODIMP nsAbOSXDirectory::GetChildCards(nsISimpleEnumerator **aCards) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; NS_ENSURE_ARG_POINTER(aCards); nsresult rv; NSArray *cards; if (mIsQueryURI) { nsCOMPtr expression; rv = nsAbQueryStringToExpression::Convert(mQueryString, getter_AddRefs(expression)); NS_ENSURE_SUCCESS(rv, rv); bool canHandle = !m_IsMailList && Search(expression, &cards); if (!canHandle) return FallbackSearch(expression, aCards); if (!mCardList) mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); else mCardList->Clear(); NS_ENSURE_SUCCESS(rv, rv); // The uuid for initializing cards nsAutoCString ourUuid; GetUuid(ourUuid); // Fill the results array and update the card list unsigned int nbCards = [cards count]; unsigned int i; nsCOMPtr card; nsCOMPtr rootOSXDirectory; rv = GetRootOSXDirectory(getter_AddRefs(rootOSXDirectory)); NS_ENSURE_SUCCESS(rv, rv); for (i = 0; i < nbCards; ++i) { rv = GetCard([cards objectAtIndex:i], getter_AddRefs(card), rootOSXDirectory); if (NS_FAILED(rv)) rv = CreateCard([cards objectAtIndex:i], getter_AddRefs(card)); NS_ENSURE_SUCCESS(rv, rv); card->SetDirectoryId(ourUuid); mCardList->AppendElement(card, false); } return NS_NewArrayEnumerator(aCards, mCardList); } // Not a search, so just return the appropriate list of items. return m_IsMailList ? NS_NewArrayEnumerator(aCards, m_AddressList) : NS_NewArrayEnumerator(aCards, mCardList); NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP nsAbOSXDirectory::GetIsQuery(bool *aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = mIsQueryURI; return NS_OK; } /* Recursive method that searches for a child card by URI. If it cannot find * it within this directory, it checks all subfolders. */ NS_IMETHODIMP nsAbOSXDirectory::GetCardByUri(const nsACString &aUri, nsIAbOSXCard **aResult) { nsCOMPtr osxCard; // Base Case if (mCardStore.Get(aUri, getter_AddRefs(osxCard))) { NS_IF_ADDREF(*aResult = osxCard); return NS_OK; } // Search children nsCOMPtr enumerator; nsresult rv = this->GetChildNodes(getter_AddRefs(enumerator)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr item; bool hasMore = false; while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { rv = enumerator->GetNext(getter_AddRefs(item)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr childDirectory; childDirectory = do_QueryInterface(item, &rv); if (NS_SUCCEEDED(rv)) { rv = childDirectory->GetCardByUri(aUri, getter_AddRefs(osxCard)); if (NS_SUCCEEDED(rv)) { NS_IF_ADDREF(*aResult = osxCard); return NS_OK; } } } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsAbOSXDirectory::GetCardFromProperty(const char *aProperty, const nsACString &aValue, bool aCaseSensitive, nsIAbCard **aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; if (aValue.IsEmpty()) return NS_OK; nsIMutableArray *list = m_IsMailList ? m_AddressList : mCardList; if (!list) return NS_OK; uint32_t length; nsresult rv = list->GetLength(&length); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr card; nsAutoCString cardValue; for (uint32_t i = 0; i < length && !*aResult; ++i) { card = do_QueryElementAt(list, i, &rv); if (NS_SUCCEEDED(rv)) { rv = card->GetPropertyAsAUTF8String(aProperty, cardValue); if (NS_SUCCEEDED(rv)) { #ifdef MOZILLA_INTERNAL_API bool equal = aCaseSensitive ? cardValue.Equals(aValue) : cardValue.Equals(aValue, nsCaseInsensitiveCStringComparator()); #else bool equal = aCaseSensitive ? cardValue.Equals(aValue) : cardValue.Equals(aValue, CaseInsensitiveCompare); #endif if (equal) NS_IF_ADDREF(*aResult = card); } } } return NS_OK; } NS_IMETHODIMP nsAbOSXDirectory::GetCardsFromProperty(const char *aProperty, const nsACString &aValue, bool aCaseSensitive, nsISimpleEnumerator **aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; if (aValue.IsEmpty()) return NS_NewEmptyEnumerator(aResult); nsIMutableArray *list = m_IsMailList ? m_AddressList : mCardList; if (!list) return NS_NewEmptyEnumerator(aResult); uint32_t length; nsresult rv = list->GetLength(&length); NS_ENSURE_SUCCESS(rv, rv); nsCOMArray resultArray; nsCOMPtr card; nsAutoCString cardValue; for (uint32_t i = 0; i < length; ++i) { card = do_QueryElementAt(list, i, &rv); if (NS_SUCCEEDED(rv)) { rv = card->GetPropertyAsAUTF8String(aProperty, cardValue); if (NS_SUCCEEDED(rv)) { #ifdef MOZILLA_INTERNAL_API bool equal = aCaseSensitive ? cardValue.Equals(aValue) : cardValue.Equals(aValue, nsCaseInsensitiveCStringComparator()); #else bool equal = aCaseSensitive ? cardValue.Equals(aValue) : cardValue.Equals(aValue, CaseInsensitiveCompare); #endif if (equal) resultArray.AppendObject(card); } } } return NS_NewArrayEnumerator(aResult, resultArray); } NS_IMETHODIMP nsAbOSXDirectory::CardForEmailAddress(const nsACString &aEmailAddress, nsIAbCard **aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; if (aEmailAddress.IsEmpty()) return NS_OK; nsIMutableArray *list = m_IsMailList ? m_AddressList : mCardList; if (!list) return NS_OK; uint32_t length; nsresult rv = list->GetLength(&length); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr card; for (uint32_t i = 0; i < length && !*aResult; ++i) { card = do_QueryElementAt(list, i, &rv); if (NS_SUCCEEDED(rv)) { bool hasEmailAddress = false; rv = card->HasEmailAddress(aEmailAddress, &hasEmailAddress); if (NS_SUCCEEDED(rv) && hasEmailAddress) NS_IF_ADDREF(*aResult = card); } } return NS_OK; } NS_IMETHODIMP nsAbOSXDirectory::HasCard(nsIAbCard *aCard, bool *aHasCard) { NS_ENSURE_ARG_POINTER(aCard); NS_ENSURE_ARG_POINTER(aHasCard); nsresult rv = NS_OK; uint32_t index; if (m_IsMailList) { if (m_AddressList) rv = m_AddressList->IndexOf(0, aCard, &index); } else if (mCardList) rv = mCardList->IndexOf(0, aCard, &index); *aHasCard = NS_SUCCEEDED(rv); return NS_OK; } NS_IMETHODIMP nsAbOSXDirectory::HasDirectory(nsIAbDirectory *aDirectory, bool *aHasDirectory) { NS_ENSURE_ARG_POINTER(aDirectory); NS_ENSURE_ARG_POINTER(aHasDirectory); *aHasDirectory = false; uint32_t pos; if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos))) *aHasDirectory = true; return NS_OK; } NS_IMETHODIMP nsAbOSXDirectory::OnSearchFinished(int32_t aResult, const nsAString &aErrorMsg) { return NS_OK; } NS_IMETHODIMP nsAbOSXDirectory::OnSearchFoundCard(nsIAbCard *aCard) { nsresult rv; if (!m_AddressList) { m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } if (!mCardList) { mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } rv = m_AddressList->AppendElement(aCard, false); NS_ENSURE_SUCCESS(rv, rv); rv = mCardList->AppendElement(aCard, false); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString ourUuid; GetUuid(ourUuid); aCard->SetDirectoryId(ourUuid); return NS_OK; } nsresult nsAbOSXDirectory::FallbackSearch(nsIAbBooleanExpression *aExpression, nsISimpleEnumerator **aCards) { nsresult rv; if (mCardList) rv = mCardList->Clear(); else mCardList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); if (m_AddressList) { m_AddressList->Clear(); } else { m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr arguments = do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = arguments->SetExpression(aExpression); NS_ENSURE_SUCCESS(rv, rv); // Don't search the subdirectories. If the current directory is a mailing // list, it won't have any subdirectories. If the current directory is an // addressbook, searching both it and the subdirectories (the mailing // lists), will yield duplicate results because every entry in a mailing // list will be an entry in the parent addressbook. rv = arguments->SetQuerySubDirectories(false); NS_ENSURE_SUCCESS(rv, rv); // Get the directory without the query nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr directory; rv = abManager->GetDirectory(mURINoQuery, getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); // Initiate the proxy query with the no query directory nsCOMPtr queryProxy = do_CreateInstance(NS_ABDIRECTORYQUERYPROXY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = queryProxy->Initiate(); NS_ENSURE_SUCCESS(rv, rv); int32_t context = 0; rv = queryProxy->DoQuery(directory, arguments, this, -1, 0, &context); NS_ENSURE_SUCCESS(rv, rv); return NS_NewArrayEnumerator(aCards, m_AddressList); } nsresult nsAbOSXDirectory::DeleteUid(const nsACString &aUid) { if (!m_AddressList) return NS_ERROR_NULL_POINTER; nsresult rv; nsCOMPtr abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // At this stage we don't know if aUid represents a card or group. The OS X // interfaces don't give us chance to find out, so we have to go through // our lists to find it. // First, we'll see if its in the group list as it is likely to be shorter. // See if this item is in our address list uint32_t addressCount; rv = m_AddressList->GetLength(&addressCount); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString uri(NS_ABOSXDIRECTORY_URI_PREFIX); uri.Append(aUid); // Iterate backwards in case we remove something while (addressCount--) { nsCOMPtr abItem(do_QueryElementAt(m_AddressList, addressCount, &rv)); if (NS_FAILED(rv)) continue; nsCOMPtr directory(do_QueryInterface(abItem, &rv)); if (NS_SUCCEEDED(rv)) { nsAutoCString dirUri; directory->GetURI(dirUri); if (uri.Equals(dirUri)) return UnassertDirectory(abManager, directory); } else { nsCOMPtr osxCard(do_QueryInterface(abItem, &rv)); if (NS_SUCCEEDED(rv)) { nsAutoCString cardUri; osxCard->GetURI(cardUri); if (uri.Equals(cardUri)) { nsCOMPtr card(do_QueryInterface(osxCard, &rv)); if (NS_SUCCEEDED(rv)) return UnassertCard(abManager, card, m_AddressList); } } } } // Second, see if it is one of the cards. if (!mCardList) return NS_ERROR_FAILURE; uri = NS_ABOSXCARD_URI_PREFIX; uri.Append(aUid); rv = mCardList->GetLength(&addressCount); NS_ENSURE_SUCCESS(rv, rv); while (addressCount--) { nsCOMPtr osxCard(do_QueryElementAt(mCardList, addressCount, &rv)); if (NS_FAILED(rv)) continue; nsAutoCString cardUri; osxCard->GetURI(cardUri); if (uri.Equals(cardUri)) { nsCOMPtr card(do_QueryInterface(osxCard, &rv)); if (NS_SUCCEEDED(rv)) return UnassertCard(abManager, card, mCardList); } } return NS_OK; }