/* -*- 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 "nsAbQueryStringToExpression.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsITextToSubURI.h" #include "nsAbBooleanExpression.h" #include "plstr.h" /** * This code parses the query expression passed in as an addressbook URI. * The expression takes the form: * (BOOL1(FIELD1,OP1,VALUE1)..(FIELDn,OPn,VALUEn)(BOOL2(FIELD1,OP1,VALUE1)...)...) * * BOOLn A boolean operator joining subsequent terms delimited by (). * For possible values see CreateBooleanExpression(). * FIELDn An addressbook card data field. * OPn An operator for the search term. * For possible values see CreateBooleanConditionString(). * VALUEn The value to be matched in the FIELDn via the OPn operator. * The value must be URL encoded by the caller, if it contains any * special characters including '(' and ')'. */ nsresult nsAbQueryStringToExpression::Convert( const nsACString& aQueryString, nsIAbBooleanExpression** expression) { nsresult rv; nsAutoCString q(aQueryString); q.StripWhitespace(); const char* queryChars = q.get(); nsCOMPtr s; rv = ParseExpression(&queryChars, getter_AddRefs(s)); NS_ENSURE_SUCCESS(rv, rv); // Case: Not end of string if (*queryChars != 0) return NS_ERROR_FAILURE; nsCOMPtr e(do_QueryInterface(s, &rv)); NS_ENSURE_SUCCESS(rv, rv); e.forget(expression); return rv; } nsresult nsAbQueryStringToExpression::ParseExpression( const char** index, nsISupports** expression) { nsresult rv; if (**index == '?') { (*index)++; } if (**index != '(') return NS_ERROR_FAILURE; const char* indexBracket = *index + 1; while (*indexBracket && *indexBracket != '(' && *indexBracket != ')') indexBracket++; // Case: End of string if (*indexBracket == 0) return NS_ERROR_FAILURE; // Case: "((" or "()" if (indexBracket == *index + 1) { return NS_ERROR_FAILURE; } // Case: "(*(" else if (*indexBracket == '(') { // printf ("Case: (*(: %s\n", *index); nsCString operation; rv = ParseOperationEntry(*index, indexBracket, getter_Copies(operation)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr e; rv = CreateBooleanExpression(operation.get(), getter_AddRefs(e)); NS_ENSURE_SUCCESS(rv, rv); // Case: "(*)(*)....(*))" *index = indexBracket; rv = ParseExpressions(index, e); NS_ENSURE_SUCCESS(rv, rv); e.forget(expression); } // Case" "(*)" else if (*indexBracket == ')') { // printf ("Case: (*): %s\n", *index); nsCOMPtr conditionString; rv = ParseCondition(index, indexBracket, getter_AddRefs(conditionString)); NS_ENSURE_SUCCESS(rv, rv); conditionString.forget(expression); } if (**index != ')') return NS_ERROR_FAILURE; (*index)++; return NS_OK; } nsresult nsAbQueryStringToExpression::ParseExpressions( const char** index, nsIAbBooleanExpression* expression) { nsresult rv; nsTArray> expressions; // Case: ")(*)(*)....(*))" // printf ("Case: )(*)(*)....(*)): %s\n", *index); while (**index == '(') { nsCOMPtr childExpression; rv = ParseExpression(index, getter_AddRefs(childExpression)); NS_ENSURE_SUCCESS(rv, rv); expressions.AppendElement(childExpression); } if (**index == 0) return NS_ERROR_FAILURE; // Case: "))" // printf ("Case: )): %s\n", *index); if (**index != ')') return NS_ERROR_FAILURE; expression->SetExpressions(expressions); return NS_OK; } nsresult nsAbQueryStringToExpression::ParseCondition( const char** index, const char* indexBracketClose, nsIAbBooleanConditionString** conditionString) { nsresult rv; (*index)++; nsCString entries[3]; for (int i = 0; i < 3; i++) { rv = ParseConditionEntry(index, indexBracketClose, getter_Copies(entries[i])); NS_ENSURE_SUCCESS(rv, rv); if (*index == indexBracketClose) break; } if (*index != indexBracketClose) return NS_ERROR_FAILURE; nsCOMPtr c; rv = CreateBooleanConditionString(entries[0].get(), entries[1].get(), entries[2].get(), getter_AddRefs(c)); NS_ENSURE_SUCCESS(rv, rv); c.forget(conditionString); return NS_OK; } nsresult nsAbQueryStringToExpression::ParseConditionEntry( const char** index, const char* indexBracketClose, char** entry) { const char* indexDeliminator = *index; while (indexDeliminator != indexBracketClose && *indexDeliminator != ',') indexDeliminator++; int entryLength = indexDeliminator - *index; if (entryLength) *entry = PL_strndup(*index, entryLength); else *entry = 0; if (indexDeliminator != indexBracketClose) *index = indexDeliminator + 1; else *index = indexDeliminator; return NS_OK; } nsresult nsAbQueryStringToExpression::ParseOperationEntry( const char* indexBracketOpen1, const char* indexBracketOpen2, char** operation) { int operationLength = indexBracketOpen2 - indexBracketOpen1 - 1; if (operationLength) *operation = PL_strndup(indexBracketOpen1 + 1, operationLength); else *operation = 0; return NS_OK; } nsresult nsAbQueryStringToExpression::CreateBooleanExpression( const char* operation, nsIAbBooleanExpression** expression) { nsAbBooleanOperationType op; if (PL_strcasecmp(operation, "and") == 0) op = nsIAbBooleanOperationTypes::AND; else if (PL_strcasecmp(operation, "or") == 0) op = nsIAbBooleanOperationTypes::OR; else if (PL_strcasecmp(operation, "not") == 0) op = nsIAbBooleanOperationTypes::NOT; else return NS_ERROR_FAILURE; nsresult rv; nsCOMPtr expr = do_CreateInstance("@mozilla.org/boolean-expression/n-peer;1", &rv); NS_ENSURE_SUCCESS(rv, rv); rv = expr->SetOperation(op); expr.forget(expression); return rv; } nsresult nsAbQueryStringToExpression::CreateBooleanConditionString( const char* attribute, const char* condition, const char* value, nsIAbBooleanConditionString** conditionString) { if (attribute == 0 || condition == 0 || value == 0) return NS_ERROR_FAILURE; nsAbBooleanConditionType c; if (PL_strcasecmp(condition, "=") == 0) c = nsIAbBooleanConditionTypes::Is; else if (PL_strcasecmp(condition, "!=") == 0) c = nsIAbBooleanConditionTypes::IsNot; else if (PL_strcasecmp(condition, "lt") == 0) c = nsIAbBooleanConditionTypes::LessThan; else if (PL_strcasecmp(condition, "gt") == 0) c = nsIAbBooleanConditionTypes::GreaterThan; else if (PL_strcasecmp(condition, "bw") == 0) c = nsIAbBooleanConditionTypes::BeginsWith; else if (PL_strcasecmp(condition, "ew") == 0) c = nsIAbBooleanConditionTypes::EndsWith; else if (PL_strcasecmp(condition, "c") == 0) c = nsIAbBooleanConditionTypes::Contains; else if (PL_strcasecmp(condition, "!c") == 0) c = nsIAbBooleanConditionTypes::DoesNotContain; else if (PL_strcasecmp(condition, "~=") == 0) c = nsIAbBooleanConditionTypes::SoundsLike; else if (PL_strcasecmp(condition, "regex") == 0) c = nsIAbBooleanConditionTypes::RegExp; else if (PL_strcasecmp(condition, "ex") == 0) c = nsIAbBooleanConditionTypes::Exists; else if (PL_strcasecmp(condition, "!ex") == 0) c = nsIAbBooleanConditionTypes::DoesNotExist; else return NS_ERROR_FAILURE; nsresult rv; nsCOMPtr cs = do_CreateInstance( "@mozilla.org/boolean-expression/condition-string;1", &rv); NS_ENSURE_SUCCESS(rv, rv); rv = cs->SetCondition(c); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsString attributeUCS2; nsString valueUCS2; rv = textToSubURI->UnEscapeAndConvert( "UTF-8"_ns, nsDependentCString(attribute), attributeUCS2); NS_ENSURE_SUCCESS(rv, rv); rv = textToSubURI->UnEscapeAndConvert("UTF-8"_ns, nsDependentCString(value), valueUCS2); NS_ENSURE_SUCCESS(rv, rv); NS_ConvertUTF16toUTF8 attributeUTF8(attributeUCS2); rv = cs->SetName(attributeUTF8.get()); NS_ENSURE_SUCCESS(rv, rv); rv = cs->SetValue(valueUCS2.get()); NS_ENSURE_SUCCESS(rv, rv); } else { NS_ConvertUTF8toUTF16 valueUCS2(value); rv = cs->SetName(attribute); NS_ENSURE_SUCCESS(rv, rv); rv = cs->SetValue(valueUCS2.get()); NS_ENSURE_SUCCESS(rv, rv); } cs.forget(conditionString); return NS_OK; }