/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Seth Spitzer * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "msgCore.h" // for pre-compiled headers #include "nsIServiceManager.h" #include "nsIAbCard.h" #include "nsAbBaseCID.h" #include "nsAbAddressCollecter.h" #include "nsIPrefService.h" #include "nsIPrefBranch2.h" #include "nsIAddrBookSession.h" #include "nsIMsgHeaderParser.h" #include "nsIRDFService.h" #include "nsRDFCID.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "prmem.h" #include "nsIAddressBook.h" NS_IMPL_ISUPPORTS2(nsAbAddressCollecter, nsIAbAddressCollecter, nsIObserver) #define PREF_MAIL_COLLECT_ADDRESSBOOK "mail.collect_addressbook" nsAbAddressCollecter::nsAbAddressCollecter() { } nsAbAddressCollecter::~nsAbAddressCollecter() { if (m_database) { m_database->Commit(nsAddrDBCommitType::kSessionCommit); m_database->Close(PR_FALSE); m_database = nsnull; } nsresult rv; nsCOMPtr pPrefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if(NS_SUCCEEDED(rv)) pPrefBranchInt->RemoveObserver(PREF_MAIL_COLLECT_ADDRESSBOOK, this); } NS_IMETHODIMP nsAbAddressCollecter::CollectUnicodeAddress(const PRUnichar *aAddress, PRBool aCreateCard, PRUint32 aSendFormat) { NS_ENSURE_ARG_POINTER(aAddress); // convert the unicode string to UTF-8... nsresult rv = CollectAddress(NS_ConvertUCS2toUTF8(aAddress).get(), aCreateCard, aSendFormat); NS_ENSURE_SUCCESS(rv,rv); return rv; } NS_IMETHODIMP nsAbAddressCollecter::GetCardFromAttribute(const char *aName, const char *aValue, nsIAbCard **aCard) { NS_ENSURE_ARG_POINTER(aCard); if (m_database) // Please DO NOT change the 3rd param of GetCardFromAttribute() call to // PR_TRUE (ie, case insensitive) without reading bugs #128535 and #121478. return m_database->GetCardFromAttribute(m_directory, aName, aValue, PR_FALSE /* retain case */, aCard); return NS_ERROR_FAILURE; } NS_IMETHODIMP nsAbAddressCollecter::CollectAddress(const char *aAddress, PRBool aCreateCard, PRUint32 aSendFormat) { // note that we're now setting the whole recipient list, // not just the pretty name of the first recipient. PRUint32 numAddresses; char *names; char *addresses; nsresult rv; nsCOMPtr pHeader = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); rv = pHeader->ParseHeaderAddresses(nsnull, aAddress, &names, &addresses, &numAddresses); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to parse, so can't collect"); if (NS_FAILED(rv)) return NS_OK; char *curName = names; char *curAddress = addresses; for (PRUint32 i = 0; i < numAddresses; i++) { nsXPIDLCString unquotedName; rv = pHeader->UnquotePhraseOrAddr(curName, PR_FALSE, getter_Copies(unquotedName)); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to unquote name"); if (NS_FAILED(rv)) continue; nsCOMPtr existingCard; nsCOMPtr cardInstance; PRBool emailAddressIn2ndEmailColumn = PR_FALSE; rv = GetCardFromAttribute(kPriEmailColumn, curAddress, getter_AddRefs(existingCard)); // We've not found a card, but is this address actually in the additional // email column? if (!existingCard) { rv = GetCardFromAttribute(k2ndEmailColumn, curAddress, getter_AddRefs(existingCard)); if (existingCard) emailAddressIn2ndEmailColumn = PR_TRUE; } if (!existingCard && aCreateCard) { nsCOMPtr senderCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, &rv); if (NS_SUCCEEDED(rv) && senderCard) { PRBool modifiedCard; rv = SetNamesForCard(senderCard, unquotedName.get(), &modifiedCard); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to set names"); rv = AutoCollectScreenName(senderCard, curAddress, &modifiedCard); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to set screenname"); rv = senderCard->SetPrimaryEmail(NS_ConvertASCIItoUCS2(curAddress).get()); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to set email"); if (aSendFormat != nsIAbPreferMailFormat::unknown) { rv = senderCard->SetPreferMailFormat(aSendFormat); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to remember preferred mail format"); } rv = AddCardToAddressBook(senderCard); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to add card"); } } else if (existingCard && !emailAddressIn2ndEmailColumn) { // address is already in the AB, so update the names PRBool setNames = PR_FALSE; if (!unquotedName.IsEmpty()) { rv = SetNamesForCard(existingCard, unquotedName.get(), &setNames); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to set names"); } PRBool setScreenName = PR_FALSE; rv = AutoCollectScreenName(existingCard, curAddress, &setScreenName); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to set screen name"); PRBool setPreferMailFormat = PR_FALSE; if (aSendFormat != nsIAbPreferMailFormat::unknown) { PRUint32 currentFormat; rv = existingCard->GetPreferMailFormat(¤tFormat); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get preferred mail format"); // we only want to update the AB if the current format is unknown if (currentFormat == nsIAbPreferMailFormat::unknown) { rv = existingCard->SetPreferMailFormat(aSendFormat); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to remember preferred mail format"); setPreferMailFormat = PR_TRUE; } } if (setScreenName || setNames || setPreferMailFormat) existingCard->EditCardToDatabase(m_abURI.get()); } curName += strlen(curName) + 1; curAddress += strlen(curAddress) + 1; } PR_FREEIF(addresses); PR_FREEIF(names); return NS_OK; } nsresult nsAbAddressCollecter::AutoCollectScreenName(nsIAbCard *aCard, const char *aEmail, PRBool *aModifiedCard) { NS_ENSURE_ARG_POINTER(aCard); NS_ENSURE_ARG_POINTER(aEmail); NS_ENSURE_ARG_POINTER(aModifiedCard); *aModifiedCard = PR_FALSE; nsXPIDLString screenName; nsresult rv = aCard->GetAimScreenName(getter_Copies(screenName)); NS_ENSURE_SUCCESS(rv,rv); // don't override existing screennames if (!screenName.IsEmpty()) return NS_OK; const char *atPos = strchr(aEmail, '@'); if (!atPos) return NS_OK; const char *domain = atPos + 1; if (!domain) return NS_OK; // username in // username@aol.com (America Online) // username@cs.com (Compuserve) // username@netscape.net (Netscape webmail) // are all AIM screennames. autocollect that info. if (strcmp(domain,"aol.com") && strcmp(domain,"cs.com") && strcmp(domain,"netscape.net")) return NS_OK; NS_ConvertASCIItoUTF16 userName(Substring(aEmail, atPos)); rv = aCard->SetAimScreenName(userName.get()); NS_ENSURE_SUCCESS(rv,rv); *aModifiedCard = PR_TRUE; return rv; } nsresult nsAbAddressCollecter::SetNamesForCard(nsIAbCard *senderCard, const char *fullName, PRBool *aModifiedCard) { char *firstName = nsnull; char *lastName = nsnull; *aModifiedCard = PR_FALSE; nsXPIDLString displayName; nsresult rv = senderCard->GetDisplayName(getter_Copies(displayName)); NS_ENSURE_SUCCESS(rv,rv); // we already have a display name, so don't do anything if (!displayName.IsEmpty()) return NS_OK; senderCard->SetDisplayName(NS_ConvertUTF8toUCS2(fullName).get()); *aModifiedCard = PR_TRUE; rv = SplitFullName(fullName, &firstName, &lastName); if (NS_SUCCEEDED(rv)) { senderCard->SetFirstName(NS_ConvertUTF8toUCS2(firstName).get()); if (lastName) senderCard->SetLastName(NS_ConvertUTF8toUCS2(lastName).get()); } PR_FREEIF(firstName); PR_FREEIF(lastName); return rv; } nsresult nsAbAddressCollecter::SplitFullName(const char *fullName, char **firstName, char **lastName) { if (fullName) { *firstName = nsCRT::strdup(fullName); if (!*firstName) return NS_ERROR_OUT_OF_MEMORY; char *plastSpace = *firstName; char *walkName = *firstName; char *plastName = nsnull; while (walkName && *walkName) { if (*walkName == ' ') { plastSpace = walkName; plastName = plastSpace + 1; } walkName++; } if (plastName) { *plastSpace = '\0'; *lastName = nsCRT::strdup(plastName); } } return NS_OK; } NS_IMETHODIMP nsAbAddressCollecter::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { nsCOMPtr pPrefBranchInt = do_QueryInterface(aSubject); NS_ASSERTION(pPrefBranchInt, "failed to get prefs"); nsresult rv; nsXPIDLCString prefVal; pPrefBranchInt->GetCharPref(PREF_MAIL_COLLECT_ADDRESSBOOK, getter_Copies(prefVal)); rv = SetAbURI(prefVal.IsEmpty() ? kPersonalAddressbookUri : prefVal.get()); NS_ASSERTION(NS_SUCCEEDED(rv),"failed to change collected ab"); return NS_OK; } nsresult nsAbAddressCollecter::Init(void) { nsresult rv; nsCOMPtr pPrefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv,rv); rv = pPrefBranchInt->AddObserver(PREF_MAIL_COLLECT_ADDRESSBOOK, this, PR_FALSE); nsXPIDLCString prefVal; pPrefBranchInt->GetCharPref(PREF_MAIL_COLLECT_ADDRESSBOOK, getter_Copies(prefVal)); return SetAbURI(prefVal.IsEmpty() ? kPersonalAddressbookUri : prefVal.get()); } nsresult nsAbAddressCollecter::AddCardToAddressBook(nsIAbCard *card) { NS_ENSURE_ARG_POINTER(card); nsCOMPtr addedCard; if (m_directory) return m_directory->AddCard(card, getter_AddRefs(addedCard)); return NS_ERROR_FAILURE; } nsresult nsAbAddressCollecter::SetAbURI(const char *aURI) { NS_ENSURE_ARG_POINTER(aURI); if (!strcmp(aURI,m_abURI.get())) return NS_OK; if (m_database) { m_database->Commit(nsAddrDBCommitType::kSessionCommit); m_database->Close(PR_FALSE); m_database = nsnull; } m_directory = nsnull; m_abURI = aURI; nsresult rv; nsCOMPtr abSession = do_GetService(NS_ADDRBOOKSESSION_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr addressBook = do_GetService(NS_ADDRESSBOOK_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = addressBook->GetAbDatabaseFromURI(m_abURI.get(), getter_AddRefs(m_database)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr resource; rv = rdfService->GetResource(m_abURI, getter_AddRefs(resource)); NS_ENSURE_SUCCESS(rv, rv); m_directory = do_QueryInterface(resource, &rv); NS_ENSURE_SUCCESS(rv, rv); return rv; }