summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/addrbook/src/nsAbAddressCollector.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/addrbook/src/nsAbAddressCollector.cpp')
-rw-r--r--comm/mailnews/addrbook/src/nsAbAddressCollector.cpp281
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;
+ }
+}