diff options
Diffstat (limited to 'comm/mailnews/addrbook/src/nsAbAddressCollector.cpp')
-rw-r--r-- | comm/mailnews/addrbook/src/nsAbAddressCollector.cpp | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/comm/mailnews/addrbook/src/nsAbAddressCollector.cpp b/comm/mailnews/addrbook/src/nsAbAddressCollector.cpp new file mode 100644 index 0000000000..09018daf96 --- /dev/null +++ b/comm/mailnews/addrbook/src/nsAbAddressCollector.cpp @@ -0,0 +1,281 @@ +/* -*- 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 "msgCore.h" // for pre-compiled headers +#include "nsISimpleEnumerator.h" + +#include "nsIAbCard.h" +#include "nsAbAddressCollector.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsString.h" +#include "prmem.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsIAbManager.h" +#include "mozilla/mailnews/MimeHeaderParser.h" + +using namespace mozilla::mailnews; + +NS_IMPL_ISUPPORTS(nsAbAddressCollector, nsIAbAddressCollector, nsIObserver) + +#define PREF_MAIL_COLLECT_ADDRESSBOOK "mail.collect_addressbook" + +nsAbAddressCollector::nsAbAddressCollector() {} + +nsAbAddressCollector::~nsAbAddressCollector() { + nsresult rv; + nsCOMPtr<nsIPrefBranch> pPrefBranchInt( + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) + pPrefBranchInt->RemoveObserver(PREF_MAIL_COLLECT_ADDRESSBOOK, this); +} + +/** + * Returns the first card found with the specified email address. This + * returns an already addrefed pointer to the card if the card is found. + */ +already_AddRefed<nsIAbCard> nsAbAddressCollector::GetCardForAddress( + const char* aProperty, const nsACString& aEmailAddress, + nsIAbDirectory** aDirectory) { + nsresult rv; + nsCOMPtr<nsIAbManager> abManager( + do_GetService("@mozilla.org/abmanager;1", &rv)); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsTArray<RefPtr<nsIAbDirectory>> directories; + rv = abManager->GetDirectories(directories); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr<nsIAbCard> result; + uint32_t count = directories.Length(); + for (uint32_t i = 0; i < count; i++) { + // Some implementations may return NS_ERROR_NOT_IMPLEMENTED here, + // so just catch the value and continue. + if (NS_FAILED(directories[i]->GetCardFromProperty( + aProperty, aEmailAddress, false, getter_AddRefs(result)))) { + continue; + } + + if (result) { + if (aDirectory) directories[i].forget(aDirectory); + return result.forget(); + } + } + return nullptr; +} + +NS_IMETHODIMP +nsAbAddressCollector::CollectAddress(const nsACString& aAddresses, + bool aCreateCard) { + // If we've not got a valid directory, no point in going any further + if (!mDirectory) return NS_OK; + + // note that we're now setting the whole recipient list, + // not just the pretty name of the first recipient. + nsTArray<nsCString> names; + nsTArray<nsCString> addresses; + ExtractAllAddresses(EncodedHeader(aAddresses), UTF16ArrayAdapter<>(names), + UTF16ArrayAdapter<>(addresses)); + uint32_t numAddresses = names.Length(); + + for (uint32_t i = 0; i < numAddresses; i++) { + // Don't allow collection of addresses with no email address, it makes + // no sense. Whilst we should never get here in most normal cases, we + // should still be careful. + if (addresses[i].IsEmpty()) continue; + + CollectSingleAddress(addresses[i], names[i], aCreateCard, false); + } + return NS_OK; +} + +NS_IMETHODIMP +nsAbAddressCollector::CollectSingleAddress(const nsACString& aEmail, + const nsACString& aDisplayName, + bool aCreateCard, + bool aSkipCheckExisting) { + if (!mDirectory) return NS_OK; + + nsresult rv; + + nsCOMPtr<nsIAbDirectory> originDirectory; + nsCOMPtr<nsIAbCard> card; + if (!aSkipCheckExisting) { + card = GetCardForAddress(kPriEmailProperty, aEmail, + getter_AddRefs(originDirectory)); + + // If a card has aEmail, but it's the secondary address, we don't want to + // update any properties, so just return. + if (!card) { + card = GetCardForAddress(k2ndEmailProperty, aEmail, + getter_AddRefs(originDirectory)); + if (card) return NS_OK; + } + } + + if (!card && (aCreateCard || aSkipCheckExisting)) { + card = do_CreateInstance("@mozilla.org/addressbook/cardproperty;1", &rv); + if (NS_SUCCEEDED(rv) && card) { + // Set up the fields for the new card. + SetNamesForCard(card, aDisplayName); + AutoCollectScreenName(card, aEmail); + + if (NS_SUCCEEDED(card->SetPrimaryEmail(NS_ConvertUTF8toUTF16(aEmail)))) { + nsCOMPtr<nsIAbCard> addedCard; + rv = mDirectory->AddCard(card, getter_AddRefs(addedCard)); + NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add card"); + } + } + } else if (card && originDirectory) { + // It could be that the origin directory is read-only, so don't try and + // write to it if it is. + bool readOnly; + rv = originDirectory->GetReadOnly(&readOnly); + NS_ENSURE_SUCCESS(rv, rv); + + if (readOnly) return NS_OK; + + // address is already in the AB, so update the names + bool modifiedCard = false; + + nsString displayName; + card->GetDisplayName(displayName); + // If we already have a display name, don't set the names on the card. + if (displayName.IsEmpty() && !aDisplayName.IsEmpty()) + modifiedCard = SetNamesForCard(card, aDisplayName); + + if (modifiedCard) originDirectory->ModifyCard(card); + } + + return NS_OK; +} + +// Works out the screen name to put on the card for some well-known addresses +void nsAbAddressCollector::AutoCollectScreenName(nsIAbCard* aCard, + const nsACString& aEmail) { + if (!aCard) return; + + int32_t atPos = aEmail.FindChar('@'); + if (atPos == -1) return; + + const nsACString& domain = Substring(aEmail, atPos + 1); + + if (domain.IsEmpty()) return; + // username in + // username@aol.com (America Online) + // username@cs.com (Compuserve) + // username@netscape.net (Netscape webmail) + // are all AIM screennames. autocollect that info. + if (domain.EqualsLiteral("aol.com") || domain.EqualsLiteral("cs.com") || + domain.EqualsLiteral("netscape.net")) + aCard->SetPropertyAsAUTF8String(kScreenNameProperty, + Substring(aEmail, 0, atPos)); + else if (domain.EqualsLiteral("gmail.com") || + domain.EqualsLiteral("googlemail.com")) + aCard->SetPropertyAsAUTF8String(kGtalkProperty, + Substring(aEmail, 0, atPos)); +} + +// Returns true if the card was modified successfully. +bool nsAbAddressCollector::SetNamesForCard(nsIAbCard* aSenderCard, + const nsACString& aFullName) { + nsCString firstName; + nsCString lastName; + bool modifiedCard = false; + + if (NS_SUCCEEDED( + aSenderCard->SetDisplayName(NS_ConvertUTF8toUTF16(aFullName)))) + modifiedCard = true; + + // Now split up the full name. + SplitFullName(nsCString(aFullName), firstName, lastName); + + if (!firstName.IsEmpty() && + NS_SUCCEEDED(aSenderCard->SetFirstName(NS_ConvertUTF8toUTF16(firstName)))) + modifiedCard = true; + + if (!lastName.IsEmpty() && + NS_SUCCEEDED(aSenderCard->SetLastName(NS_ConvertUTF8toUTF16(lastName)))) + modifiedCard = true; + + if (modifiedCard) aSenderCard->SetPropertyAsBool("PreferDisplayName", false); + + return modifiedCard; +} + +// Splits the first and last name based on the space between them. +void nsAbAddressCollector::SplitFullName(const nsCString& aFullName, + nsCString& aFirstName, + nsCString& aLastName) { + int index = aFullName.RFindChar(' '); + if (index != -1) { + aLastName = Substring(aFullName, index + 1); + aFirstName = Substring(aFullName, 0, index); + } +} + +// Observes the collected address book pref in case it changes. +NS_IMETHODIMP +nsAbAddressCollector::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject); + if (!prefBranch) { + NS_ASSERTION(prefBranch, "failed to get prefs"); + return NS_OK; + } + + SetUpAbFromPrefs(prefBranch); + return NS_OK; +} + +// Initialises the collector with the required items. +nsresult nsAbAddressCollector::Init(void) { + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefBranch( + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = prefBranch->AddObserver(PREF_MAIL_COLLECT_ADDRESSBOOK, this, false); + NS_ENSURE_SUCCESS(rv, rv); + + SetUpAbFromPrefs(prefBranch); + return NS_OK; +} + +// Performs the necessary changes to set up the collector for the specified +// collected address book. +void nsAbAddressCollector::SetUpAbFromPrefs(nsIPrefBranch* aPrefBranch) { + nsCString abURI; + aPrefBranch->GetCharPref(PREF_MAIL_COLLECT_ADDRESSBOOK, abURI); + + if (abURI.IsEmpty()) abURI.AssignLiteral(kPersonalAddressbookUri); + + if (abURI == mABURI) return; + + mDirectory = nullptr; + mABURI = abURI; + + nsresult rv; + nsCOMPtr<nsIAbManager> abManager( + do_GetService("@mozilla.org/abmanager;1", &rv)); + NS_ENSURE_SUCCESS_VOID(rv); + + rv = abManager->GetDirectory(mABURI, getter_AddRefs(mDirectory)); + NS_ENSURE_SUCCESS_VOID(rv); + + bool readOnly; + rv = mDirectory->GetReadOnly(&readOnly); + NS_ENSURE_SUCCESS_VOID(rv); + + // If the directory is read-only, we can't write to it, so just blank it out + // here, and warn because we shouldn't hit this (UI is wrong). + if (readOnly) { + NS_ERROR( + "Address Collection book preferences is set to a read-only book. " + "Address collection will not take place."); + mDirectory = nullptr; + } +} |