diff options
Diffstat (limited to 'mailnews/addrbook/src/nsAbOutlookDirectory.cpp')
-rw-r--r-- | mailnews/addrbook/src/nsAbOutlookDirectory.cpp | 1539 |
1 files changed, 1539 insertions, 0 deletions
diff --git a/mailnews/addrbook/src/nsAbOutlookDirectory.cpp b/mailnews/addrbook/src/nsAbOutlookDirectory.cpp new file mode 100644 index 000000000..0d39d1d17 --- /dev/null +++ b/mailnews/addrbook/src/nsAbOutlookDirectory.cpp @@ -0,0 +1,1539 @@ +/* -*- 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 "nsAbOutlookDirectory.h" +#include "nsAbWinHelper.h" + +#include "nsAbBaseCID.h" +#include "nsIAbCard.h" +#include "nsStringGlue.h" +#include "nsAbDirectoryQuery.h" +#include "nsIAbBooleanExpression.h" +#include "nsIAbManager.h" +#include "nsIAbMDBDirectory.h" +#include "nsAbQueryStringToExpression.h" +#include "nsAbUtils.h" +#include "nsEnumeratorUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/Logging.h" +#include "prthread.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsCRTGlue.h" +#include "nsArrayUtils.h" +#include "nsArrayEnumerator.h" +#include "nsMsgUtils.h" + +#ifdef PR_LOGGING +static PRLogModuleInfo* gAbOutlookDirectoryLog + = PR_NewLogModule("nsAbOutlookDirectoryLog"); +#endif + +#define PRINTF(args) MOZ_LOG(gAbOutlookDirectoryLog, mozilla::LogLevel::Debug, args) + +nsAbOutlookDirectory::nsAbOutlookDirectory(void) + : nsAbDirProperty(), + mCurrentQueryId(0), mSearchContext(-1), + mAbWinType(nsAbWinType_Unknown), mMapiData(nullptr) +{ + mMapiData = new nsMapiEntry ; + mProtector = PR_NewLock() ; +} + +nsAbOutlookDirectory::~nsAbOutlookDirectory(void) +{ + if (mMapiData) { delete mMapiData ; } + if (mProtector) { PR_DestroyLock(mProtector) ; } +} + +NS_IMPL_ISUPPORTS_INHERITED(nsAbOutlookDirectory, nsAbDirProperty, + nsIAbDirectoryQuery, nsIAbDirectorySearch, + nsIAbDirSearchListener) + +NS_IMETHODIMP nsAbOutlookDirectory::Init(const char *aUri) +{ + nsresult rv = nsAbDirProperty::Init(aUri); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString entry; + nsAutoCString stub; + + mAbWinType = getAbWinType(kOutlookDirectoryScheme, mURINoQuery.get(), stub, entry); + if (mAbWinType == nsAbWinType_Unknown) { + PRINTF(("Huge problem URI=%s.\n", mURINoQuery)); + return NS_ERROR_INVALID_ARG; + } + nsAbWinHelperGuard mapiAddBook (mAbWinType); + nsString prefix; + nsAutoString unichars; + ULONG objectType = 0; + + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + + mMapiData->Assign(entry); + if (!mapiAddBook->GetPropertyLong(*mMapiData, PR_OBJECT_TYPE, objectType)) { + PRINTF(("Cannot get type.\n")); + return NS_ERROR_FAILURE; + } + if (!mapiAddBook->GetPropertyUString(*mMapiData, PR_DISPLAY_NAME_W, unichars)) { + PRINTF(("Cannot get name.\n")); + return NS_ERROR_FAILURE; + } + + if (mAbWinType == nsAbWinType_Outlook) + prefix.AssignLiteral("OP "); + else + prefix.AssignLiteral("OE "); + prefix.Append(unichars); + + if (objectType == MAPI_DISTLIST) { + m_IsMailList = true; + SetDirName(unichars); + } + else { + m_IsMailList = false; + SetDirName(prefix); + } + + return UpdateAddressList(); +} + +// nsIAbDirectory methods + +NS_IMETHODIMP nsAbOutlookDirectory::GetDirType(int32_t *aDirType) +{ + NS_ENSURE_ARG_POINTER(aDirType); + *aDirType = MAPIDirectory; + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::GetURI(nsACString &aURI) +{ + if (mURI.IsEmpty()) + return NS_ERROR_NOT_INITIALIZED; + + aURI = mURI; + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::GetChildNodes(nsISimpleEnumerator **aNodes) +{ + NS_ENSURE_ARG_POINTER(aNodes); + + *aNodes = nullptr; + + if (mIsQueryURI) { + return NS_NewEmptyEnumerator(aNodes); + } + + nsresult rv; + nsCOMPtr<nsIMutableArray> nodeList(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetChildNodes(nodeList); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewArrayEnumerator(aNodes, nodeList); +} + +NS_IMETHODIMP nsAbOutlookDirectory::GetChildCards(nsISimpleEnumerator **aCards) +{ + NS_ENSURE_ARG_POINTER(aCards); + *aCards = nullptr; + + nsresult rv; + nsCOMPtr<nsIMutableArray> cardList(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + mCardList.Clear(); + + rv = mIsQueryURI ? StartSearch() : GetChildCards(cardList, nullptr); + + NS_ENSURE_SUCCESS(rv, rv); + if (!m_AddressList) + { + m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Fill the results array and update the card list + // Also update the address list and notify any changes. + uint32_t nbCards = 0; + + NS_NewArrayEnumerator(aCards, cardList); + cardList->GetLength(&nbCards); + + nsCOMPtr<nsIAbCard> card; + nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, + &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + + for (uint32_t i = 0; i < nbCards; ++i) + { + card = do_QueryElementAt(cardList, i, &rv); + if (NS_FAILED(rv)) + continue; + + if (!mCardList.Get(card, nullptr)) + { + // We are dealing with a new element (probably directly + // added from Outlook), we may need to sync m_AddressList + mCardList.Put(card, card); + + bool isMailList = false; + + rv = card->GetIsMailList(&isMailList); + NS_ENSURE_SUCCESS(rv, rv); + if (isMailList) + { + // We can have mailing lists only in folder, + // we must add the directory to m_AddressList + nsCString mailListUri; + rv = card->GetMailListURI(getter_Copies(mailListUri)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIAbDirectory> mailList; + rv = abManager->GetDirectory(mailListUri, getter_AddRefs(mailList)); + NS_ENSURE_SUCCESS(rv, rv); + + m_AddressList->AppendElement(mailList, false); + NotifyItemAddition(mailList); + } + else if (m_IsMailList) + { + m_AddressList->AppendElement(card, false); + NotifyItemAddition(card); + } + } + } + return rv; +} + +NS_IMETHODIMP nsAbOutlookDirectory::GetIsQuery(bool *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = mIsQueryURI; + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::HasCard(nsIAbCard *aCard, bool *aHasCard) +{ + if (!aCard || !aHasCard) + return NS_ERROR_NULL_POINTER; + + *aHasCard = mCardList.Get(aCard, nullptr); + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::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; +} + + +static nsresult ExtractCardEntry(nsIAbCard *aCard, nsCString& aEntry) +{ + aEntry.Truncate(); + + nsCString uri; + aCard->GetPropertyAsAUTF8String("OutlookEntryURI", uri); + + // If we don't have a URI, uri will be empty. getAbWinType doesn't set + // aEntry to anything if uri is empty, so it will be truncated, allowing us + // to accept cards not initialized by us. + nsAutoCString stub; + getAbWinType(kOutlookCardScheme, uri.get(), stub, aEntry); + return NS_OK; +} + +static nsresult ExtractDirectoryEntry(nsIAbDirectory *aDirectory, nsCString& aEntry) +{ + aEntry.Truncate(); + nsCString uri; + nsresult rv = aDirectory->GetURI(uri); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString stub; + nsAbWinType objType = getAbWinType(kOutlookDirectoryScheme, uri.get(), stub, aEntry); + + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::DeleteCards(nsIArray *aCardList) +{ + if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; } + if (!aCardList) { return NS_ERROR_NULL_POINTER ; } + uint32_t nbCards = 0 ; + nsresult retCode = NS_OK ; + nsAbWinHelperGuard mapiAddBook (mAbWinType) ; + + if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; } + + retCode = aCardList->GetLength(&nbCards); + NS_ENSURE_SUCCESS(retCode, retCode) ; + uint32_t i = 0 ; + nsAutoCString entryString ; + nsMapiEntry cardEntry ; + + for (i = 0 ; i < nbCards ; ++ i) { + nsCOMPtr<nsIAbCard> card(do_QueryElementAt(aCardList, i, &retCode)); + NS_ENSURE_SUCCESS(retCode, retCode); + + retCode = ExtractCardEntry(card, entryString) ; + if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) { + card->SetDirectoryId(EmptyCString()); + + cardEntry.Assign(entryString) ; + if (!mapiAddBook->DeleteEntry(*mMapiData, cardEntry)) { + PRINTF(("Cannot delete card %s.\n", entryString.get())) ; + } + else { + mCardList.Remove(card); + if (m_IsMailList && m_AddressList) + { + uint32_t pos; + if (NS_SUCCEEDED(m_AddressList->IndexOf(0, card, &pos))) + m_AddressList->RemoveElementAt(pos); + } + retCode = NotifyItemDeletion(card); + NS_ENSURE_SUCCESS(retCode, retCode) ; + } + } + else { + PRINTF(("Card doesn't belong in this directory.\n")) ; + } + } + return NS_OK ; +} + +NS_IMETHODIMP nsAbOutlookDirectory::DeleteDirectory(nsIAbDirectory *aDirectory) +{ + if (mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; } + if (!aDirectory) { return NS_ERROR_NULL_POINTER ; } + nsresult retCode = NS_OK ; + nsAbWinHelperGuard mapiAddBook (mAbWinType) ; + nsAutoCString entryString ; + + if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; } + retCode = ExtractDirectoryEntry(aDirectory, entryString) ; + if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) { + nsMapiEntry directoryEntry ; + + directoryEntry.Assign(entryString) ; + if (!mapiAddBook->DeleteEntry(*mMapiData, directoryEntry)) { + PRINTF(("Cannot delete directory %s.\n", entryString.get())) ; + } + else { + uint32_t pos; + if (m_AddressList && NS_SUCCEEDED(m_AddressList->IndexOf(0, aDirectory, &pos))) + m_AddressList->RemoveElementAt(pos); + + retCode = NotifyItemDeletion(aDirectory); + NS_ENSURE_SUCCESS(retCode, retCode); + } + } + else { + PRINTF(("Directory doesn't belong to this folder.\n")) ; + } + return retCode ; +} + +NS_IMETHODIMP nsAbOutlookDirectory::AddCard(nsIAbCard *aData, nsIAbCard **addedCard) +{ + if (mIsQueryURI) + return NS_ERROR_NOT_IMPLEMENTED; + + NS_ENSURE_ARG_POINTER(aData); + + nsresult retCode = NS_OK ; + bool hasCard = false ; + + retCode = HasCard(aData, &hasCard) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + if (hasCard) { + PRINTF(("Has card.\n")) ; + NS_IF_ADDREF(*addedCard = aData); + return NS_OK ; + } + retCode = CreateCard(aData, addedCard) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + + mCardList.Put(*addedCard, *addedCard); + + if (!m_AddressList) + { + m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &retCode); + NS_ENSURE_SUCCESS(retCode, retCode); + } + + if (m_IsMailList) + m_AddressList->AppendElement(*addedCard, false); + NotifyItemAddition(*addedCard) ; + return retCode ; +} + +NS_IMETHODIMP nsAbOutlookDirectory::DropCard(nsIAbCard *aData, bool needToCopyCard) +{ + nsCOMPtr <nsIAbCard> addedCard; + return AddCard(aData, getter_AddRefs(addedCard)); +} + +NS_IMETHODIMP nsAbOutlookDirectory::AddMailList(nsIAbDirectory *aMailList, nsIAbDirectory **addedList) +{ + if (mIsQueryURI) + return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_ARG_POINTER(aMailList); + NS_ENSURE_ARG_POINTER(addedList); + if (m_IsMailList) + return NS_OK; + nsAbWinHelperGuard mapiAddBook (mAbWinType); + nsAutoCString entryString; + nsMapiEntry newEntry; + bool didCopy = false; + + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + nsresult rv = ExtractDirectoryEntry(aMailList, entryString); + if (NS_SUCCEEDED(rv) && !entryString.IsEmpty()) + { + nsMapiEntry sourceEntry; + + sourceEntry.Assign(entryString); + mapiAddBook->CopyEntry(*mMapiData, sourceEntry, newEntry); + } + if (newEntry.mByteCount == 0) + { + if (!mapiAddBook->CreateDistList(*mMapiData, newEntry)) + return NS_ERROR_FAILURE; + } + else { + didCopy = true; + } + newEntry.ToString(entryString); + nsAutoCString uri; + + buildAbWinUri(kOutlookDirectoryScheme, mAbWinType, uri); + uri.Append(entryString); + + nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, + &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIAbDirectory> newList; + rv = abManager->GetDirectory(uri, getter_AddRefs(newList)); + NS_ENSURE_SUCCESS(rv, rv); + + if (!didCopy) + { + rv = newList->CopyMailList(aMailList); + NS_ENSURE_SUCCESS(rv, rv); + rv = newList->EditMailListToDatabase(nullptr); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (!m_AddressList) + { + m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + m_AddressList->AppendElement(newList, false); + NotifyItemAddition(newList); + NS_IF_ADDREF(*addedList = newList); + + return rv; +} + +NS_IMETHODIMP nsAbOutlookDirectory::EditMailListToDatabase(nsIAbCard *listCard) +{ + if (mIsQueryURI) + return NS_ERROR_NOT_IMPLEMENTED; + + nsresult rv; + nsString name; + nsAbWinHelperGuard mapiAddBook(mAbWinType); + + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + + rv = GetDirName(name); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mapiAddBook->SetPropertyUString(*mMapiData, PR_DISPLAY_NAME_W, + name.get())) + return NS_ERROR_FAILURE; + + return CommitAddressList(); +} + +struct OutlookTableAttr +{ + const char *mOuterName ; + ULONG mMapiProp ; +} ; + +// Here, we are forced to use the Ascii versions of the properties +// instead of the widechar ones, because the content restriction +// operators do not work on unicode strings in mapi. +static const OutlookTableAttr OutlookTableStringToProp [] = +{ + {kFirstNameProperty, PR_GIVEN_NAME_A}, + {kLastNameProperty, PR_SURNAME_A}, + {kDisplayNameProperty, PR_DISPLAY_NAME_A}, + {kNicknameProperty, PR_NICKNAME_A}, + {kPriEmailProperty, PR_EMAIL_ADDRESS_A}, + {kWorkPhoneProperty, PR_BUSINESS_TELEPHONE_NUMBER_A}, + {kHomePhoneProperty, PR_HOME_TELEPHONE_NUMBER_A}, + {kFaxProperty, PR_BUSINESS_FAX_NUMBER_A}, + {kPagerProperty, PR_PAGER_TELEPHONE_NUMBER_A}, + {kCellularProperty, PR_MOBILE_TELEPHONE_NUMBER_A}, + {kHomeAddressProperty, PR_HOME_ADDRESS_STREET_A}, + {kHomeCityProperty, PR_HOME_ADDRESS_CITY_A}, + {kHomeStateProperty, PR_HOME_ADDRESS_STATE_OR_PROVINCE_A}, + {kHomeZipCodeProperty, PR_HOME_ADDRESS_POSTAL_CODE_A}, + {kHomeCountryProperty, PR_HOME_ADDRESS_COUNTRY_A}, + {kWorkAddressProperty, PR_BUSINESS_ADDRESS_STREET_A}, + {kWorkCityProperty, PR_BUSINESS_ADDRESS_CITY_A}, + {kWorkStateProperty, PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A}, + {kWorkZipCodeProperty, PR_BUSINESS_ADDRESS_POSTAL_CODE_A}, + {kWorkCountryProperty, PR_BUSINESS_ADDRESS_COUNTRY_A}, + {kJobTitleProperty, PR_TITLE_A}, + {kDepartmentProperty, PR_DEPARTMENT_NAME_A}, + {kCompanyProperty, PR_COMPANY_NAME_A}, + {kWorkWebPageProperty, PR_BUSINESS_HOME_PAGE_A}, + {kHomeWebPageProperty, PR_PERSONAL_HOME_PAGE_A}, + // For the moment, we don't support querying on the birthday + // sub-elements. +#if 0 + {kBirthYearProperty, PR_BIRTHDAY}, + {kBirthMonthProperty, PR_BIRTHDAY}, + {kBirthDayProperty, PR_BIRTHDAY}, +#endif // 0 + {kNotesProperty, PR_COMMENT_A} +} ; + +static const uint32_t OutlookTableNbProps = sizeof(OutlookTableStringToProp) / + sizeof(OutlookTableStringToProp [0]) ; + +static ULONG findPropertyTag(const char *aName) { + uint32_t i = 0 ; + + for (i = 0 ; i < OutlookTableNbProps ; ++ i) { + if (strcmp(aName, OutlookTableStringToProp [i].mOuterName) == 0) { + return OutlookTableStringToProp [i].mMapiProp ; + } + } + return 0 ; +} + +static nsresult BuildRestriction(nsIAbBooleanConditionString *aCondition, + SRestriction& aRestriction, + bool& aSkipItem) +{ + if (!aCondition) { return NS_ERROR_NULL_POINTER ; } + aSkipItem = false ; + nsAbBooleanConditionType conditionType = 0 ; + nsresult retCode = NS_OK ; + nsCString name; + nsString value; + ULONG propertyTag = 0 ; + nsAutoCString valueAscii ; + + retCode = aCondition->GetCondition(&conditionType) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + retCode = aCondition->GetName(getter_Copies(name)) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + retCode = aCondition->GetValue(getter_Copies(value)) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + LossyCopyUTF16toASCII(value, valueAscii); + propertyTag = findPropertyTag(name.get()) ; + if (propertyTag == 0) { + aSkipItem = true ; + return retCode ; + } + switch (conditionType) { + case nsIAbBooleanConditionTypes::Exists : + aRestriction.rt = RES_EXIST ; + aRestriction.res.resExist.ulPropTag = propertyTag ; + break ; + case nsIAbBooleanConditionTypes::DoesNotExist : + aRestriction.rt = RES_NOT ; + aRestriction.res.resNot.lpRes = new SRestriction ; + aRestriction.res.resNot.lpRes->rt = RES_EXIST ; + aRestriction.res.resNot.lpRes->res.resExist.ulPropTag = propertyTag ; + break ; + case nsIAbBooleanConditionTypes::Contains : + aRestriction.rt = RES_CONTENT ; + aRestriction.res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_LOOSE ; + aRestriction.res.resContent.ulPropTag = propertyTag ; + aRestriction.res.resContent.lpProp = new SPropValue ; + aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + case nsIAbBooleanConditionTypes::DoesNotContain : + aRestriction.rt = RES_NOT ; + aRestriction.res.resNot.lpRes = new SRestriction ; + aRestriction.res.resNot.lpRes->rt = RES_CONTENT ; + aRestriction.res.resNot.lpRes->res.resContent.ulFuzzyLevel = FL_SUBSTRING | FL_LOOSE ; + aRestriction.res.resNot.lpRes->res.resContent.ulPropTag = propertyTag ; + aRestriction.res.resNot.lpRes->res.resContent.lpProp = new SPropValue ; + aRestriction.res.resNot.lpRes->res.resContent.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resNot.lpRes->res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + case nsIAbBooleanConditionTypes::Is : + aRestriction.rt = RES_CONTENT ; + aRestriction.res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_LOOSE ; + aRestriction.res.resContent.ulPropTag = propertyTag ; + aRestriction.res.resContent.lpProp = new SPropValue ; + aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + case nsIAbBooleanConditionTypes::IsNot : + aRestriction.rt = RES_NOT ; + aRestriction.res.resNot.lpRes = new SRestriction ; + aRestriction.res.resNot.lpRes->rt = RES_CONTENT ; + aRestriction.res.resNot.lpRes->res.resContent.ulFuzzyLevel = FL_FULLSTRING | FL_LOOSE ; + aRestriction.res.resNot.lpRes->res.resContent.ulPropTag = propertyTag ; + aRestriction.res.resNot.lpRes->res.resContent.lpProp = new SPropValue ; + aRestriction.res.resNot.lpRes->res.resContent.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resNot.lpRes->res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + case nsIAbBooleanConditionTypes::BeginsWith : + aRestriction.rt = RES_CONTENT ; + aRestriction.res.resContent.ulFuzzyLevel = FL_PREFIX | FL_LOOSE ; + aRestriction.res.resContent.ulPropTag = propertyTag ; + aRestriction.res.resContent.lpProp = new SPropValue ; + aRestriction.res.resContent.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resContent.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + case nsIAbBooleanConditionTypes::EndsWith : + // This condition should be implemented through regular expressions, + // but MAPI doesn't match them correctly. +#if 0 + aRestriction.rt = RES_PROPERTY ; + aRestriction.res.resProperty.relop = RELOP_RE ; + aRestriction.res.resProperty.ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp = new SPropValue ; + aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ; +#else + aSkipItem = true ; +#endif // 0 + break ; + case nsIAbBooleanConditionTypes::SoundsLike : + // This condition cannot be implemented in MAPI. + aSkipItem = true ; + break ; + case nsIAbBooleanConditionTypes::RegExp : + // This condition should be implemented this way, but the following + // code will never match (through MAPI's fault). +#if 0 + aRestriction.rt = RES_PROPERTY ; + aRestriction.res.resProperty.relop = RELOP_RE ; + aRestriction.res.resProperty.ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp = new SPropValue ; + aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ; +#else + aSkipItem = true ; +#endif // 0 + break ; + case nsIAbBooleanConditionTypes::LessThan : + aRestriction.rt = RES_PROPERTY ; + aRestriction.res.resProperty.relop = RELOP_LT ; + aRestriction.res.resProperty.ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp = new SPropValue ; + aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + case nsIAbBooleanConditionTypes::GreaterThan : + aRestriction.rt = RES_PROPERTY ; + aRestriction.res.resProperty.relop = RELOP_GT ; + aRestriction.res.resProperty.ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp = new SPropValue ; + aRestriction.res.resProperty.lpProp->ulPropTag = propertyTag ; + aRestriction.res.resProperty.lpProp->Value.lpszA = strdup(valueAscii.get()) ; + break ; + default : + aSkipItem = true ; + break ; + } + return retCode ; +} + +static nsresult BuildRestriction(nsIAbBooleanExpression *aLevel, + SRestriction& aRestriction) +{ + if (!aLevel) { return NS_ERROR_NULL_POINTER ; } + aRestriction.rt = RES_COMMENT ; + nsresult retCode = NS_OK ; + nsAbBooleanOperationType operationType = 0 ; + uint32_t nbExpressions = 0 ; + nsCOMPtr<nsIArray> expressions; + + retCode = aLevel->GetOperation(&operationType); + NS_ENSURE_SUCCESS(retCode, retCode); + retCode = aLevel->GetExpressions(getter_AddRefs(expressions)); + NS_ENSURE_SUCCESS(retCode, retCode); + retCode = expressions->GetLength(&nbExpressions); + NS_ENSURE_SUCCESS(retCode, retCode); + if (nbExpressions == 0) { + PRINTF(("Error, no expressions.\n")) ; + return NS_OK ; + } + if (operationType == nsIAbBooleanOperationTypes::NOT && nbExpressions != 1) { + PRINTF(("Error, unary operation NOT with multiple operands.\n")) ; + return NS_OK ; + } + LPSRestriction restrictionArray = new SRestriction [nbExpressions] ; + uint32_t realNbExpressions = 0 ; + bool skipItem = false ; + uint32_t i = 0 ; + + nsCOMPtr<nsIAbBooleanConditionString> condition; + nsCOMPtr<nsIAbBooleanExpression> subExpression; + + for (i = 0; i < nbExpressions; ++i) { + condition = do_QueryElementAt(expressions, i, &retCode); + + if (NS_SUCCEEDED(retCode)) { + retCode = BuildRestriction(condition, *restrictionArray, skipItem); + if (NS_SUCCEEDED(retCode)) { + if (!skipItem) { + ++restrictionArray; + ++realNbExpressions; + } + } + else + PRINTF(("Cannot build restriction for item %d %08x.\n", i, retCode)); + } + else { + subExpression = do_QueryElementAt(expressions, i, &retCode); + + if (NS_SUCCEEDED(retCode)) { + retCode = BuildRestriction(subExpression, *restrictionArray); + if (NS_SUCCEEDED(retCode)) { + if (restrictionArray->rt != RES_COMMENT) { + ++restrictionArray; + ++realNbExpressions; + } + } + } + else + PRINTF(("Cannot get interface for item %d %08x.\n", i, retCode)); + } + } + + restrictionArray -= realNbExpressions ; + if (realNbExpressions > 1) { + if (operationType == nsIAbBooleanOperationTypes::OR) { + aRestriction.rt = RES_OR ; + aRestriction.res.resOr.lpRes = restrictionArray ; + aRestriction.res.resOr.cRes = realNbExpressions ; + } + else if (operationType == nsIAbBooleanOperationTypes::AND) { + aRestriction.rt = RES_AND ; + aRestriction.res.resAnd.lpRes = restrictionArray ; + aRestriction.res.resAnd.cRes = realNbExpressions ; + } + else { + PRINTF(("Unsupported operation %d.\n", operationType)) ; + } + } + else if (realNbExpressions == 1) { + if (operationType == nsIAbBooleanOperationTypes::NOT) { + aRestriction.rt = RES_NOT ; + // This copy is to ensure that every NOT restriction is being + // allocated by new and not new[] (see destruction of restriction) + aRestriction.res.resNot.lpRes = new SRestriction ; + memcpy(aRestriction.res.resNot.lpRes, restrictionArray, sizeof(SRestriction)) ; + } + else { + // Case where the restriction array is redundant, + // we need to fill the restriction directly. + memcpy(&aRestriction, restrictionArray, sizeof(SRestriction)) ; + } + delete [] restrictionArray ; + } + if (aRestriction.rt == RES_COMMENT) { + // This means we haven't really built any useful expression + delete [] restrictionArray ; + } + return NS_OK ; +} + +static nsresult BuildRestriction(nsIAbDirectoryQueryArguments *aArguments, + SRestriction& aRestriction) +{ + if (!aArguments) { return NS_ERROR_NULL_POINTER ; } + nsresult retCode = NS_OK ; + + nsCOMPtr<nsISupports> supports ; + retCode = aArguments->GetExpression(getter_AddRefs(supports)) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + nsCOMPtr<nsIAbBooleanExpression> booleanQuery = + do_QueryInterface(supports, &retCode) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + retCode = BuildRestriction(booleanQuery, aRestriction) ; + return retCode ; +} + +static void DestroyRestriction(SRestriction& aRestriction) +{ + switch(aRestriction.rt) { + case RES_AND : + case RES_OR : + { + for (ULONG i = 0 ; i < aRestriction.res.resAnd.cRes ; ++ i) { + DestroyRestriction(aRestriction.res.resAnd.lpRes [i]) ; + } + delete [] aRestriction.res.resAnd.lpRes ; + } + break ; + case RES_COMMENT : + break ; + case RES_CONTENT : + if (PROP_TYPE(aRestriction.res.resContent.ulPropTag) == PT_UNICODE) { + NS_Free(aRestriction.res.resContent.lpProp->Value.lpszW) ; + } + else if (PROP_TYPE(aRestriction.res.resContent.ulPropTag) == PT_STRING8) { + NS_Free(aRestriction.res.resContent.lpProp->Value.lpszA) ; + } + delete aRestriction.res.resContent.lpProp ; + break ; + case RES_EXIST : + break ; + case RES_NOT : + DestroyRestriction(*aRestriction.res.resNot.lpRes) ; + delete aRestriction.res.resNot.lpRes ; + break ; + case RES_BITMASK : + case RES_COMPAREPROPS : + break ; + case RES_PROPERTY : + if (PROP_TYPE(aRestriction.res.resProperty.ulPropTag) == PT_UNICODE) { + NS_Free(aRestriction.res.resProperty.lpProp->Value.lpszW) ; + } + else if (PROP_TYPE(aRestriction.res.resProperty.ulPropTag) == PT_STRING8) { + NS_Free(aRestriction.res.resProperty.lpProp->Value.lpszA) ; + } + delete aRestriction.res.resProperty.lpProp ; + case RES_SIZE : + case RES_SUBRESTRICTION : + break ; + } +} + +struct QueryThreadArgs +{ + nsAbOutlookDirectory *mThis ; + SRestriction mRestriction ; + nsCOMPtr<nsIAbDirSearchListener> mListener ; + int32_t mResultLimit ; + int32_t mTimeout ; + int32_t mThreadId ; +} ; + +static void QueryThreadFunc(void *aArguments) +{ + QueryThreadArgs *arguments = reinterpret_cast<QueryThreadArgs *>(aArguments) ; + + if (!aArguments) { return ; } + arguments->mThis->ExecuteQuery(arguments->mRestriction, arguments->mListener, + arguments->mResultLimit, arguments->mTimeout, + arguments->mThreadId) ; + DestroyRestriction(arguments->mRestriction) ; + delete arguments ; +} + +NS_IMETHODIMP nsAbOutlookDirectory::DoQuery(nsIAbDirectory *aDirectory, + nsIAbDirectoryQueryArguments *aArguments, + nsIAbDirSearchListener *aListener, + int32_t aResultLimit, int32_t aTimeout, + int32_t *aReturnValue) +{ + if (!aArguments || !aListener || !aReturnValue) { + return NS_ERROR_NULL_POINTER; + } + *aReturnValue = -1; + + QueryThreadArgs *threadArgs = new QueryThreadArgs; + PRThread *newThread = nullptr; + + if (!threadArgs) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = BuildRestriction(aArguments, threadArgs->mRestriction); + NS_ENSURE_SUCCESS(rv, rv); + + threadArgs->mThis = this; + threadArgs->mListener = aListener; + threadArgs->mResultLimit = aResultLimit; + threadArgs->mTimeout = aTimeout; + + PR_Lock(mProtector); + *aReturnValue = ++mCurrentQueryId; + PR_Unlock(mProtector); + + threadArgs->mThreadId = *aReturnValue; + newThread = PR_CreateThread(PR_USER_THREAD, + QueryThreadFunc, + threadArgs, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_UNJOINABLE_THREAD, + 0); + + if (!newThread ) { + DestroyRestriction(threadArgs->mRestriction); + delete threadArgs; + return NS_ERROR_OUT_OF_MEMORY; + } + + mQueryThreads.Put(*aReturnValue, newThread); + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::StopQuery(int32_t aContext) +{ + PRThread *queryThread; + if (mQueryThreads.Get(aContext, &queryThread)) { + PR_Interrupt(queryThread); + mQueryThreads.Remove(aContext); + } + return NS_OK; +} + +// nsIAbDirectorySearch methods +NS_IMETHODIMP nsAbOutlookDirectory::StartSearch(void) +{ + if (!mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; } + nsresult retCode = NS_OK ; + + retCode = StopSearch() ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + mCardList.Clear(); + + nsCOMPtr<nsIAbBooleanExpression> expression ; + + nsCOMPtr<nsIAbDirectoryQueryArguments> arguments = do_CreateInstance(NS_ABDIRECTORYQUERYARGUMENTS_CONTRACTID,&retCode); + NS_ENSURE_SUCCESS(retCode, retCode); + + retCode = nsAbQueryStringToExpression::Convert(mQueryString, getter_AddRefs(expression)) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + retCode = arguments->SetExpression(expression) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + + retCode = arguments->SetQuerySubDirectories(true) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + + return DoQuery(this, arguments, this, -1, 0, &mSearchContext); +} + +NS_IMETHODIMP nsAbOutlookDirectory::StopSearch(void) +{ + if (!mIsQueryURI) { return NS_ERROR_NOT_IMPLEMENTED ; } + return StopQuery(mSearchContext) ; +} + +// nsIAbDirSearchListener +NS_IMETHODIMP nsAbOutlookDirectory::OnSearchFinished(int32_t aResult, + const nsAString &aErrorMsg) +{ + return NS_OK; +} + +NS_IMETHODIMP nsAbOutlookDirectory::OnSearchFoundCard(nsIAbCard *aCard) +{ + mCardList.Put(aCard, aCard); + nsresult rv; + nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv)) + rv = abManager->NotifyDirectoryItemAdded(this, aCard); + + return rv; +} + +nsresult nsAbOutlookDirectory::ExecuteQuery(SRestriction &aRestriction, + nsIAbDirSearchListener *aListener, + int32_t aResultLimit, int32_t aTimeout, + int32_t aThreadId) + +{ + if (!aListener) + return NS_ERROR_NULL_POINTER; + + nsresult retCode = NS_OK; + + nsCOMPtr<nsIMutableArray> resultsArray(do_CreateInstance(NS_ARRAY_CONTRACTID, + &retCode)); + NS_ENSURE_SUCCESS(retCode, retCode); + + retCode = GetChildCards(resultsArray, + aRestriction.rt == RES_COMMENT ? nullptr : &aRestriction); + NS_ENSURE_SUCCESS(retCode, retCode); + + uint32_t nbResults = 0; + retCode = resultsArray->GetLength(&nbResults); + NS_ENSURE_SUCCESS(retCode, retCode); + + if (aResultLimit > 0 && nbResults > static_cast<uint32_t>(aResultLimit)) { + nbResults = static_cast<uint32_t>(aResultLimit) ; + } + + uint32_t i = 0; + nsCOMPtr<nsIAbCard> card; + + for (i = 0 ; i < nbResults ; ++ i) { + card = do_QueryElementAt(resultsArray, i, &retCode); + NS_ENSURE_SUCCESS(retCode, retCode); + + aListener->OnSearchFoundCard(card); + } + + mQueryThreads.Remove(aThreadId); + + aListener->OnSearchFinished(nsIAbDirectoryQueryResultListener::queryResultComplete, + EmptyString()); + return retCode; +} + +// This function expects the aCards array to already be created. +nsresult nsAbOutlookDirectory::GetChildCards(nsIMutableArray *aCards, + void *aRestriction) +{ + nsAbWinHelperGuard mapiAddBook(mAbWinType); + + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + + nsMapiEntryArray cardEntries; + LPSRestriction restriction = (LPSRestriction) aRestriction; + + if (!mapiAddBook->GetCards(*mMapiData, restriction, cardEntries)) { + PRINTF(("Cannot get cards.\n")); + return NS_ERROR_FAILURE; + } + + nsAutoCString ourUuid; + GetUuid(ourUuid); + + nsAutoCString entryId; + nsAutoCString uriName; + nsCOMPtr<nsIAbCard> childCard; + nsresult rv; + + for (ULONG card = 0; card < cardEntries.mNbEntries; ++card) { + cardEntries.mEntries[card].ToString(entryId); + buildAbWinUri(kOutlookCardScheme, mAbWinType, uriName); + uriName.Append(entryId); + + rv = OutlookCardForURI(uriName, getter_AddRefs(childCard)); + NS_ENSURE_SUCCESS(rv, rv); + childCard->SetDirectoryId(ourUuid); + + aCards->AppendElement(childCard, false); + } + return rv; +} + +nsresult nsAbOutlookDirectory::GetChildNodes(nsIMutableArray* aNodes) +{ + NS_ENSURE_ARG_POINTER(aNodes); + + aNodes->Clear(); + + nsAbWinHelperGuard mapiAddBook(mAbWinType); + nsMapiEntryArray nodeEntries; + + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + + if (!mapiAddBook->GetNodes(*mMapiData, nodeEntries)) + { + PRINTF(("Cannot get nodes.\n")); + return NS_ERROR_FAILURE; + } + + nsAutoCString entryId; + nsAutoCString uriName; + nsresult rv = NS_OK; + + nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + for (ULONG node = 0; node < nodeEntries.mNbEntries; ++node) + { + nodeEntries.mEntries[node].ToString(entryId); + buildAbWinUri(kOutlookDirectoryScheme, mAbWinType, uriName); + uriName.Append(entryId); + + nsCOMPtr <nsIAbDirectory> directory; + rv = abManager->GetDirectory(uriName, getter_AddRefs(directory)); + NS_ENSURE_SUCCESS(rv, rv); + + aNodes->AppendElement(directory, false); + } + return rv; +} + +nsresult nsAbOutlookDirectory::NotifyItemDeletion(nsISupports *aItem) +{ + nsresult rv; + nsCOMPtr<nsIAbManager> abManager(do_GetService(NS_ABMANAGER_CONTRACTID, &rv)); + + if (NS_SUCCEEDED(rv)) + rv = abManager->NotifyDirectoryItemDeleted(this, aItem); + + return rv; +} + +nsresult nsAbOutlookDirectory::NotifyItemAddition(nsISupports *aItem) +{ + nsresult rv; + nsCOMPtr<nsIAbManager> abManager = do_GetService(NS_ABMANAGER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) + rv = abManager->NotifyDirectoryItemAdded(this, aItem); + + return rv; +} + +// This is called from EditMailListToDatabase. +// We got m_AddressList containing the list of cards the mailing +// list is supposed to contain at the end. +nsresult nsAbOutlookDirectory::CommitAddressList(void) +{ + if (!m_IsMailList) { + PRINTF(("We are not in a mailing list, no commit can be done.\n")); + return NS_ERROR_UNEXPECTED; + } + + nsresult rv; + uint32_t i = 0; + nsCOMPtr<nsIMutableArray> oldList(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetChildCards(oldList, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + if (!m_AddressList) + return NS_ERROR_NULL_POINTER; + + uint32_t nbCards = 0; + rv = m_AddressList->GetLength(&nbCards); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISupports> element; + nsCOMPtr<nsIAbCard> newCard; + uint32_t pos; + + for (i = 0; i < nbCards; ++i) { + element = do_QueryElementAt(m_AddressList, i, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (NS_SUCCEEDED(oldList->IndexOf(0, element, &pos))) { + rv = oldList->RemoveElementAt(pos); + NS_ENSURE_SUCCESS(rv, rv); + + // The entry was not already there + nsCOMPtr<nsIAbCard> card(do_QueryInterface(element, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CreateCard(card, getter_AddRefs(newCard)); + NS_ENSURE_SUCCESS(rv, rv); + m_AddressList->ReplaceElementAt(newCard, i, false); + } + } + return DeleteCards(oldList); +} + +nsresult nsAbOutlookDirectory::UpdateAddressList(void) +{ + if (!m_AddressList) + { + nsresult rv; + m_AddressList = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + + return m_IsMailList ? GetChildCards(m_AddressList, nullptr) : + GetChildNodes(m_AddressList); +} + +nsresult nsAbOutlookDirectory::CreateCard(nsIAbCard *aData, nsIAbCard **aNewCard) +{ + if (!aData || !aNewCard) { return NS_ERROR_NULL_POINTER ; } + *aNewCard = nullptr ; + nsresult retCode = NS_OK ; + nsAbWinHelperGuard mapiAddBook (mAbWinType) ; + nsMapiEntry newEntry ; + nsAutoCString entryString ; + bool didCopy = false ; + + if (!mapiAddBook->IsOK()) { return NS_ERROR_FAILURE ; } + // If we get an nsIAbCard that maps onto an Outlook card uri + // we simply copy the contents of the Outlook card. + retCode = ExtractCardEntry(aData, entryString) ; + if (NS_SUCCEEDED(retCode) && !entryString.IsEmpty()) { + nsMapiEntry sourceEntry ; + + + sourceEntry.Assign(entryString) ; + if (m_IsMailList) { + // In the case of a mailing list, we can use the address + // as a direct template to build the new one (which is done + // by CopyEntry). + mapiAddBook->CopyEntry(*mMapiData, sourceEntry, newEntry) ; + didCopy = true ; + } + else { + // Else, we have to create a temporary address and copy the + // source into it. Yes it's silly. + mapiAddBook->CreateEntry(*mMapiData, newEntry) ; + } + } + // If this approach doesn't work, well we're back to creating and copying. + if (newEntry.mByteCount == 0) { + // In the case of a mailing list, we cannot directly create a new card, + // we have to create a temporary one in a real folder (to be able to use + // templates) and then copy it to the mailing list. + if (m_IsMailList) { + nsMapiEntry parentEntry ; + nsMapiEntry temporaryEntry ; + + if (!mapiAddBook->GetDefaultContainer(parentEntry)) { + return NS_ERROR_FAILURE ; + } + if (!mapiAddBook->CreateEntry(parentEntry, temporaryEntry)) { + return NS_ERROR_FAILURE ; + } + if (!mapiAddBook->CopyEntry(*mMapiData, temporaryEntry, newEntry)) { + return NS_ERROR_FAILURE ; + } + if (!mapiAddBook->DeleteEntry(parentEntry, temporaryEntry)) { + return NS_ERROR_FAILURE ; + } + } + // If we're on a real address book folder, we can directly create an + // empty card. + else if (!mapiAddBook->CreateEntry(*mMapiData, newEntry)) { + return NS_ERROR_FAILURE ; + } + } + newEntry.ToString(entryString) ; + nsAutoCString uri ; + + buildAbWinUri(kOutlookCardScheme, mAbWinType, uri) ; + uri.Append(entryString) ; + + nsCOMPtr<nsIAbCard> newCard; + retCode = OutlookCardForURI(uri, getter_AddRefs(newCard)); + NS_ENSURE_SUCCESS(retCode, retCode); + + nsAutoCString ourUuid; + GetUuid(ourUuid); + newCard->SetDirectoryId(ourUuid); + + if (!didCopy) { + retCode = newCard->Copy(aData) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + retCode = ModifyCard(newCard) ; + NS_ENSURE_SUCCESS(retCode, retCode) ; + } + *aNewCard = newCard ; + NS_ADDREF(*aNewCard) ; + return retCode ; +} + +static void UnicodeToWord(const char16_t *aUnicode, WORD& aWord) +{ + aWord = 0 ; + if (aUnicode == nullptr || *aUnicode == 0) { return ; } + nsresult errorCode = NS_OK; + nsAutoString unichar (aUnicode) ; + + aWord = static_cast<WORD>(unichar.ToInteger(&errorCode)); + if (NS_FAILED(errorCode)) { + PRINTF(("Error conversion string %S: %08x.\n", unichar.get(), errorCode)) ; + } +} + +#define PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST "mail.addr_book.lastnamefirst" + + +NS_IMETHODIMP nsAbOutlookDirectory::ModifyCard(nsIAbCard *aModifiedCard) +{ + NS_ENSURE_ARG_POINTER(aModifiedCard); + + nsString *properties = nullptr; + nsAutoString utility; + nsAbWinHelperGuard mapiAddBook(mAbWinType); + + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + + nsCString entry; + nsresult retCode = ExtractCardEntry(aModifiedCard, entry); + NS_ENSURE_SUCCESS(retCode, retCode); + // If we don't have the card entry, we can't work. + if (entry.IsEmpty()) + return NS_ERROR_FAILURE; + + nsMapiEntry mapiData; + mapiData.Assign(entry); + + // First, all the standard properties in one go + properties = new nsString[index_LastProp]; + if (!properties) { + return NS_ERROR_OUT_OF_MEMORY; + } + aModifiedCard->GetFirstName(properties[index_FirstName]); + aModifiedCard->GetLastName(properties[index_LastName]); + // This triple search for something to put in the name + // is because in the case of a mailing list edition in + // Mozilla, the display name will not be provided, and + // MAPI doesn't allow that, so we fall back on an optional + // name, and when all fails, on the email address. + aModifiedCard->GetDisplayName(properties[index_DisplayName]); + if (properties[index_DisplayName].IsEmpty()) { + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefBranch = + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + int32_t format; + rv = prefBranch->GetIntPref(PREF_MAIL_ADDR_BOOK_LASTNAMEFIRST, &format); + NS_ENSURE_SUCCESS(rv,rv); + + rv = aModifiedCard->GenerateName(format, nullptr, + properties[index_DisplayName]); + NS_ENSURE_SUCCESS(rv,rv); + + if (properties[index_DisplayName].IsEmpty()) { + aModifiedCard->GetPrimaryEmail(properties[index_DisplayName]); + } + } + aModifiedCard->SetDisplayName(properties[index_DisplayName]); + aModifiedCard->GetPrimaryEmail(properties[index_EmailAddress]); + aModifiedCard->GetPropertyAsAString(kNicknameProperty, properties[index_NickName]); + aModifiedCard->GetPropertyAsAString(kWorkPhoneProperty, properties[index_WorkPhoneNumber]); + aModifiedCard->GetPropertyAsAString(kHomePhoneProperty, properties[index_HomePhoneNumber]); + aModifiedCard->GetPropertyAsAString(kFaxProperty, properties[index_WorkFaxNumber]); + aModifiedCard->GetPropertyAsAString(kPagerProperty, properties[index_PagerNumber]); + aModifiedCard->GetPropertyAsAString(kCellularProperty, properties[index_MobileNumber]); + aModifiedCard->GetPropertyAsAString(kHomeCityProperty, properties[index_HomeCity]); + aModifiedCard->GetPropertyAsAString(kHomeStateProperty, properties[index_HomeState]); + aModifiedCard->GetPropertyAsAString(kHomeZipCodeProperty, properties[index_HomeZip]); + aModifiedCard->GetPropertyAsAString(kHomeCountryProperty, properties[index_HomeCountry]); + aModifiedCard->GetPropertyAsAString(kWorkCityProperty, properties[index_WorkCity]); + aModifiedCard->GetPropertyAsAString(kWorkStateProperty, properties[index_WorkState]); + aModifiedCard->GetPropertyAsAString(kWorkZipCodeProperty, properties[index_WorkZip]); + aModifiedCard->GetPropertyAsAString(kWorkCountryProperty, properties[index_WorkCountry]); + aModifiedCard->GetPropertyAsAString(kJobTitleProperty, properties[index_JobTitle]); + aModifiedCard->GetPropertyAsAString(kDepartmentProperty, properties[index_Department]); + aModifiedCard->GetPropertyAsAString(kCompanyProperty, properties[index_Company]); + aModifiedCard->GetPropertyAsAString(kWorkWebPageProperty, properties[index_WorkWebPage]); + aModifiedCard->GetPropertyAsAString(kHomeWebPageProperty, properties[index_HomeWebPage]); + aModifiedCard->GetPropertyAsAString(kNotesProperty, properties[index_Comments]); + if (!mapiAddBook->SetPropertiesUString(mapiData, OutlookCardMAPIProps, + index_LastProp, properties)) { + PRINTF(("Cannot set general properties.\n")) ; + } + + delete [] properties; + nsString unichar; + nsString unichar2; + WORD year = 0; + WORD month = 0; + WORD day = 0; + + aModifiedCard->GetPropertyAsAString(kHomeAddressProperty, unichar); + aModifiedCard->GetPropertyAsAString(kHomeAddress2Property, unichar2); + + utility.Assign(unichar.get()); + if (!utility.IsEmpty()) + utility.AppendLiteral("\r\n"); + + utility.Append(unichar2.get()); + if (!mapiAddBook->SetPropertyUString(mapiData, PR_HOME_ADDRESS_STREET_W, utility.get())) { + PRINTF(("Cannot set home address.\n")) ; + } + + unichar.Truncate(); + aModifiedCard->GetPropertyAsAString(kWorkAddressProperty, unichar); + unichar2.Truncate(); + aModifiedCard->GetPropertyAsAString(kWorkAddress2Property, unichar2); + + utility.Assign(unichar.get()); + if (!utility.IsEmpty()) + utility.AppendLiteral("\r\n"); + + utility.Append(unichar2.get()); + if (!mapiAddBook->SetPropertyUString(mapiData, PR_BUSINESS_ADDRESS_STREET_W, utility.get())) { + PRINTF(("Cannot set work address.\n")) ; + } + + unichar.Truncate(); + aModifiedCard->GetPropertyAsAString(kBirthYearProperty, unichar); + UnicodeToWord(unichar.get(), year); + unichar.Truncate(); + aModifiedCard->GetPropertyAsAString(kBirthMonthProperty, unichar); + UnicodeToWord(unichar.get(), month); + unichar.Truncate(); + aModifiedCard->GetPropertyAsAString(kBirthDayProperty, unichar); + UnicodeToWord(unichar.get(), day); + if (!mapiAddBook->SetPropertyDate(mapiData, PR_BIRTHDAY, year, month, day)) { + PRINTF(("Cannot set date.\n")) ; + } + + return retCode; +} + +NS_IMETHODIMP nsAbOutlookDirectory::OnQueryFoundCard(nsIAbCard *aCard) +{ + return OnSearchFoundCard(aCard); +} + +NS_IMETHODIMP nsAbOutlookDirectory::OnQueryResult(int32_t aResult, + int32_t aErrorCode) +{ + return OnSearchFinished(aResult, EmptyString()); +} + +NS_IMETHODIMP nsAbOutlookDirectory::UseForAutocomplete(const nsACString &aIdentityKey, bool *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = false; + return NS_OK; +} + +static void splitString(nsString& aSource, nsString& aTarget) +{ + aTarget.Truncate(); + int32_t offset = aSource.FindChar('\n'); + + if (offset >= 0) + { + const char16_t *source = aSource.get() + offset + 1; + while (*source) + { + if (*source == '\n' || *source == '\r') + aTarget.Append(char16_t(' ')); + else + aTarget.Append(*source); + ++source; + } + aSource.SetLength(offset); + } +} + +nsresult OutlookCardForURI(const nsACString &aUri, nsIAbCard **newCard) +{ + NS_ENSURE_ARG_POINTER(newCard); + + nsAutoCString entry; + nsAutoCString stub; + uint32_t abWinType = getAbWinType(kOutlookCardScheme, + PromiseFlatCString(aUri).get(), stub, entry); + if (abWinType == nsAbWinType_Unknown) + { + PRINTF(("Huge problem URI=%s.\n", PromiseFlatCString(aUri).get())); + return NS_ERROR_INVALID_ARG; + } + + nsAbWinHelperGuard mapiAddBook(abWinType); + if (!mapiAddBook->IsOK()) + return NS_ERROR_FAILURE; + + nsresult rv; + nsCOMPtr<nsIAbCard> card = do_CreateInstance(NS_ABCARDPROPERTY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + card->SetPropertyAsAUTF8String("OutlookEntryURI", aUri); + card->SetLocalId(aUri); + + nsMapiEntry mapiData; + mapiData.Assign(entry); + + nsString unichars[index_LastProp]; + + if (mapiAddBook->GetPropertiesUString(mapiData, OutlookCardMAPIProps, + index_LastProp, unichars)) + { + card->SetFirstName(unichars[index_FirstName]); + card->SetLastName(unichars[index_LastName]); + card->SetDisplayName(unichars[index_DisplayName]); + card->SetPrimaryEmail(unichars[index_EmailAddress]); + card->SetPropertyAsAString(kNicknameProperty, unichars[index_NickName]); + card->SetPropertyAsAString(kWorkPhoneProperty, unichars[index_WorkPhoneNumber]); + card->SetPropertyAsAString(kHomePhoneProperty, unichars[index_HomePhoneNumber]); + card->SetPropertyAsAString(kFaxProperty, unichars[index_WorkFaxNumber]); + card->SetPropertyAsAString(kPagerProperty, unichars[index_PagerNumber]); + card->SetPropertyAsAString(kCellularProperty, unichars[index_MobileNumber]); + card->SetPropertyAsAString(kHomeCityProperty, unichars[index_HomeCity]); + card->SetPropertyAsAString(kHomeStateProperty, unichars[index_HomeState]); + card->SetPropertyAsAString(kHomeZipCodeProperty, unichars[index_HomeZip]); + card->SetPropertyAsAString(kHomeCountryProperty, unichars[index_HomeCountry]); + card->SetPropertyAsAString(kWorkCityProperty, unichars[index_WorkCity]); + card->SetPropertyAsAString(kWorkStateProperty, unichars[index_WorkState]); + card->SetPropertyAsAString(kWorkZipCodeProperty, unichars[index_WorkZip]); + card->SetPropertyAsAString(kWorkCountryProperty, unichars[index_WorkCountry]); + card->SetPropertyAsAString(kJobTitleProperty, unichars[index_JobTitle]); + card->SetPropertyAsAString(kDepartmentProperty, unichars[index_Department]); + card->SetPropertyAsAString(kCompanyProperty, unichars[index_Company]); + card->SetPropertyAsAString(kWorkWebPageProperty, unichars[index_WorkWebPage]); + card->SetPropertyAsAString(kHomeWebPageProperty, unichars[index_HomeWebPage]); + card->SetPropertyAsAString(kNotesProperty, unichars[index_Comments]); + } + + ULONG cardType = 0; + if (mapiAddBook->GetPropertyLong(mapiData, PR_OBJECT_TYPE, cardType)) + { + card->SetIsMailList(cardType == MAPI_DISTLIST); + if (cardType == MAPI_DISTLIST) + { + nsAutoCString normalChars; + buildAbWinUri(kOutlookDirectoryScheme, abWinType, normalChars); + normalChars.Append(entry); + card->SetMailListURI(normalChars.get()); + } + } + + nsAutoString unichar; + nsAutoString unicharBis; + if (mapiAddBook->GetPropertyUString(mapiData, PR_HOME_ADDRESS_STREET_W, unichar)) + { + splitString(unichar, unicharBis); + card->SetPropertyAsAString(kHomeAddressProperty, unichar); + card->SetPropertyAsAString(kHomeAddress2Property, unicharBis); + } + if (mapiAddBook->GetPropertyUString(mapiData, PR_BUSINESS_ADDRESS_STREET_W, + unichar)) + { + splitString(unichar, unicharBis); + card->SetPropertyAsAString(kWorkAddressProperty, unichar); + card->SetPropertyAsAString(kWorkAddress2Property, unicharBis); + } + + WORD year = 0, month = 0, day = 0; + if (mapiAddBook->GetPropertyDate(mapiData, PR_BIRTHDAY, year, month, day)) + { + card->SetPropertyAsUint32(kBirthYearProperty, year); + card->SetPropertyAsUint32(kBirthMonthProperty, month); + card->SetPropertyAsUint32(kBirthDayProperty, day); + } + + card.swap(*newCard); + return NS_OK; +} |