/* -*- 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 "nsAbDirectoryQuery.h" #include "nsAbDirectoryQueryProxy.h" #include "nsAbUtils.h" #include "nsAbBooleanExpression.h" #include "nsArrayUtils.h" #include "nsComponentManagerUtils.h" #include "nsStringGlue.h" #include "nsUnicharUtils.h" #include "nsIAbDirSearchListener.h" #include "nsISimpleEnumerator.h" #include "nsMsgUtils.h" NS_IMPL_ISUPPORTS(nsAbDirectoryQuerySimpleBooleanExpression, nsIAbBooleanExpression) nsAbDirectoryQuerySimpleBooleanExpression::nsAbDirectoryQuerySimpleBooleanExpression() : mOperation (nsIAbBooleanOperationTypes::AND) { } nsAbDirectoryQuerySimpleBooleanExpression::~nsAbDirectoryQuerySimpleBooleanExpression() { } /* attribute nsAbBooleanOperationType operation; */ NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::GetOperation(nsAbBooleanOperationType *aOperation) { if (!aOperation) return NS_ERROR_NULL_POINTER; *aOperation = mOperation; return NS_OK; } NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::SetOperation(nsAbBooleanOperationType aOperation) { if (aOperation != nsIAbBooleanOperationTypes::AND && aOperation != nsIAbBooleanOperationTypes::OR) return NS_ERROR_FAILURE; mOperation = aOperation; return NS_OK; } /* attribute nsIArray expressions; */ NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::GetExpressions(nsIArray **aExpressions) { if (!aExpressions) return NS_ERROR_NULL_POINTER; if (!mExpressions) { mExpressions = do_CreateInstance(NS_ARRAY_CONTRACTID); if (!mExpressions) return NS_ERROR_OUT_OF_MEMORY; } NS_ADDREF(*aExpressions = mExpressions); return NS_OK; } NS_IMETHODIMP nsAbDirectoryQuerySimpleBooleanExpression::SetExpressions(nsIArray *aExpressions) { if (!aExpressions) return NS_ERROR_NULL_POINTER; // Ensure all the items are of the right type. nsresult rv; uint32_t count; rv = aExpressions->GetLength(&count); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr queryExpression; for (uint32_t i = 0; i < count; ++i) { queryExpression = do_QueryElementAt(aExpressions, i, &rv); if (NS_FAILED(rv)) return NS_ERROR_ILLEGAL_VALUE; } // Values ok, so we can just save and return. mExpressions = aExpressions; return NS_OK; } NS_IMPL_ISUPPORTS(nsAbDirectoryQueryArguments, nsIAbDirectoryQueryArguments) nsAbDirectoryQueryArguments::nsAbDirectoryQueryArguments() : mQuerySubDirectories(true) { } nsAbDirectoryQueryArguments::~nsAbDirectoryQueryArguments() { } /* attribute nsISupports matchItems; */ NS_IMETHODIMP nsAbDirectoryQueryArguments::GetExpression(nsISupports** aExpression) { if (!aExpression) return NS_ERROR_NULL_POINTER; NS_IF_ADDREF(*aExpression = mExpression); return NS_OK; } NS_IMETHODIMP nsAbDirectoryQueryArguments::SetExpression(nsISupports* aExpression) { mExpression = aExpression; return NS_OK; } /* attribute boolean querySubDirectories; */ NS_IMETHODIMP nsAbDirectoryQueryArguments::GetQuerySubDirectories(bool* aQuerySubDirectories) { NS_ENSURE_ARG_POINTER(aQuerySubDirectories); *aQuerySubDirectories = mQuerySubDirectories; return NS_OK; } NS_IMETHODIMP nsAbDirectoryQueryArguments::SetQuerySubDirectories(bool aQuerySubDirectories) { mQuerySubDirectories = aQuerySubDirectories; return NS_OK; } NS_IMETHODIMP nsAbDirectoryQueryArguments::GetTypeSpecificArg(nsISupports** aArg) { NS_ENSURE_ARG_POINTER(aArg); NS_IF_ADDREF(*aArg = mTypeSpecificArg); return NS_OK; } NS_IMETHODIMP nsAbDirectoryQueryArguments::SetTypeSpecificArg(nsISupports* aArg) { mTypeSpecificArg = aArg; return NS_OK; } NS_IMETHODIMP nsAbDirectoryQueryArguments::GetFilter(nsACString & aFilter) { aFilter.Assign(mFilter); return NS_OK; } NS_IMETHODIMP nsAbDirectoryQueryArguments::SetFilter(const nsACString & aFilter) { mFilter.Assign(aFilter); return NS_OK; } NS_IMPL_ISUPPORTS(nsAbDirectoryQueryPropertyValue, nsIAbDirectoryQueryPropertyValue) nsAbDirectoryQueryPropertyValue::nsAbDirectoryQueryPropertyValue() { } nsAbDirectoryQueryPropertyValue::nsAbDirectoryQueryPropertyValue(const char* aName, const char16_t* aValue) { mName = aName; mValue = aValue; } nsAbDirectoryQueryPropertyValue::nsAbDirectoryQueryPropertyValue(const char* aName, nsISupports* aValueISupports) { mName = aName; mValueISupports = aValueISupports; } nsAbDirectoryQueryPropertyValue::~nsAbDirectoryQueryPropertyValue() { } /* read only attribute string name; */ NS_IMETHODIMP nsAbDirectoryQueryPropertyValue::GetName(char* *aName) { *aName = mName.IsEmpty() ? 0 : ToNewCString(mName); return NS_OK; } /* read only attribute wstring value; */ NS_IMETHODIMP nsAbDirectoryQueryPropertyValue::GetValue(char16_t* *aValue) { *aValue = ToNewUnicode(mValue); if (!(*aValue)) return NS_ERROR_OUT_OF_MEMORY; else return NS_OK; } /* readonly attribute nsISupports valueISupports; */ NS_IMETHODIMP nsAbDirectoryQueryPropertyValue::GetValueISupports(nsISupports* *aValueISupports) { if (!mValueISupports) return NS_ERROR_NULL_POINTER; NS_IF_ADDREF(*aValueISupports = mValueISupports); return NS_OK; } /* Implementation file */ NS_IMPL_ISUPPORTS(nsAbDirectoryQuery, nsIAbDirectoryQuery) nsAbDirectoryQuery::nsAbDirectoryQuery() { } nsAbDirectoryQuery::~nsAbDirectoryQuery() { } NS_IMETHODIMP nsAbDirectoryQuery::DoQuery(nsIAbDirectory *aDirectory, nsIAbDirectoryQueryArguments* arguments, nsIAbDirSearchListener* listener, int32_t resultLimit, int32_t timeOut, int32_t* _retval) { NS_ENSURE_ARG_POINTER(aDirectory); nsCOMPtr supportsExpression; nsresult rv = arguments->GetExpression(getter_AddRefs(supportsExpression)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr expression(do_QueryInterface(supportsExpression, &rv)); NS_ENSURE_SUCCESS(rv, rv); bool doSubDirectories; rv = arguments->GetQuerySubDirectories(&doSubDirectories); NS_ENSURE_SUCCESS(rv, rv); rv = query(aDirectory, expression, listener, doSubDirectories, &resultLimit); rv = NS_FAILED(rv) ? queryError(listener) : queryFinished(listener); *_retval = 0; return rv; } /* void stopQuery (in long contextID); */ NS_IMETHODIMP nsAbDirectoryQuery::StopQuery(int32_t contextID) { return NS_OK; } nsresult nsAbDirectoryQuery::query(nsIAbDirectory* directory, nsIAbBooleanExpression* expression, nsIAbDirSearchListener* listener, bool doSubDirectories, int32_t* resultLimit) { if (*resultLimit == 0) return NS_OK; nsresult rv = queryCards(directory, expression, listener, resultLimit); NS_ENSURE_SUCCESS(rv, rv); if (doSubDirectories && resultLimit != 0) { rv = queryChildren(directory, expression, listener, doSubDirectories, resultLimit); NS_ENSURE_SUCCESS(rv, rv); } return rv; } nsresult nsAbDirectoryQuery::queryChildren(nsIAbDirectory* directory, nsIAbBooleanExpression* expression, nsIAbDirSearchListener* listener, bool doSubDirectories, int32_t* resultLimit) { nsresult rv = NS_OK; nsCOMPtr subDirectories; rv = directory->GetChildNodes(getter_AddRefs(subDirectories)); NS_ENSURE_SUCCESS(rv, rv); bool hasMore; while (NS_SUCCEEDED(rv = subDirectories->HasMoreElements(&hasMore)) && hasMore) { nsCOMPtr item; rv = subDirectories->GetNext (getter_AddRefs (item)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr subDirectory(do_QueryInterface(item, &rv)); NS_ENSURE_SUCCESS(rv, rv); rv = query(subDirectory, expression, listener, doSubDirectories, resultLimit); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsresult nsAbDirectoryQuery::queryCards(nsIAbDirectory* directory, nsIAbBooleanExpression* expression, nsIAbDirSearchListener* listener, int32_t* resultLimit) { nsresult rv = NS_OK; nsCOMPtr cards; rv = directory->GetChildCards(getter_AddRefs(cards)); if (NS_FAILED(rv)) { if (rv != NS_ERROR_NOT_IMPLEMENTED) NS_ENSURE_SUCCESS(rv, rv); else return NS_OK; } if (!cards) return NS_OK; bool more; while (NS_SUCCEEDED(cards->HasMoreElements(&more)) && more) { nsCOMPtr item; rv = cards->GetNext(getter_AddRefs(item)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr card(do_QueryInterface(item, &rv)); NS_ENSURE_SUCCESS(rv, rv); rv = matchCard (card, expression, listener, resultLimit); NS_ENSURE_SUCCESS(rv, rv); if (*resultLimit == 0) return NS_OK; } return NS_OK; } nsresult nsAbDirectoryQuery::matchCard(nsIAbCard* card, nsIAbBooleanExpression* expression, nsIAbDirSearchListener* listener, int32_t* resultLimit) { bool matchFound = false; nsresult rv = matchCardExpression(card, expression, &matchFound); NS_ENSURE_SUCCESS(rv, rv); if (matchFound) { (*resultLimit)--; rv = queryMatch(card, listener); NS_ENSURE_SUCCESS(rv, rv); } return rv; } nsresult nsAbDirectoryQuery::matchCardExpression(nsIAbCard* card, nsIAbBooleanExpression* expression, bool* result) { nsAbBooleanOperationType operation; nsresult rv = expression->GetOperation (&operation); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr childExpressions; rv = expression->GetExpressions (getter_AddRefs (childExpressions)); NS_ENSURE_SUCCESS(rv, rv); uint32_t count; rv = childExpressions->GetLength(&count); NS_ENSURE_SUCCESS(rv, rv); if (operation == nsIAbBooleanOperationTypes::NOT && count > 1) return NS_ERROR_FAILURE; bool value = *result = false; nsCOMPtr childCondition; nsCOMPtr childExpression; for (uint32_t i = 0; i < count; i++) { childCondition = do_QueryElementAt(childExpressions, i, &rv); if (NS_SUCCEEDED(rv)) { rv = matchCardCondition (card, childCondition, &value); NS_ENSURE_SUCCESS(rv, rv); } else { childExpression = do_QueryElementAt(childExpressions, i, &rv); if (NS_SUCCEEDED(rv)) { rv = matchCardExpression (card, childExpression, &value); NS_ENSURE_SUCCESS(rv, rv); } else return NS_ERROR_FAILURE; } if (operation == nsIAbBooleanOperationTypes::OR && value) break; else if (operation == nsIAbBooleanOperationTypes::AND && !value) break; else if (operation == nsIAbBooleanOperationTypes::NOT) value = !value; } *result = value; return NS_OK; } nsresult nsAbDirectoryQuery::matchCardCondition(nsIAbCard* card, nsIAbBooleanConditionString* condition, bool* matchFound) { nsAbBooleanConditionType conditionType; nsresult rv = condition->GetCondition (&conditionType); NS_ENSURE_SUCCESS(rv, rv); nsCString name; rv = condition->GetName (getter_Copies (name)); NS_ENSURE_SUCCESS(rv, rv); if (name.Equals ("card:nsIAbCard")) { *matchFound = (conditionType == nsIAbBooleanConditionTypes::Exists); return NS_OK; } nsString matchValue; rv = condition->GetValue (getter_Copies (matchValue)); NS_ENSURE_SUCCESS(rv, rv); if (name.EqualsLiteral("IsMailList")) { bool isMailList; rv = card->GetIsMailList(&isMailList); NS_ENSURE_SUCCESS(rv, rv); // Only equals is supported. if (conditionType != nsIAbBooleanConditionTypes::Is) return NS_ERROR_FAILURE; *matchFound = isMailList ? matchValue.EqualsLiteral("TRUE") : matchValue.EqualsLiteral("FALSE"); return NS_OK; } nsString value; (void)card->GetPropertyAsAString(name.get(), value); if (value.IsEmpty()) { *matchFound = (conditionType == nsIAbBooleanConditionTypes::DoesNotExist) ? true : false; return NS_OK; } /* TODO * What about allowing choice between case insensitive * and case sensitive comparisons? * */ switch (conditionType) { case nsIAbBooleanConditionTypes::Exists: *matchFound = true; break; case nsIAbBooleanConditionTypes::Contains: *matchFound = CaseInsensitiveFindInReadable(matchValue, value); break; case nsIAbBooleanConditionTypes::DoesNotContain: *matchFound = !CaseInsensitiveFindInReadable(matchValue, value); break; case nsIAbBooleanConditionTypes::Is: *matchFound = value.Equals(matchValue, nsCaseInsensitiveStringComparator()); break; case nsIAbBooleanConditionTypes::IsNot: *matchFound = !value.Equals(matchValue, nsCaseInsensitiveStringComparator()); break; case nsIAbBooleanConditionTypes::BeginsWith: *matchFound = StringBeginsWith(value, matchValue, nsCaseInsensitiveStringComparator()); break; case nsIAbBooleanConditionTypes::LessThan: *matchFound = Compare(value, matchValue, nsCaseInsensitiveStringComparator()) < 0; break; case nsIAbBooleanConditionTypes::GreaterThan: *matchFound = Compare(value, matchValue, nsCaseInsensitiveStringComparator()) > 0; break; case nsIAbBooleanConditionTypes::EndsWith: *matchFound = StringEndsWith(value, matchValue, nsCaseInsensitiveStringComparator()); break; case nsIAbBooleanConditionTypes::SoundsLike: case nsIAbBooleanConditionTypes::RegExp: *matchFound = false; break; default: *matchFound = false; } return rv; } nsresult nsAbDirectoryQuery::queryMatch(nsIAbCard* card, nsIAbDirSearchListener* listener) { return listener->OnSearchFoundCard(card); } nsresult nsAbDirectoryQuery::queryFinished(nsIAbDirSearchListener* listener) { return listener->OnSearchFinished(nsIAbDirectoryQueryResultListener::queryResultComplete, EmptyString()); } nsresult nsAbDirectoryQuery::queryError(nsIAbDirSearchListener* listener) { return listener->OnSearchFinished(nsIAbDirectoryQueryResultListener::queryResultError, EmptyString()); }