diff options
Diffstat (limited to 'mailnews/addrbook/src/nsAbManager.cpp')
-rw-r--r-- | mailnews/addrbook/src/nsAbManager.cpp | 1422 |
1 files changed, 1422 insertions, 0 deletions
diff --git a/mailnews/addrbook/src/nsAbManager.cpp b/mailnews/addrbook/src/nsAbManager.cpp new file mode 100644 index 000000000..2de1b4468 --- /dev/null +++ b/mailnews/addrbook/src/nsAbManager.cpp @@ -0,0 +1,1422 @@ +/* -*- 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 "nsAbManager.h" +#include "nsAbBaseCID.h" +#include "nsAddrDatabase.h" +#include "nsIAbMDBDirectory.h" +#include "nsIOutputStream.h" +#include "nsNetUtil.h" +#include "nsMsgI18N.h" +#include "nsIStringBundle.h" +#include "nsMsgUtils.h" +#include "nsAppDirectoryServiceDefs.h" +#include "plstr.h" +#include "prmem.h" +#include "nsIServiceManager.h" +#include "mozIDOMWindow.h" +#include "nsIFilePicker.h" +#include "plbase64.h" +#include "nsIWindowWatcher.h" +#include "nsDirectoryServiceUtils.h" +#include "nsVCard.h" +#include "nsVCardObj.h" +#include "nsIAbLDAPAttributeMap.h" +#include "nsICommandLine.h" +#include "nsIFile.h" +#include "nsIMutableArray.h" +#include "nsArrayUtils.h" +#include "nsDirectoryServiceUtils.h" +#include "nsIObserverService.h" +#include "nsDirPrefs.h" +#include "nsThreadUtils.h" +#include "nsIAbDirFactory.h" +#include "nsComponentManagerUtils.h" +#include "nsIIOService.h" +#include "nsAbQueryStringToExpression.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Services.h" +using namespace mozilla; + +struct ExportAttributesTableStruct +{ + const char* abPropertyName; + uint32_t plainTextStringID; +}; + +// our schema is not fixed yet, but we still want some sort of objectclass +// for now, use obsolete in the class name, hinting that this will change +// see bugs bug #116692 and #118454 +#define MOZ_AB_OBJECTCLASS "mozillaAbPersonAlpha" + +// for now, the oder of the attributes with true for includeForPlainText +// should be in the same order as they are in the import code +// see importMsgProperties and nsImportStringBundle. +// +// XXX todo, merge with what's in nsAbLDAPProperties.cpp, so we can +// use this for LDAP and LDIF export +// +// here's how we're coming up with the ldapPropertyName values +// if they are specified in RFC 2798, use them +// else use the 4.x LDIF attribute names (for example, "xmozillanickname" +// as we want to allow export from mozilla back to 4.x, and other apps +// are probably out there that can handle 4.x LDIF) +// else use the MOZ_AB_LDIF_PREFIX prefix, see nsIAddrDatabase.idl + +const ExportAttributesTableStruct EXPORT_ATTRIBUTES_TABLE[] = { + {kFirstNameProperty, 2100}, + {kLastNameProperty, 2101}, + {kDisplayNameProperty, 2102}, + {kNicknameProperty, 2103}, + {kPriEmailProperty, 2104}, + {k2ndEmailProperty, 2105}, + {kScreenNameProperty, 2136}, + {kPreferMailFormatProperty, 0}, + {kLastModifiedDateProperty, 0}, + {kWorkPhoneProperty, 2106}, + {kWorkPhoneTypeProperty, 0}, + {kHomePhoneProperty, 2107}, + {kHomePhoneTypeProperty, 0}, + {kFaxProperty, 2108}, + {kFaxTypeProperty, 0}, + {kPagerProperty, 2109}, + {kPagerTypeProperty, 0}, + {kCellularProperty, 2110}, + {kCellularTypeProperty, 0}, + {kHomeAddressProperty, 2111}, + {kHomeAddress2Property, 2112}, + {kHomeCityProperty, 2113}, + {kHomeStateProperty, 2114}, + {kHomeZipCodeProperty, 2115}, + {kHomeCountryProperty, 2116}, + {kWorkAddressProperty, 2117}, + {kWorkAddress2Property, 2118}, + {kWorkCityProperty, 2119}, + {kWorkStateProperty, 2120}, + {kWorkZipCodeProperty, 2121}, + {kWorkCountryProperty, 2122}, + {kJobTitleProperty, 2123}, + {kDepartmentProperty, 2124}, + {kCompanyProperty, 2125}, + {kWorkWebPageProperty, 2126}, + {kHomeWebPageProperty, 2127}, + {kBirthYearProperty, 2128}, // unused for now + {kBirthMonthProperty, 2129}, // unused for now + {kBirthDayProperty, 2130}, // unused for now + {kCustom1Property, 2131}, + {kCustom2Property, 2132}, + {kCustom3Property, 2133}, + {kCustom4Property, 2134}, + {kNotesProperty, 2135}, + {kAnniversaryYearProperty, 0}, + {kAnniversaryMonthProperty, 0}, + {kAnniversaryDayProperty, 0}, + {kSpouseNameProperty, 0}, + {kFamilyNameProperty, 0}, +}; + +// +// nsAbManager +// +nsAbManager::nsAbManager() +{ +} + +nsAbManager::~nsAbManager() +{ +} + +NS_IMPL_ISUPPORTS(nsAbManager, nsIAbManager, nsICommandLineHandler, + nsIObserver) + +nsresult nsAbManager::Init() +{ + NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED); + + nsresult rv = observerService->AddObserver(this, "profile-do-change", false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, + false); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::Observe(nsISupports *aSubject, const char *aTopic, + const char16_t *someData) +{ + // The nsDirPrefs code caches all the directories that it got + // from the first profiles prefs.js. + // When we profile switch, we need to force it to shut down. + // We'll re-load all the directories from the second profiles prefs.js + // that happens in nsAbBSDirectory::GetChildNodes() + // when we call DIR_GetDirectories(). + if (!strcmp(aTopic, "profile-do-change")) + { + DIR_ShutDown(); + return NS_OK; + } + + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) + { + DIR_ShutDown(); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED); + + nsresult rv = observerService->RemoveObserver(this, "profile-do-change"); + NS_ENSURE_SUCCESS(rv, rv); + + rv = observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +// +// nsIAbManager +// + +NS_IMETHODIMP nsAbManager::GetDirectories(nsISimpleEnumerator **aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + // We cache the top level AB to ensure that nsIAbDirectory items are not + // created and dumped every time GetDirectories is called. This was causing + // performance problems, especially with the content policy on messages + // with lots of urls. + nsresult rv; + nsCOMPtr<nsIAbDirectory> rootAddressBook; + rv = GetRootDirectory(getter_AddRefs(rootAddressBook)); + NS_ENSURE_SUCCESS(rv, rv); + + return rootAddressBook->GetChildNodes(aResult); +} + +nsresult +nsAbManager::GetRootDirectory(nsIAbDirectory **aResult) +{ + // We cache the top level AB to ensure that nsIAbDirectory items are not + // created and dumped every time GetDirectories is called. This was causing + // performance problems, especially with the content policy on messages + // with lots of urls. + nsresult rv; + + if (!mCacheTopLevelAb) + { + nsCOMPtr<nsIAbDirectory> rootAddressBook(do_GetService(NS_ABDIRECTORY_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + mCacheTopLevelAb = rootAddressBook; + } + + NS_IF_ADDREF(*aResult = mCacheTopLevelAb); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::GetDirectoryFromId(const nsACString &aDirPrefId, + nsIAbDirectory **aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsCOMPtr<nsISimpleEnumerator> enumerator; + nsresult rv = GetDirectories(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsISupports> support; + nsCOMPtr<nsIAbDirectory> directory; + + bool hasMore = false; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) { + rv = enumerator->GetNext(getter_AddRefs(support)); + NS_ENSURE_SUCCESS(rv, rv); + directory = do_QueryInterface(support, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("Unable to select Address book nsAbManager::GetDirectoryFromId()"); + continue; + } + + nsCString dirPrefId; + directory->GetDirPrefId(dirPrefId); + if (dirPrefId.Equals(aDirPrefId)) { + directory.forget(aResult); + return NS_OK; + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::GetDirectory(const nsACString &aURI, + nsIAbDirectory **aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + nsresult rv; + nsCOMPtr<nsIAbDirectory> directory; + + // Was the directory root requested? + if (aURI.EqualsLiteral(kAllDirectoryRoot)) + { + rv = GetRootDirectory(getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + NS_IF_ADDREF(*aResult = directory); + return NS_OK; + } + + // Do we have a copy of this directory already within our look-up table? + if (!mAbStore.Get(aURI, getter_AddRefs(directory))) + { + // The directory wasn't in our look-up table, so we need to instantiate + // it. First, extract the scheme from the URI... + + nsAutoCString scheme; + + int32_t colon = aURI.FindChar(':'); + if (colon <= 0) + return NS_ERROR_MALFORMED_URI; + scheme = Substring(aURI, 0, colon); + + // Construct the appropriate nsIAbDirectory... + nsAutoCString contractID; + contractID.AssignLiteral(NS_AB_DIRECTORY_TYPE_CONTRACTID_PREFIX); + contractID.Append(scheme); + directory = do_CreateInstance(contractID.get(), &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Init it with the URI + rv = directory->Init(PromiseFlatCString(aURI).get()); + NS_ENSURE_SUCCESS(rv, rv); + + // Check if this directory was initiated with a search query. If so, + // we don't cache it. + bool isQuery = false; + rv = directory->GetIsQuery(&isQuery); + NS_ENSURE_SUCCESS(rv, rv); + + if (!isQuery) + mAbStore.Put(aURI, directory); + } + NS_IF_ADDREF(*aResult = directory); + + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::NewAddressBook(const nsAString &aDirName, + const nsACString &aURI, + const uint32_t aType, + const nsACString &aPrefName, + nsACString &aResult) +{ + nsresult rv; + + nsCOMPtr<nsIAbDirectory> parentDir; + rv = GetRootDirectory(getter_AddRefs(parentDir)); + NS_ENSURE_SUCCESS(rv, rv); + return parentDir->CreateNewDirectory(aDirName, aURI, aType, aPrefName, aResult); +} + +NS_IMETHODIMP nsAbManager::DeleteAddressBook(const nsACString &aURI) +{ + // Find the address book + nsresult rv; + + nsCOMPtr<nsIAbDirectory> directory; + rv = GetDirectory(aURI, getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectory> rootDirectory; + rv = GetRootDirectory(getter_AddRefs(rootDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + // Go through each of the children of the address book + // (so, the mailing lists) and remove their entries from + // the look up table. + nsCOMPtr<nsISimpleEnumerator> enumerator; + rv = directory->GetChildNodes(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISupports> item; + nsCOMPtr<nsIAbDirectory> childDirectory; + bool hasMore = false; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) + { + rv = enumerator->GetNext(getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, rv); + + childDirectory = do_QueryInterface(item, &rv); + if (NS_SUCCEEDED(rv)) + { + nsCString childURI; + rv = childDirectory->GetURI(childURI); + NS_ENSURE_SUCCESS(rv, rv); + + mAbStore.Remove(childURI); + } + } + + mAbStore.Remove(aURI); + + bool isMailList; + rv = directory->GetIsMailList(&isMailList); + NS_ENSURE_SUCCESS(rv, rv); + + if (!isMailList) + // If we're not a mailing list, then our parent + // must be the root address book directory. + return rootDirectory->DeleteDirectory(directory); + + nsCString parentUri; + parentUri.Append(aURI); + int32_t pos = parentUri.RFindChar('/'); + + // If we didn't find a /, we're in trouble. + if (pos == -1) + return NS_ERROR_FAILURE; + + parentUri = StringHead(parentUri, pos); + nsCOMPtr<nsIAbDirectory> parentDirectory; + rv = GetDirectory(parentUri, getter_AddRefs(parentDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + return parentDirectory->DeleteDirectory(directory); +} + +NS_IMETHODIMP nsAbManager::AddAddressBookListener(nsIAbListener *aListener, + abListenerNotifyFlagValue aNotifyFlags) +{ + NS_ENSURE_ARG_POINTER(aListener); + + abListener newListener(aListener, aNotifyFlags); + mListeners.AppendElementUnlessExists(newListener); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::RemoveAddressBookListener(nsIAbListener *aListener) +{ + NS_ENSURE_ARG_POINTER(aListener); + + mListeners.RemoveElement(aListener); + return NS_OK; +} + +#define NOTIFY_AB_LISTENERS(propertyflag_, propertyfunc_, params_) \ + PR_BEGIN_MACRO \ + nsTObserverArray<abListener>::ForwardIterator iter(mListeners); \ + while (iter.HasMore()) { \ + const abListener &abL = iter.GetNext(); \ + if (abL.mNotifyFlags & nsIAbListener::propertyflag_) \ + abL.mListener->propertyfunc_ params_; \ + } \ + PR_END_MACRO + +NS_IMETHODIMP nsAbManager::NotifyItemPropertyChanged(nsISupports *aItem, + const char *aProperty, + const char16_t* aOldValue, + const char16_t* aNewValue) +{ + NOTIFY_AB_LISTENERS(itemChanged, OnItemPropertyChanged, + (aItem, aProperty, aOldValue, aNewValue)); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::NotifyDirectoryItemAdded(nsIAbDirectory *aParentDirectory, + nsISupports *aItem) +{ + NOTIFY_AB_LISTENERS(itemAdded, OnItemAdded, (aParentDirectory, aItem)); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::NotifyDirectoryItemDeleted(nsIAbDirectory *aParentDirectory, + nsISupports *aItem) +{ + NOTIFY_AB_LISTENERS(directoryItemRemoved, OnItemRemoved, + (aParentDirectory, aItem)); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::NotifyDirectoryDeleted(nsIAbDirectory *aParentDirectory, + nsISupports *aDirectory) +{ + NOTIFY_AB_LISTENERS(directoryRemoved, OnItemRemoved, + (aParentDirectory, aDirectory)); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::GetUserProfileDirectory(nsIFile **userDir) +{ + NS_ENSURE_ARG_POINTER(userDir); + *userDir = nullptr; + + nsresult rv; + nsCOMPtr<nsIFile> profileDir; + nsAutoCString pathBuf; + + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir)); + NS_ENSURE_SUCCESS(rv, rv); + + profileDir.forget(userDir); + return NS_OK; +} + +NS_IMETHODIMP nsAbManager::MailListNameExists(const char16_t *aName, bool *aExists) +{ + nsresult rv; + NS_ENSURE_ARG_POINTER(aExists); + + *aExists = false; + + // now get the top-level book + nsCOMPtr<nsIAbDirectory> topDirectory; + rv = GetRootDirectory(getter_AddRefs(topDirectory)); + + // now go through the address books + nsCOMPtr<nsISimpleEnumerator> enumerator; + rv = topDirectory->GetChildNodes(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) + { + nsCOMPtr<nsISupports> item; + rv = enumerator->GetNext(getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectory> directory = do_QueryInterface(item, &rv); + if (NS_FAILED(rv)) + continue; + + rv = directory->HasMailListWithName(aName, aExists); + if (NS_SUCCEEDED(rv) && *aExists) + return NS_OK; + } + + *aExists = false; + return NS_OK; +} + +#define CSV_DELIM "," +#define CSV_DELIM_LEN 1 +#define TAB_DELIM "\t" +#define TAB_DELIM_LEN 1 + +#define CSV_FILE_EXTENSION ".csv" +#define TAB_FILE_EXTENSION ".tab" +#define TXT_FILE_EXTENSION ".txt" +#define VCF_FILE_EXTENSION ".vcf" +#define LDIF_FILE_EXTENSION ".ldi" +#define LDIF_FILE_EXTENSION2 ".ldif" + +enum ADDRESSBOOK_EXPORT_FILE_TYPE +{ + CSV_EXPORT_TYPE = 0, + CSV_EXPORT_TYPE_UTF8 = 1, + TAB_EXPORT_TYPE = 2, + TAB_EXPORT_TYPE_UTF8 = 3, + VCF_EXPORT_TYPE = 4, + LDIF_EXPORT_TYPE = 5, +}; + +NS_IMETHODIMP nsAbManager::ExportAddressBook(mozIDOMWindowProxy *aParentWin, nsIAbDirectory *aDirectory) +{ + NS_ENSURE_ARG_POINTER(aParentWin); + + nsresult rv; + nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::services::GetStringBundleService(); + NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); + nsCOMPtr<nsIStringBundle> bundle; + rv = bundleService->CreateBundle("chrome://messenger/locale/addressbook/addressBook.properties", getter_AddRefs(bundle)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString dirName; + aDirectory->GetDirName(dirName); + const char16_t *formatStrings[] = { dirName.get() }; + + nsString title; + rv = bundle->FormatStringFromName(u"ExportAddressBookNameTitle", formatStrings, + ArrayLength(formatStrings), getter_Copies(title)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = filePicker->Init(aParentWin, title, nsIFilePicker::modeSave); + NS_ENSURE_SUCCESS(rv, rv); + + filePicker->SetDefaultString(dirName); + + nsString filterString; + + // CSV: System charset and UTF-8. + rv = bundle->GetStringFromName(u"CSVFilesSysCharset", getter_Copies(filterString)); + NS_ENSURE_SUCCESS(rv, rv); + rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.csv")); + NS_ENSURE_SUCCESS(rv, rv); + rv = bundle->GetStringFromName(u"CSVFilesUTF8", getter_Copies(filterString)); + NS_ENSURE_SUCCESS(rv, rv); + rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.csv")); + NS_ENSURE_SUCCESS(rv, rv); + + // Tab separated: System charset and UTF-8. + rv = bundle->GetStringFromName(u"TABFilesSysCharset", getter_Copies(filterString)); + NS_ENSURE_SUCCESS(rv, rv); + rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.tab; *.txt")); + NS_ENSURE_SUCCESS(rv, rv); + rv = bundle->GetStringFromName(u"TABFilesUTF8", getter_Copies(filterString)); + NS_ENSURE_SUCCESS(rv, rv); + rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.tab; *.txt")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = bundle->GetStringFromName(u"VCFFiles", getter_Copies(filterString)); + NS_ENSURE_SUCCESS(rv, rv); + rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.vcf")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = bundle->GetStringFromName(u"LDIFFiles", getter_Copies(filterString)); + NS_ENSURE_SUCCESS(rv, rv); + rv = filePicker->AppendFilter(filterString, NS_LITERAL_STRING("*.ldi; *.ldif")); + NS_ENSURE_SUCCESS(rv, rv); + + int16_t dialogResult; + filePicker->Show(&dialogResult); + + if (dialogResult == nsIFilePicker::returnCancel) + return rv; + + nsCOMPtr<nsIFile> localFile; + rv = filePicker->GetFile(getter_AddRefs(localFile)); + NS_ENSURE_SUCCESS(rv, rv); + + if (dialogResult == nsIFilePicker::returnReplace) { + // be extra safe and only delete when the file is really a file + bool isFile; + rv = localFile->IsFile(&isFile); + if (NS_SUCCEEDED(rv) && isFile) { + rv = localFile->Remove(false /* recursive delete */); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + // The type of export is determined by the drop-down in + // the file picker dialog. + int32_t exportType; + rv = filePicker->GetFilterIndex(&exportType); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoString fileName; + rv = localFile->GetLeafName(fileName); + NS_ENSURE_SUCCESS(rv, rv); + + switch ( exportType ) + { + default: + case LDIF_EXPORT_TYPE: // ldif + // If filename does not have the correct ext, add one. + if ((MsgFind(fileName, LDIF_FILE_EXTENSION, true, fileName.Length() - strlen(LDIF_FILE_EXTENSION)) == -1) && + (MsgFind(fileName, LDIF_FILE_EXTENSION2, true, fileName.Length() - strlen(LDIF_FILE_EXTENSION2)) == -1)) { + + // Add the extension and build a new localFile. + fileName.AppendLiteral(LDIF_FILE_EXTENSION2); + localFile->SetLeafName(fileName); + } + rv = ExportDirectoryToLDIF(aDirectory, localFile); + break; + + case CSV_EXPORT_TYPE: // csv + case CSV_EXPORT_TYPE_UTF8: + // If filename does not have the correct ext, add one. + if (MsgFind(fileName, CSV_FILE_EXTENSION, true, fileName.Length() - strlen(CSV_FILE_EXTENSION)) == -1) { + + // Add the extension and build a new localFile. + fileName.AppendLiteral(CSV_FILE_EXTENSION); + localFile->SetLeafName(fileName); + } + rv = ExportDirectoryToDelimitedText(aDirectory, CSV_DELIM, CSV_DELIM_LEN, localFile, + exportType==CSV_EXPORT_TYPE_UTF8); + break; + + case TAB_EXPORT_TYPE: // tab & text + case TAB_EXPORT_TYPE_UTF8: + // If filename does not have the correct ext, add one. + if ((MsgFind(fileName, TXT_FILE_EXTENSION, true, fileName.Length() - strlen(TXT_FILE_EXTENSION)) == -1) && + (MsgFind(fileName, TAB_FILE_EXTENSION, true, fileName.Length() - strlen(TAB_FILE_EXTENSION)) == -1)) { + + // Add the extension and build a new localFile. + fileName.AppendLiteral(TXT_FILE_EXTENSION); + localFile->SetLeafName(fileName); + } + rv = ExportDirectoryToDelimitedText(aDirectory, TAB_DELIM, TAB_DELIM_LEN, localFile, + exportType==TAB_EXPORT_TYPE_UTF8); + break; + + case VCF_EXPORT_TYPE: // vCard + // If filename does not have the correct ext, add one. + if (MsgFind(fileName, VCF_FILE_EXTENSION, true, fileName.Length() - strlen(VCF_FILE_EXTENSION)) == -1) { + + // Add the extension and build a new localFile. + fileName.AppendLiteral(VCF_FILE_EXTENSION); + localFile->SetLeafName(fileName); + } + rv = ExportDirectoryToVCard(aDirectory, localFile); + break; + }; + + return rv; +} + +nsresult +nsAbManager::ExportDirectoryToDelimitedText(nsIAbDirectory *aDirectory, + const char *aDelim, + uint32_t aDelimLen, + nsIFile *aLocalFile, + bool useUTF8) +{ + nsCOMPtr <nsISimpleEnumerator> cardsEnumerator; + nsCOMPtr <nsIAbCard> card; + + nsresult rv; + + nsCOMPtr <nsIOutputStream> outputStream; + rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), + aLocalFile, + PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, + 0664); + + // the desired file may be read only + if (NS_FAILED(rv)) + return rv; + + uint32_t i; + uint32_t writeCount; + uint32_t length; + + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::services::GetStringBundleService(); + NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); + + nsCOMPtr<nsIStringBundle> bundle; + rv = bundleService->CreateBundle("chrome://messenger/locale/importMsgs.properties", getter_AddRefs(bundle)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString revisedName; + nsString columnName; + + for (i = 0; i < ArrayLength(EXPORT_ATTRIBUTES_TABLE); i++) { + if (EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID != 0) { + + // We don't need to truncate the string here as getter_Copies will + // do that for us. + if (NS_FAILED(bundle->GetStringFromID(EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID, getter_Copies(columnName)))) + columnName.AppendInt(EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID); + + rv = nsMsgI18NConvertFromUnicode(useUTF8 ? "UTF-8" : nsMsgI18NFileSystemCharset(), + columnName, revisedName); + NS_ENSURE_SUCCESS(rv,rv); + + rv = outputStream->Write(revisedName.get(), + revisedName.Length(), + &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + + if (revisedName.Length() != writeCount) + return NS_ERROR_FAILURE; + + if (i < ArrayLength(EXPORT_ATTRIBUTES_TABLE) - 1) { + rv = outputStream->Write(aDelim, aDelimLen, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + + if (aDelimLen != writeCount) + return NS_ERROR_FAILURE; + } + } + } + rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (MSG_LINEBREAK_LEN != writeCount) + return NS_ERROR_FAILURE; + + rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator)); + if (NS_SUCCEEDED(rv) && cardsEnumerator) { + nsCOMPtr<nsISupports> item; + bool more; + while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more) { + rv = cardsEnumerator->GetNext(getter_AddRefs(item)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + bool isMailList; + rv = card->GetIsMailList(&isMailList); + NS_ENSURE_SUCCESS(rv,rv); + + + if (isMailList) { + // .tab, .txt and .csv aren't able to export mailing lists + // use LDIF for that. + } + else { + nsString value; + nsCString valueCStr; + + for (i = 0; i < ArrayLength(EXPORT_ATTRIBUTES_TABLE); i++) { + if (EXPORT_ATTRIBUTES_TABLE[i].plainTextStringID != 0) { + rv = card->GetPropertyAsAString(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName, value); + if (NS_FAILED(rv)) + value.Truncate(); + + // If a string contains at least one comma, tab or double quote then + // we need to quote the entire string. Also if double quote is part + // of the string we need to quote the double quote(s) as well. + nsAutoString newValue(value); + bool needsQuotes = false; + if(newValue.FindChar('"') != -1) + { + needsQuotes = true; + + int32_t match = 0; + uint32_t offset = 0; + nsString oldSubstr = NS_LITERAL_STRING("\""); + nsString newSubstr = NS_LITERAL_STRING("\"\""); + while (offset < newValue.Length()) { + match = newValue.Find(oldSubstr, offset); + if (match == -1) + break; + + newValue.Replace(offset + match, oldSubstr.Length(), newSubstr); + offset += (match + newSubstr.Length()); + } + } + if (!needsQuotes && (newValue.FindChar(',') != -1 || newValue.FindChar('\x09') != -1)) + needsQuotes = true; + + // Make sure we quote if containing CR/LF. + if (newValue.FindChar('\r') != -1 || + newValue.FindChar('\n') != -1) + needsQuotes = true; + + if (needsQuotes) + { + newValue.Insert(NS_LITERAL_STRING("\""), 0); + newValue.AppendLiteral("\""); + } + + rv = nsMsgI18NConvertFromUnicode(useUTF8 ? "UTF-8" : nsMsgI18NFileSystemCharset(), + newValue, valueCStr); + NS_ENSURE_SUCCESS(rv,rv); + + if (NS_FAILED(rv)) { + NS_ERROR("failed to convert string to system charset. use LDIF"); + valueCStr = "?"; + } + + length = valueCStr.Length(); + if (length) { + rv = outputStream->Write(valueCStr.get(), length, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (length != writeCount) + return NS_ERROR_FAILURE; + } + valueCStr = ""; + } + else { + // something we don't support for the current export + // for example, .tab doesn't export preferred html format + continue; // go to next field + } + + if (i < ArrayLength(EXPORT_ATTRIBUTES_TABLE) - 1) { + rv = outputStream->Write(aDelim, aDelimLen, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (aDelimLen != writeCount) + return NS_ERROR_FAILURE; + } + } + + // write out the linebreak that separates the cards + rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (MSG_LINEBREAK_LEN != writeCount) + return NS_ERROR_FAILURE; + } + } + } + } + + rv = outputStream->Flush(); + NS_ENSURE_SUCCESS(rv,rv); + + rv = outputStream->Close(); + NS_ENSURE_SUCCESS(rv,rv); + return NS_OK; +} + +nsresult +nsAbManager::ExportDirectoryToVCard(nsIAbDirectory *aDirectory, nsIFile *aLocalFile) +{ + nsCOMPtr <nsISimpleEnumerator> cardsEnumerator; + nsCOMPtr <nsIAbCard> card; + + nsresult rv; + + nsCOMPtr <nsIOutputStream> outputStream; + rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), + aLocalFile, + PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, + 0664); + + // the desired file may be read only + if (NS_FAILED(rv)) + return rv; + + uint32_t writeCount; + uint32_t length; + + rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator)); + if (NS_SUCCEEDED(rv) && cardsEnumerator) { + nsCOMPtr<nsISupports> item; + bool more; + while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more) { + rv = cardsEnumerator->GetNext(getter_AddRefs(item)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + bool isMailList; + rv = card->GetIsMailList(&isMailList); + NS_ENSURE_SUCCESS(rv,rv); + + if (isMailList) { + // we don't know how to export mailing lists to vcf + // use LDIF for that. + } + else { + nsCString escapedValue; + rv = card->TranslateTo(NS_LITERAL_CSTRING("vcard"), escapedValue); + NS_ENSURE_SUCCESS(rv,rv); + + nsCString valueCStr; + MsgUnescapeString(escapedValue, 0, valueCStr); + + length = valueCStr.Length(); + rv = outputStream->Write(valueCStr.get(), length, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (length != writeCount) + return NS_ERROR_FAILURE; + } + } + } + } + + rv = outputStream->Flush(); + NS_ENSURE_SUCCESS(rv,rv); + + rv = outputStream->Close(); + NS_ENSURE_SUCCESS(rv,rv); + return NS_OK; +} + + +nsresult +nsAbManager::ExportDirectoryToLDIF(nsIAbDirectory *aDirectory, nsIFile *aLocalFile) +{ + nsCOMPtr <nsISimpleEnumerator> cardsEnumerator; + nsCOMPtr <nsIAbCard> card; + + nsresult rv; + + nsCOMPtr <nsIOutputStream> outputStream; + rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), + aLocalFile, + PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE, + 0664); + + // the desired file may be read only + if (NS_FAILED(rv)) + return rv; + + // Get the default attribute map for ldap. We use the default attribute + // map rather than one for a specific server because if people want an + // ldif export using a servers specific schema, then they can use ldapsearch + nsCOMPtr<nsIAbLDAPAttributeMapService> mapSrv = + do_GetService("@mozilla.org/addressbook/ldap-attribute-map-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbLDAPAttributeMap> attrMap; + rv = mapSrv->GetMapForPrefBranch(NS_LITERAL_CSTRING("ldap_2.servers.default.attrmap"), + getter_AddRefs(attrMap)); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t i; + uint32_t writeCount; + uint32_t length; + + rv = aDirectory->GetChildCards(getter_AddRefs(cardsEnumerator)); + if (NS_SUCCEEDED(rv) && cardsEnumerator) { + nsCOMPtr<nsISupports> item; + bool more; + while (NS_SUCCEEDED(cardsEnumerator->HasMoreElements(&more)) && more) { + rv = cardsEnumerator->GetNext(getter_AddRefs(item)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr <nsIAbCard> card = do_QueryInterface(item, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + bool isMailList; + rv = card->GetIsMailList(&isMailList); + NS_ENSURE_SUCCESS(rv,rv); + + if (isMailList) { + nsCString mailListCStr; + + rv = AppendLDIFForMailList(card, attrMap, mailListCStr); + NS_ENSURE_SUCCESS(rv,rv); + + length = mailListCStr.Length(); + rv = outputStream->Write(mailListCStr.get(), length, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (length != writeCount) + return NS_ERROR_FAILURE; + } + else { + nsString value; + nsCString valueCStr; + + rv = AppendBasicLDIFForCard(card, attrMap, valueCStr); + NS_ENSURE_SUCCESS(rv,rv); + + length = valueCStr.Length(); + rv = outputStream->Write(valueCStr.get(), length, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (length != writeCount) + return NS_ERROR_FAILURE; + + valueCStr.Truncate(); + + nsAutoCString ldapAttribute; + + for (i = 0; i < ArrayLength(EXPORT_ATTRIBUTES_TABLE); i++) { + if (NS_SUCCEEDED(attrMap->GetFirstAttribute(nsDependentCString(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName), + ldapAttribute)) && + !ldapAttribute.IsEmpty()) { + + rv = card->GetPropertyAsAString(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName, value); + if (NS_FAILED(rv)) + value.Truncate(); + + if (!PL_strcmp(EXPORT_ATTRIBUTES_TABLE[i].abPropertyName, kPreferMailFormatProperty)) { + if (value.EqualsLiteral("html")) + value.AssignLiteral("true"); + else if (value.EqualsLiteral("plaintext")) + value.AssignLiteral("false"); + else + value.Truncate(); // unknown. + } + + if (!value.IsEmpty()) { + rv = AppendProperty(ldapAttribute.get(), value.get(), valueCStr); + NS_ENSURE_SUCCESS(rv,rv); + + valueCStr += MSG_LINEBREAK; + } + else + valueCStr.Truncate(); + + length = valueCStr.Length(); + if (length) { + rv = outputStream->Write(valueCStr.get(), length, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (length != writeCount) + return NS_ERROR_FAILURE; + } + valueCStr.Truncate(); + } + else { + // something we don't support yet + // ldif doesn't export multiple addresses + } + } + + // write out the linebreak that separates the cards + rv = outputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &writeCount); + NS_ENSURE_SUCCESS(rv,rv); + if (MSG_LINEBREAK_LEN != writeCount) + return NS_ERROR_FAILURE; + } + } + } + } + + rv = outputStream->Flush(); + NS_ENSURE_SUCCESS(rv,rv); + + rv = outputStream->Close(); + NS_ENSURE_SUCCESS(rv,rv); + return NS_OK; +} + +nsresult nsAbManager::AppendLDIFForMailList(nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult) +{ + nsresult rv; + nsString attrValue; + + rv = AppendDNForCard("dn", aCard, aAttrMap, aResult); + NS_ENSURE_SUCCESS(rv,rv); + + aResult += MSG_LINEBREAK \ + "objectclass: top" MSG_LINEBREAK \ + "objectclass: groupOfNames" MSG_LINEBREAK; + + rv = aCard->GetDisplayName(attrValue); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoCString ldapAttributeName; + + rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kDisplayNameProperty), + ldapAttributeName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = AppendProperty(ldapAttributeName.get(), attrValue.get(), aResult); + NS_ENSURE_SUCCESS(rv,rv); + aResult += MSG_LINEBREAK; + + rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kNicknameProperty), + ldapAttributeName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aCard->GetPropertyAsAString(kNicknameProperty, attrValue); + if (NS_SUCCEEDED(rv) && !attrValue.IsEmpty()) { + rv = AppendProperty(ldapAttributeName.get(), attrValue.get(), aResult); + NS_ENSURE_SUCCESS(rv,rv); + aResult += MSG_LINEBREAK; + } + + rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kNotesProperty), + ldapAttributeName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aCard->GetPropertyAsAString(kNotesProperty, attrValue); + if (NS_SUCCEEDED(rv) && !attrValue.IsEmpty()) { + rv = AppendProperty(ldapAttributeName.get(), attrValue.get(), aResult); + NS_ENSURE_SUCCESS(rv,rv); + aResult += MSG_LINEBREAK; + } + + nsCString mailListURI; + rv = aCard->GetMailListURI(getter_Copies(mailListURI)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr <nsIAbDirectory> mailList; + rv = GetDirectory(mailListURI, getter_AddRefs(mailList)); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr<nsIMutableArray> addresses; + rv = mailList->GetAddressLists(getter_AddRefs(addresses)); + if (addresses) { + uint32_t total = 0; + addresses->GetLength(&total); + if (total) { + uint32_t i; + for (i = 0; i < total; i++) { + nsCOMPtr <nsIAbCard> listCard = do_QueryElementAt(addresses, i, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + rv = AppendDNForCard("member", listCard, aAttrMap, aResult); + NS_ENSURE_SUCCESS(rv,rv); + + aResult += MSG_LINEBREAK; + } + } + } + + aResult += MSG_LINEBREAK; + return NS_OK; +} + +nsresult nsAbManager::AppendDNForCard(const char *aProperty, nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult) +{ + nsString email; + nsString displayName; + nsAutoCString ldapAttributeName; + + nsresult rv = aCard->GetPrimaryEmail(email); + NS_ENSURE_SUCCESS(rv,rv); + + rv = aCard->GetDisplayName(displayName); + NS_ENSURE_SUCCESS(rv,rv); + + nsString cnStr; + + rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kDisplayNameProperty), + ldapAttributeName); + NS_ENSURE_SUCCESS(rv, rv); + + if (!displayName.IsEmpty()) { + cnStr += NS_ConvertUTF8toUTF16(ldapAttributeName).get(); + cnStr.AppendLiteral("="); + cnStr.Append(displayName); + if (!email.IsEmpty()) { + cnStr.AppendLiteral(","); + } + } + + rv = aAttrMap->GetFirstAttribute(NS_LITERAL_CSTRING(kPriEmailProperty), + ldapAttributeName); + NS_ENSURE_SUCCESS(rv, rv); + + if (!email.IsEmpty()) { + cnStr += NS_ConvertUTF8toUTF16(ldapAttributeName).get(); + cnStr.AppendLiteral("="); + cnStr.Append(email); + } + + rv = AppendProperty(aProperty, cnStr.get(), aResult); + NS_ENSURE_SUCCESS(rv,rv); + return rv; +} + +nsresult nsAbManager::AppendBasicLDIFForCard(nsIAbCard *aCard, nsIAbLDAPAttributeMap *aAttrMap, nsACString &aResult) +{ + nsresult rv = AppendDNForCard("dn", aCard, aAttrMap, aResult); + NS_ENSURE_SUCCESS(rv,rv); + aResult += MSG_LINEBREAK \ + "objectclass: top" MSG_LINEBREAK \ + "objectclass: person" MSG_LINEBREAK \ + "objectclass: organizationalPerson" MSG_LINEBREAK \ + "objectclass: inetOrgPerson" MSG_LINEBREAK \ + "objectclass: " MOZ_AB_OBJECTCLASS MSG_LINEBREAK; + return rv; +} + +bool nsAbManager::IsSafeLDIFString(const char16_t *aStr) +{ + // follow RFC 2849 to determine if something is safe "as is" for LDIF + if (aStr[0] == char16_t(' ') || + aStr[0] == char16_t(':') || + aStr[0] == char16_t('<')) + return false; + + uint32_t i; + uint32_t len = NS_strlen(aStr); + for (i=0; i<len; i++) { + // If string contains CR or LF, it is not safe for LDIF + // and MUST be base64 encoded + if ((aStr[i] == char16_t('\n')) || + (aStr[i] == char16_t('\r')) || + (!NS_IsAscii(aStr[i]))) + return false; + } + return true; +} + +nsresult nsAbManager::AppendProperty(const char *aProperty, const char16_t *aValue, nsACString &aResult) +{ + NS_ENSURE_ARG_POINTER(aValue); + + aResult += aProperty; + + // if the string is not safe "as is", base64 encode it + if (IsSafeLDIFString(aValue)) { + aResult.AppendLiteral(": "); + aResult.Append(NS_LossyConvertUTF16toASCII(aValue)); + } + else { + char *base64Str = PL_Base64Encode(NS_ConvertUTF16toUTF8(aValue).get(), 0, nullptr); + if (!base64Str) + return NS_ERROR_OUT_OF_MEMORY; + + aResult.AppendLiteral(":: "); + aResult.Append(nsDependentCString(base64Str)); + PR_Free(base64Str); + } + + return NS_OK; +} + +char *getCString(VObject *vObj) +{ + if (VALUE_TYPE(vObj) == VCVT_USTRINGZ) + return fakeCString(vObjectUStringZValue(vObj)); + if (VALUE_TYPE(vObj) == VCVT_STRINGZ) + return PL_strdup(vObjectStringZValue(vObj)); + return NULL; +} + +static void convertNameValue(VObject *vObj, nsIAbCard *aCard) +{ + const char *cardPropName = NULL; + + // if the vCard property is not a root property then we need to determine its exact property. + // a good example of this is VCTelephoneProp, this prop has four objects underneath it: + // fax, work and home and cellular. + if (PL_strcasecmp(VCCityProp, vObjectName(vObj)) == 0) + cardPropName = kWorkCityProperty; + else if (PL_strcasecmp(VCTelephoneProp, vObjectName(vObj)) == 0) + { + if (isAPropertyOf(vObj, VCFaxProp)) + cardPropName = kFaxProperty; + else if (isAPropertyOf(vObj, VCWorkProp)) + cardPropName = kWorkPhoneProperty; + else if (isAPropertyOf(vObj, VCHomeProp)) + cardPropName = kHomePhoneProperty; + else if (isAPropertyOf(vObj, VCCellularProp)) + cardPropName = kCellularProperty; + else if (isAPropertyOf(vObj, VCPagerProp)) + cardPropName = kPagerProperty; + else + return; + } + else if (PL_strcasecmp(VCEmailAddressProp, vObjectName(vObj)) == 0) + cardPropName = kPriEmailProperty; + else if (PL_strcasecmp(VCFamilyNameProp, vObjectName(vObj)) == 0) + cardPropName = kLastNameProperty; + else if (PL_strcasecmp(VCFullNameProp, vObjectName(vObj)) == 0) + cardPropName = kDisplayNameProperty; + else if (PL_strcasecmp(VCGivenNameProp, vObjectName(vObj)) == 0) + cardPropName = kFirstNameProperty; + else if (PL_strcasecmp(VCOrgNameProp, vObjectName(vObj)) == 0) + cardPropName = kCompanyProperty; + else if (PL_strcasecmp(VCOrgUnitProp, vObjectName(vObj)) == 0) + cardPropName = kDepartmentProperty; + else if (PL_strcasecmp(VCPostalCodeProp, vObjectName(vObj)) == 0) + cardPropName = kWorkZipCodeProperty; + else if (PL_strcasecmp(VCRegionProp, vObjectName(vObj)) == 0) + cardPropName = kWorkStateProperty; + else if (PL_strcasecmp(VCStreetAddressProp, vObjectName(vObj)) == 0) + cardPropName = kWorkAddressProperty; + else if (PL_strcasecmp(VCPostalBoxProp, vObjectName(vObj)) == 0) + cardPropName = kWorkAddress2Property; + else if (PL_strcasecmp(VCCountryNameProp, vObjectName(vObj)) == 0) + cardPropName = kWorkCountryProperty; + else if (PL_strcasecmp(VCTitleProp, vObjectName(vObj)) == 0) + cardPropName = kJobTitleProperty; + else if (PL_strcasecmp(VCUseHTML, vObjectName(vObj)) == 0) + cardPropName = kPreferMailFormatProperty; + else if (PL_strcasecmp(VCNoteProp, vObjectName(vObj)) == 0) + cardPropName = kNotesProperty; + else if (PL_strcasecmp(VCURLProp, vObjectName(vObj)) == 0) + cardPropName = kWorkWebPageProperty; + else + return; + + if (!VALUE_TYPE(vObj)) + return; + + char *cardPropValue = getCString(vObj); + if (PL_strcmp(cardPropName, kPreferMailFormatProperty)) { + aCard->SetPropertyAsAUTF8String(cardPropName, nsDependentCString(cardPropValue)); + } else { + if (!PL_strcmp(cardPropValue, "TRUE")) + aCard->SetPropertyAsUint32(cardPropName, nsIAbPreferMailFormat::html); + else if (!PL_strcmp(cardPropValue, "FALSE")) + aCard->SetPropertyAsUint32(cardPropName, nsIAbPreferMailFormat::plaintext); + else + aCard->SetPropertyAsUint32(cardPropName, nsIAbPreferMailFormat::unknown); + } + PR_FREEIF(cardPropValue); + return; +} + +static void convertFromVObject(VObject *vObj, nsIAbCard *aCard) +{ + if (vObj) + { + VObjectIterator t; + + convertNameValue(vObj, aCard); + + initPropIterator(&t, vObj); + while (moreIteration(&t)) + { + VObject * nextObject = nextVObject(&t); + convertFromVObject(nextObject, aCard); + } + } + return; +} + +NS_IMETHODIMP nsAbManager::EscapedVCardToAbCard(const char *aEscapedVCardStr, nsIAbCard **aCard) +{ + NS_ENSURE_ARG_POINTER(aEscapedVCardStr); + NS_ENSURE_ARG_POINTER(aCard); + + nsCOMPtr <nsIAbCard> cardFromVCard = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID); + if (!cardFromVCard) + return NS_ERROR_FAILURE; + + // aEscapedVCardStr will be "" the first time, before you have a vCard + if (*aEscapedVCardStr != '\0') { + nsCString unescapedData; + MsgUnescapeString(nsDependentCString(aEscapedVCardStr), 0, unescapedData); + + VObject *vObj = parse_MIME(unescapedData.get(), unescapedData.Length()); + if (vObj) + { + convertFromVObject(vObj, cardFromVCard); + + cleanVObject(vObj); + } + else + NS_WARNING("Parse of vCard failed"); + } + + NS_IF_ADDREF(*aCard = cardFromVCard); + return NS_OK; +} + +NS_IMETHODIMP +nsAbManager::Handle(nsICommandLine* aCmdLine) +{ + nsresult rv; + bool found; + + rv = aCmdLine->HandleFlag(NS_LITERAL_STRING("addressbook"), false, &found); + NS_ENSURE_SUCCESS(rv, rv); + + if (!found) + return NS_OK; + + nsCOMPtr<nsIWindowWatcher> wwatch (do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + NS_ENSURE_TRUE(wwatch, NS_ERROR_FAILURE); + + nsCOMPtr<mozIDOMWindowProxy> opened; + wwatch->OpenWindow(nullptr, + "chrome://messenger/content/addressbook/addressbook.xul", + "_blank", + "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar", + nullptr, getter_AddRefs(opened)); + aCmdLine->SetPreventDefault(true); + return NS_OK; +} + +NS_IMETHODIMP +nsAbManager::GetHelpInfo(nsACString& aResult) +{ + aResult.Assign(NS_LITERAL_CSTRING(" -addressbook Open the address book at startup.\n")); + return NS_OK; +} + +NS_IMETHODIMP +nsAbManager::GenerateUUID(const nsACString &aDirectoryId, + const nsACString &aLocalId, nsACString &uuid) +{ + uuid.Assign(aDirectoryId); + uuid.Append('#'); + uuid.Append(aLocalId); + return NS_OK; +} + +NS_IMETHODIMP +nsAbManager::ConvertQueryStringToExpression(const nsACString &aQueryString, + nsIAbBooleanExpression **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + return nsAbQueryStringToExpression::Convert(aQueryString, + _retval); +} |