diff options
Diffstat (limited to 'comm/mailnews/addrbook/src/nsAbOSXDirectory.mm')
-rw-r--r-- | comm/mailnews/addrbook/src/nsAbOSXDirectory.mm | 911 |
1 files changed, 911 insertions, 0 deletions
diff --git a/comm/mailnews/addrbook/src/nsAbOSXDirectory.mm b/comm/mailnews/addrbook/src/nsAbOSXDirectory.mm new file mode 100644 index 0000000000..fe29a8a0d2 --- /dev/null +++ b/comm/mailnews/addrbook/src/nsAbOSXDirectory.mm @@ -0,0 +1,911 @@ +/* -*- 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 "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 <AddressBook/AddressBook.h> + +#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<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectory> 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_BLOCK_RETURN; + + 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<nsIAbOSXCard> osxCard; + nsresult rv = osxDirectory->GetCardByUri(uri, getter_AddRefs(osxCard)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbCard> card = do_QueryInterface(osxCard, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aResult = card); + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +static nsresult CreateCard(ABRecord* aRecord, nsIAbCard** aResult) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + NSString* uid = [aRecord uniqueId]; + NS_ASSERTION(uid, "No UID for card!."); + if (!uid) return NS_ERROR_FAILURE; + + nsresult rv; + nsCOMPtr<nsIAbOSXCard> osxCard = + do_CreateInstance("@mozilla.org/addressbook/directory;1?type=moz-abosxcard", &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<nsIAbCard> card = do_QueryInterface(osxCard, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + NS_IF_ADDREF(*aResult = card); + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +static nsresult Sync(NSString* aUid) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + ABAddressBook* addressBook = [ABAddressBook sharedAddressBook]; + ABRecord* card = [addressBook recordForUniqueId:aUid]; + if ([card isKindOfClass:[ABGroup class]]) { + nsCOMPtr<nsIAbDirectory> directory; + GetOrCreateGroup(aUid, getter_AddRefs(directory)); + nsCOMPtr<nsIAbOSXDirectory> osxDirectory = do_QueryInterface(directory); + + if (osxDirectory) { + osxDirectory->Update(); + } + } else { + nsCOMPtr<nsIAbCard> abCard; + nsresult rv; + + nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectory> directory; + rv = abManager->GetDirectory(nsLiteralCString(NS_ABOSXDIRECTORY_URI_PREFIX "/"), + getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbOSXDirectory> osxDirectory = do_QueryInterface(directory, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetCard(card, getter_AddRefs(abCard), osxDirectory); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbOSXCard> osxCard = do_QueryInterface(abCard); + osxCard->Update(true); + } + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +@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<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS_VOID(rv); + + nsCOMPtr<nsIAbDirectory> directory; + rv = abManager->GetDirectory(nsLiteralCString(NS_ABOSXDIRECTORY_URI_PREFIX "/"), + getter_AddRefs(directory)); + NS_ENSURE_SUCCESS_VOID(rv); + + nsCOMPtr<nsIAbOSXDirectory> 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<nsIAbDirectory> directory; + GetOrCreateGroup([inserted objectAtIndex:i], getter_AddRefs(directory)); + + rv = osxDirectory->AssertDirectory(abManager, directory); + NS_ENSURE_SUCCESS_VOID(rv); + } else { + nsCOMPtr<nsIAbCard> 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<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS_VOID(rv); + + nsCOMPtr<nsIAbDirectory> directory; + rv = abManager->GetDirectory(nsLiteralCString(NS_ABOSXDIRECTORY_URI_PREFIX "/"), + getter_AddRefs(directory)); + NS_ENSURE_SUCCESS_VOID(rv); + + nsCOMPtr<nsIAbOSXDirectory> 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 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) + +NS_IMETHODIMP +nsAbOSXDirectory::Init(const char* aUri) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + 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<nsIMutableArray> cardList; + bool isRootOSXDirectory = false; + + if (mURI.Length() <= sizeof(NS_ABOSXDIRECTORY_URI_PREFIX)) { + isRootOSXDirectory = true; + + 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(mURI, 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(mURI, 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; + } + + unsigned int nbCards = [cards count]; + nsCOMPtr<nsIAbCard> card; + nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbOSXDirectory> 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); + + // 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. + AssertCard(abManager, card); + } + + if (isRootOSXDirectory) { + AssertChildNodes(); + } + + return NS_OK; + + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +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<nsIAbOSXCard> 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) { + return true; + } + + return false; +} + +nsresult nsAbOSXDirectory::GetRootOSXDirectory(nsIAbOSXDirectory** aResult) { + if (!mCacheTopLevelOSXAb) { + // Attempt to get card from the toplevel directories + nsresult rv; + nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectory> directory; + rv = abManager->GetDirectory(nsLiteralCString(NS_ABOSXDIRECTORY_URI_PREFIX "/"), + getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbOSXDirectory> 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_BLOCK_RETURN; + + nsresult rv; + nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + 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(mURI, 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<nsIAbCard> 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<nsIAbCard> abCard; + nsCOMPtr<nsIAbOSXDirectory> 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(mURI, 21)).get()]]; + NSString* stringValue = [card valueForProperty:kABGroupNameProperty]; + if (![stringValue isEqualToString:WrapString(m_ListDirName)]) { + nsAutoString oldValue(m_ListDirName); + AssignToString(stringValue, m_ListDirName); + } + + if (groups) { + mutableArray = [NSMutableArray arrayWithArray:groups]; + nsCOMPtr<nsIAbDirectory> 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_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +nsresult nsAbOSXDirectory::AssertChildNodes() { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + + // Mailing lists can't have childnodes. + if (m_IsMailList) { + return NS_OK; + } + + nsresult rv; + nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &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<nsIAbDirectory> 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_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +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); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult nsAbOSXDirectory::AssertCard(nsIAbManager* aManager, nsIAbCard* aCard) { + nsAutoCString ourUID; + GetUID(ourUID); + aCard->SetDirectoryUID(ourUID); + + nsresult rv = + m_IsMailList ? m_AddressList->AppendElement(aCard) : mCardList->AppendElement(aCard); + NS_ENSURE_SUCCESS(rv, rv); + + // Get the card's URI and add it to our card store + nsCOMPtr<nsIAbOSXCard> osxCard = do_QueryInterface(aCard, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString uri; + rv = osxCard->GetURI(uri); + + nsCOMPtr<nsIAbOSXCard> retrievedCard; + if (!mCardStore.Get(uri, getter_AddRefs(retrievedCard))) mCardStore.InsertOrUpdate(uri, osxCard); + + return NS_OK; +} + +nsresult nsAbOSXDirectory::UnassertCard(nsIAbManager* aManager, nsIAbCard* aCard, + nsIMutableArray* aCardList) { + uint32_t pos; + if (NS_SUCCEEDED(aCardList->IndexOf(0, aCard, &pos))) { + nsresult rv = aCardList->RemoveElementAt(pos); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +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 NS_OK; +} + +NS_IMETHODIMP nsAbOSXDirectory::GetChildNodes(nsTArray<RefPtr<nsIAbDirectory>>& aNodes) { + aNodes.Clear(); + // Mailing lists don't have childnodes. + if (m_IsMailList || !m_AddressList) { + return NS_OK; + } + + uint32_t count = 0; + nsresult rv = m_AddressList->GetLength(&count); + NS_ENSURE_SUCCESS(rv, rv); + aNodes.SetCapacity(count); + for (uint32_t i = 0; i < count; i++) { + nsCOMPtr<nsIAbDirectory> dir = do_QueryElementAt(m_AddressList, i, &rv); + NS_ENSURE_SUCCESS(rv, rv); + aNodes.AppendElement(&*dir); + } + return NS_OK; +} + +NS_IMETHODIMP +nsAbOSXDirectory::GetChildCardCount(uint32_t* aCount) { + nsIMutableArray* srcCards = m_IsMailList ? m_AddressList : mCardList; + return srcCards->GetLength(aCount); +} + +NS_IMETHODIMP +nsAbOSXDirectory::GetChildCards(nsTArray<RefPtr<nsIAbCard>>& aCards) { + NS_OBJC_BEGIN_TRY_BLOCK_RETURN; + aCards.Clear(); + + // Not a search, so just return the appropriate list of items. + nsIMutableArray* srcCards = m_IsMailList ? m_AddressList : mCardList; + uint32_t count = 0; + nsresult rv = srcCards->GetLength(&count); + NS_ENSURE_SUCCESS(rv, rv); + aCards.SetCapacity(count); + for (uint32_t i = 0; i < count; i++) { + nsCOMPtr<nsIAbCard> card = do_QueryElementAt(srcCards, i, &rv); + NS_ENSURE_SUCCESS(rv, rv); + aCards.AppendElement(&*card); + } + return NS_OK; + NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); +} + +/* 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<nsIAbOSXCard> osxCard; + + // Base Case + if (mCardStore.Get(aUri, getter_AddRefs(osxCard))) { + NS_IF_ADDREF(*aResult = osxCard); + return NS_OK; + } + // Search children + nsTArray<RefPtr<nsIAbDirectory>> children; + nsresult rv = this->GetChildNodes(children); + NS_ENSURE_SUCCESS(rv, rv); + + for (nsIAbDirectory* dir : children) { + nsCOMPtr<nsIAbOSXDirectory> childDirectory = do_QueryInterface(dir, &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<nsIAbCard> 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)) { + bool equal = aCaseSensitive ? cardValue.Equals(aValue) + : cardValue.Equals(aValue, nsCaseInsensitiveCStringComparator); + if (equal) NS_IF_ADDREF(*aResult = card); + } + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsAbOSXDirectory::GetCardsFromProperty(const char* aProperty, const nsACString& aValue, + bool aCaseSensitive, nsTArray<RefPtr<nsIAbCard>>& aResult) { + aResult.Clear(); + 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); + aResult.SetCapacity(length); + for (uint32_t i = 0; i < length; ++i) { + nsCOMPtr<nsIAbCard> card = do_QueryElementAt(list, i, &rv); + if (NS_SUCCEEDED(rv)) { + nsAutoCString cardValue; + rv = card->GetPropertyAsAUTF8String(aProperty, cardValue); + if (NS_SUCCEEDED(rv)) { + bool equal = aCaseSensitive ? cardValue.Equals(aValue) + : cardValue.Equals(aValue, nsCaseInsensitiveCStringComparator); + if (equal) aResult.AppendElement(&*card); + } + } + } + + return NS_OK; +} + +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<nsIAbCard> 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::Search(const nsAString& query, const nsAString& searchString, + nsIAbDirSearchListener* listener) { + nsresult rv; + + nsCOMPtr<nsIAbBooleanExpression> expression; + rv = nsAbQueryStringToExpression::Convert(NS_ConvertUTF16toUTF8(query), + getter_AddRefs(expression)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectoryQueryArguments> arguments = + do_CreateInstance("@mozilla.org/addressbook/directory/query-arguments;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = arguments->SetExpression(expression); + 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); + + // Initiate the proxy query with the no query directory + nsCOMPtr<nsIAbDirectoryQueryProxy> queryProxy = + do_CreateInstance("@mozilla.org/addressbook/directory-query/proxy;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = queryProxy->Initiate(); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t context = 0; + rv = queryProxy->DoQuery(this, arguments, listener, -1, 0, &context); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult nsAbOSXDirectory::DeleteUid(const nsACString& aUid) { + if (!m_AddressList) return NS_ERROR_NULL_POINTER; + + nsresult rv; + nsCOMPtr<nsIAbManager> abManager = do_GetService("@mozilla.org/abmanager;1", &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<nsISupports> abItem(do_QueryElementAt(m_AddressList, addressCount, &rv)); + if (NS_FAILED(rv)) continue; + + nsCOMPtr<nsIAbDirectory> directory(do_QueryInterface(abItem, &rv)); + if (NS_SUCCEEDED(rv)) { + nsAutoCString dirUri; + directory->GetURI(dirUri); + if (uri.Equals(dirUri)) return UnassertDirectory(abManager, directory); + } else { + nsCOMPtr<nsIAbOSXCard> osxCard(do_QueryInterface(abItem, &rv)); + if (NS_SUCCEEDED(rv)) { + nsAutoCString cardUri; + osxCard->GetURI(cardUri); + if (uri.Equals(cardUri)) { + nsCOMPtr<nsIAbCard> 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<nsIAbOSXCard> osxCard(do_QueryElementAt(mCardList, addressCount, &rv)); + if (NS_FAILED(rv)) continue; + + nsAutoCString cardUri; + osxCard->GetURI(cardUri); + + if (uri.Equals(cardUri)) { + nsCOMPtr<nsIAbCard> card(do_QueryInterface(osxCard, &rv)); + if (NS_SUCCEEDED(rv)) return UnassertCard(abManager, card, mCardList); + } + } + return NS_OK; +} |