package org.ovirt.engine.core.searchbackend; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.SqlInjectionException; import org.ovirt.engine.core.common.utils.EnumUtils; import org.ovirt.engine.core.compat.EnumCompat; import org.ovirt.engine.core.compat.IntegerCompat; import org.ovirt.engine.core.compat.LogCompat; import org.ovirt.engine.core.compat.LogFactoryCompat; import org.ovirt.engine.core.compat.RefObject; import org.ovirt.engine.core.compat.Regex; import org.ovirt.engine.core.compat.StringFormat; import org.ovirt.engine.core.compat.StringHelper; public class SyntaxChecker implements ISyntaxChecker { private SearchObjectAutoCompleter mSearchObjectAC; private BaseAutoCompleter mColonAC; private BaseAutoCompleter mPluralAC; private BaseAutoCompleter mSortbyAC; private BaseAutoCompleter mPageAC; private BaseAutoCompleter mAndAC; private BaseAutoCompleter mOrAC; private BaseAutoCompleter mDotAC; private BaseAutoCompleter mSortDirectionAC; private java.util.HashMap<SyntaxObjectType, SyntaxObjectType[]> mStateMap; private Regex mFirstDQRegexp; private Regex mNonSpaceRegexp; private java.util.ArrayList<Character> mDisAllowedChars; private static SqlInjectionChecker sqlInjectionChecker; public SyntaxChecker(int searchReasultsLimit, boolean hasDesktop) { mSearchObjectAC = new SearchObjectAutoCompleter(hasDesktop); mColonAC = new BaseAutoCompleter(":"); mPluralAC = new BaseAutoCompleter("S"); mSortbyAC = new BaseAutoCompleter("SORTBY"); mPageAC = new BaseAutoCompleter("PAGE"); mSortDirectionAC = new BaseAutoCompleter(new String[] { "ASC", "DESC" }); mAndAC = new BaseAutoCompleter("AND"); mOrAC = new BaseAutoCompleter("OR"); mDotAC = new BaseAutoCompleter("."); mDisAllowedChars = new java.util.ArrayList<Character>(java.util.Arrays.asList(new Character[] { '\'', ';' })); mFirstDQRegexp = new Regex("^\\s*\"$"); mNonSpaceRegexp = new Regex("^\\S+$"); mStateMap = new java.util.HashMap<SyntaxObjectType, SyntaxObjectType[]>(); mStateMap.put(SyntaxObjectType.BEGIN, new SyntaxObjectType[] { SyntaxObjectType.SEARCH_OBJECT }); mStateMap.put(SyntaxObjectType.SEARCH_OBJECT, new SyntaxObjectType[] { SyntaxObjectType.COLON }); SyntaxObjectType[] afterColon = { SyntaxObjectType.CROSS_REF_OBJ, SyntaxObjectType.CONDITION_FIELD, SyntaxObjectType.SORTBY, SyntaxObjectType.PAGE, SyntaxObjectType.CONDITION_VALUE, SyntaxObjectType.END }; mStateMap.put(SyntaxObjectType.COLON, afterColon); SyntaxObjectType[] afterCrossRefObj = { SyntaxObjectType.DOT, SyntaxObjectType.CONDITION_RELATION }; mStateMap.put(SyntaxObjectType.CROSS_REF_OBJ, afterCrossRefObj); mStateMap.put(SyntaxObjectType.DOT, new SyntaxObjectType[] { SyntaxObjectType.CONDITION_FIELD }); mStateMap.put(SyntaxObjectType.CONDITION_FIELD, new SyntaxObjectType[] { SyntaxObjectType.CONDITION_RELATION }); mStateMap.put(SyntaxObjectType.CONDITION_RELATION, new SyntaxObjectType[] { SyntaxObjectType.CONDITION_VALUE }); SyntaxObjectType[] afterConditionValue = { SyntaxObjectType.OR, SyntaxObjectType.AND, SyntaxObjectType.CROSS_REF_OBJ, SyntaxObjectType.CONDITION_FIELD, SyntaxObjectType.SORTBY, SyntaxObjectType.PAGE, SyntaxObjectType.CONDITION_VALUE }; mStateMap.put(SyntaxObjectType.CONDITION_VALUE, afterConditionValue); SyntaxObjectType[] AndOrArray = { SyntaxObjectType.CROSS_REF_OBJ, SyntaxObjectType.CONDITION_FIELD, SyntaxObjectType.CONDITION_VALUE }; mStateMap.put(SyntaxObjectType.AND, AndOrArray); mStateMap.put(SyntaxObjectType.OR, AndOrArray); mStateMap.put(SyntaxObjectType.SORTBY, new SyntaxObjectType[] { SyntaxObjectType.SORT_FIELD }); mStateMap.put(SyntaxObjectType.SORT_FIELD, new SyntaxObjectType[] { SyntaxObjectType.SORT_DIRECTION }); mStateMap.put(SyntaxObjectType.SORT_DIRECTION, new SyntaxObjectType[] { SyntaxObjectType.PAGE }); mStateMap.put(SyntaxObjectType.PAGE, new SyntaxObjectType[] { SyntaxObjectType.PAGE_VALUE }); mStateMap.put(SyntaxObjectType.PAGE_VALUE, new SyntaxObjectType[] { SyntaxObjectType.END }); // get sql injection checker for active database engine. try { sqlInjectionChecker = getSqlInjectionChecker(); } catch (Exception e) { log.errorFormat("Failed to load Sql Injection Checker. {0}", e.getMessage()); } } private enum ValueParseResult { Err, Normal, FreeText; public int getValue() { return this.ordinal(); } public static ValueParseResult forValue(int value) { return values()[value]; } } // VB & C# TO JAVA CONVERTER TODO TASK: final is a keyword in Java. Change // the name: private ValueParseResult handleValuePhrase(boolean final2, String searchText, int idx, RefObject<Integer> startPos, SyntaxContainer container) { boolean addObjFlag = false; ValueParseResult retval = ValueParseResult.Normal; IConditionFieldAutoCompleter curConditionFieldAC; char curChar = searchText.charAt(idx); String strRealObj = searchText.substring(startPos.argvalue, idx + 1); boolean betweenDoubleQuotes = searchText.substring(startPos.argvalue, idx).contains("\""); if (curChar == '"') { betweenDoubleQuotes = (!betweenDoubleQuotes); if (betweenDoubleQuotes) { if (!mFirstDQRegexp.IsMatch(strRealObj)) { container.setErr(SyntaxError.INVALID_CONDITION_VALUE, startPos.argvalue, idx + 1); return ValueParseResult.Err; } } else { strRealObj = StringHelper.trim(strRealObj, new char[] { '\"' }); addObjFlag = true; } } // Doing this condition to identify whether this is the last // searchObject and no space is predicted !! if (final2) { if (((curChar == ' ') || (idx + 1 == searchText.length())) && (betweenDoubleQuotes == false) && (addObjFlag == false)) { strRealObj = strRealObj.trim(); if (mNonSpaceRegexp.IsMatch(strRealObj)) { addObjFlag = true; } else { startPos.argvalue = idx + 1; } } } else { if ((curChar == ' ') && (betweenDoubleQuotes == false) && (addObjFlag == false)) { strRealObj = strRealObj.trim(); if (mNonSpaceRegexp.IsMatch(strRealObj)) { addObjFlag = true; } else { startPos.argvalue = idx + 1; } } } if (addObjFlag) { String curRefObj = container.getPreviousSyntaxObject(3, SyntaxObjectType.CROSS_REF_OBJ); String curConditionField = container.getPreviousSyntaxObject(1, SyntaxObjectType.CONDITION_FIELD); curConditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(curRefObj); if (curConditionFieldAC == null) { container.setErr(SyntaxError.CANT_GET_CONDITION_FIELD_AC, startPos.argvalue, idx); return ValueParseResult.Err; } if ((!StringHelper.EqOp(curConditionField, "")) && (!curConditionFieldAC.validateFieldValue(curConditionField, strRealObj))) { container.setErr(SyntaxError.INVALID_CONDITION_VALUE, startPos.argvalue, idx); return ValueParseResult.Err; } container.addSyntaxObject(SyntaxObjectType.CONDITION_VALUE, strRealObj, startPos.argvalue, idx + 1); retval = ValueParseResult.FreeText; startPos.argvalue = idx + 1; container.setvalid(true); } return retval; } /** * gets the sql injection checker class for current db vendor. * @return SqlInjectionChecker * @throws Exception */ private SqlInjectionChecker getSqlInjectionChecker() throws Exception { // This can not be done with reflection like: // return (SqlInjectionChecker) Class.forName(props.getProperty(SQL_INJECTION)).newInstance(); // GWT lacks support of reflection. if (((String)Config.GetValue(ConfigValues.DBEngine)).equalsIgnoreCase("postgres")){ return new PostgresSqlInjectionChecker(); } else { throw new IllegalStateException("Failed to get correct sql injection checker instance name :" + SqlInjectionChecker.class); } } // VB & C# TO JAVA CONVERTER TODO TASK: final is a keyword in Java. Change // the name: public SyntaxContainer analyzeSyntaxState(String searchText, boolean final2) { SyntaxContainer retval = new SyntaxContainer(searchText); IConditionFieldAutoCompleter curConditionFieldAC = null; IAutoCompleter curConditionRelationAC = null; java.util.ArrayList<String> freeTextObjSearched = new java.util.ArrayList<String>(); char[] searchCharArr = searchText.toCharArray(); int curStartPos = 0; String tryNextObj = ""; boolean keepValid; for (int idx = 0; idx < searchCharArr.length; idx++) { SyntaxObjectType curState = retval.getState(); char curChar = searchCharArr[idx]; if (mDisAllowedChars.contains(curChar)) { retval.setErr(SyntaxError.INVALID_CHARECTER, curStartPos, idx + 1); return retval; } if ((curChar == ' ') && (curState != SyntaxObjectType.CONDITION_RELATION) && (curState != SyntaxObjectType.COLON) && (curState != SyntaxObjectType.CONDITION_VALUE) && (curState != SyntaxObjectType.OR) && (curState != SyntaxObjectType.AND)) { curStartPos += 1; continue; } String strRealObj = searchText.substring(curStartPos, idx + 1); String nextObject = strRealObj.toUpperCase(); switch (curState) { case BEGIN: // we have found a search-object if (!mSearchObjectAC.validate(nextObject)) { if (!mSearchObjectAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.INVALID_SEARCH_OBJECT, curStartPos, idx - curStartPos + 1); return retval; } } else { if (searchCharArr.length >= idx + 2) // Check that this // maybe a plural { // Validate that the next character is an 's' if (mPluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) { // Then just move things along. idx++; StringBuilder sb = new StringBuilder(nextObject); sb.append('S'); nextObject = sb.toString(); } } retval.addSyntaxObject(SyntaxObjectType.SEARCH_OBJECT, nextObject, curStartPos, idx + 1); retval.setvalid(true); curStartPos = idx + 1; } break; case SEARCH_OBJECT: if (!mColonAC.validate(nextObject)) { if (!mColonAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.COLON_NOT_NEXT_TO_SEARCH_OBJECT, curStartPos, idx + 1); return retval; } } else { retval.addSyntaxObject(SyntaxObjectType.COLON, nextObject, idx, idx + 1); curStartPos = idx + 1; retval.setvalid(true); } break; case CROSS_REF_OBJ: String curRefObj = retval.getPreviousSyntaxObject(0, SyntaxObjectType.CROSS_REF_OBJ); curConditionRelationAC = mSearchObjectAC.getObjectRelationshipAutoCompleter(curRefObj); if (idx + 1 < searchCharArr.length) { tryNextObj = searchText.substring(curStartPos, idx + 2).toUpperCase(); } if (curConditionRelationAC == null) { retval.setErr(SyntaxError.CONDITION_CANT_CREATE_RRELATIONS_AC, curStartPos, idx + 1); return retval; } if (mDotAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.DOT, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if ((!StringHelper.EqOp(tryNextObj, "")) && (curConditionRelationAC.validate(tryNextObj))) { break; // i.e. the relation object has another charecter } else if (curConditionRelationAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.CONDITION_RELATION, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if ((!curConditionRelationAC.validateCompletion(nextObject)) && (!mDotAC.validateCompletion(nextObject))) { retval.setErr(SyntaxError.INVALID_POST_CROSS_REF_OBJ, curStartPos, idx + 1); return retval; } tryNextObj = ""; break; case DOT: curRefObj = retval.getPreviousSyntaxObject(1, SyntaxObjectType.CROSS_REF_OBJ); curConditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(curRefObj); if (curConditionFieldAC == null) { retval.setErr(SyntaxError.CANT_GET_CONDITION_FIELD_AC, curStartPos, idx); return retval; } if (!curConditionFieldAC.validate(nextObject)) { if (!curConditionFieldAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.INVALID_CONDITION_FILED, curStartPos, idx + 1); return retval; } } else { retval.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } break; case AND: case OR: keepValid = false; curConditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(retval.getSearchObjectStr()); if (curConditionFieldAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mSearchObjectAC.isCrossReferece(nextObject, retval.getFirst().getBody())) { if (searchCharArr.length >= idx + 2) // Check that this // maybe a plural { // Validate that the next character is an 's' if (mPluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) { // Then just move things along. idx++; StringBuilder sb = new StringBuilder(nextObject); sb.append('S'); nextObject = sb.toString(); } } retval.addSyntaxObject(SyntaxObjectType.CROSS_REF_OBJ, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else { RefObject<Integer> tempRefObject = new RefObject<Integer>(curStartPos); ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject, retval); curStartPos = tempRefObject.argvalue; if (ans != ValueParseResult.Err) { if (ans == ValueParseResult.FreeText) { curRefObj = retval.getSearchObjectStr(); if (freeTextObjSearched.contains(curRefObj)) { retval.setErr(SyntaxError.FREE_TEXT_ALLOWED_ONCE_PER_OBJ, curStartPos, idx + 1); return retval; } freeTextObjSearched.add(curRefObj); retval.setvalid(true); keepValid = true; } } else if ((!curConditionFieldAC.validateCompletion(nextObject)) && (!mSearchObjectAC.validateCompletion(nextObject))) { retval.setErr(SyntaxError.INVALID_POST_OR_AND_PHRASE, curStartPos, idx + 1); return retval; } } if (keepValid == false) { retval.setvalid(false); } break; case COLON: keepValid = false; curConditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(retval.getSearchObjectStr()); if (curConditionFieldAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mSortbyAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.SORTBY, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mPageAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.PAGE, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mSearchObjectAC.isCrossReferece(nextObject, retval.getFirst().getBody())) { if (searchCharArr.length >= idx + 2) // Check that this // maybe a plural { // Validate that the next character is an 's' if (mPluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) { // Then just move things along. idx++; StringBuilder sb = new StringBuilder(nextObject); sb.append('S'); nextObject = sb.toString(); } } retval.addSyntaxObject(SyntaxObjectType.CROSS_REF_OBJ, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else { RefObject<Integer> tempRefObject2 = new RefObject<Integer>(curStartPos); ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject2, retval); curStartPos = tempRefObject2.argvalue; if (ans != ValueParseResult.Err) { if (ans == ValueParseResult.FreeText) { freeTextObjSearched.add(retval.getSearchObjectStr()); } keepValid = true; } else if ((!curConditionFieldAC.validateCompletion(nextObject)) && (!mSortbyAC.validateCompletion(nextObject)) && (!mSearchObjectAC.validateCompletion(nextObject))) { retval.setErr(SyntaxError.INVALID_POST_COLON_PHRASE, curStartPos, idx + 1); return retval; } } if (keepValid == false) { retval.setvalid(false); } break; case CONDITION_VALUE: nextObject = nextObject.trim(); if (nextObject.length() > 0) { keepValid = false; curRefObj = retval.getSearchObjectStr(); curConditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(curRefObj); if (curConditionFieldAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.CONDITION_FIELD, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mSortbyAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.SORTBY, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mPageAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.PAGE, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mSearchObjectAC.isCrossReferece(nextObject, retval.getFirst().getBody())) { if (searchCharArr.length >= idx + 2) // Check that this // maybe a // plural { // Validate that the next character is an 's' if (mPluralAC.validate(searchText.substring(idx + 1, idx + 1 + 1))) { // Then just move things along. idx++; StringBuilder sb = new StringBuilder(nextObject); sb.append('S'); nextObject = sb.toString(); } } retval.addSyntaxObject(SyntaxObjectType.CROSS_REF_OBJ, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mAndAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.AND, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if (mOrAC.validate(nextObject)) { retval.addSyntaxObject(SyntaxObjectType.OR, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; } else if ((!curConditionFieldAC.validateCompletion(nextObject)) && (!mSortbyAC.validateCompletion(nextObject)) && (!mSearchObjectAC.validateCompletion(nextObject)) && (!mAndAC.validateCompletion(nextObject)) && (!mOrAC.validateCompletion(nextObject))) { RefObject<Integer> tempRefObject3 = new RefObject<Integer>(curStartPos); ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject3, retval); curStartPos = tempRefObject3.argvalue; if (ans != ValueParseResult.Err) { if (ans == ValueParseResult.FreeText) { if (freeTextObjSearched.contains(curRefObj)) { retval.setErr(SyntaxError.FREE_TEXT_ALLOWED_ONCE_PER_OBJ, curStartPos, idx + 1); return retval; } freeTextObjSearched.add(curRefObj); retval.setvalid(true); keepValid = true; } } else { retval.setErr(SyntaxError.INVALID_POST_CONDITION_VALUE_PHRASE, curStartPos, idx + 1); return retval; } } if (keepValid == false) { retval.setvalid(false); } } break; case CONDITION_FIELD: curRefObj = retval.getPreviousSyntaxObject(2, SyntaxObjectType.CROSS_REF_OBJ); String curConditionField = retval.getPreviousSyntaxObject(0, SyntaxObjectType.CONDITION_FIELD); curConditionRelationAC = mSearchObjectAC .getFieldRelationshipAutoCompleter(curRefObj, curConditionField); if (curConditionRelationAC == null) { retval.setErr(SyntaxError.CONDITION_CANT_CREATE_RRELATIONS_AC, curStartPos, idx + 1); return retval; } if (idx + 1 < searchCharArr.length) { tryNextObj = searchText.substring(curStartPos, idx + 2).toUpperCase(); if (curConditionRelationAC.validate(tryNextObj)) { break; } } if (!curConditionRelationAC.validate(nextObject)) { if (!curConditionRelationAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.INVALID_CONDITION_RELATION, curStartPos, idx + 1); return retval; } } else { retval.addSyntaxObject(SyntaxObjectType.CONDITION_RELATION, nextObject, curStartPos, idx + 1); } curStartPos = idx + 1; retval.setvalid(false); tryNextObj = ""; break; case CONDITION_RELATION: { RefObject<Integer> tempRefObject4 = new RefObject<Integer>(curStartPos); ValueParseResult ans = handleValuePhrase(final2, searchText, idx, tempRefObject4, retval); curStartPos = tempRefObject4.argvalue; if (ans == ValueParseResult.Err) { return retval; } if (ans == ValueParseResult.FreeText) { if (retval.getPreviousSyntaxObjectType(2) == SyntaxObjectType.CROSS_REF_OBJ) { curRefObj = retval.getObjSingularName(retval.getPreviousSyntaxObject(2, SyntaxObjectType.CROSS_REF_OBJ)); if (freeTextObjSearched.contains(curRefObj)) { retval.setErr(SyntaxError.FREE_TEXT_ALLOWED_ONCE_PER_OBJ, curStartPos, idx + 1); return retval; } freeTextObjSearched.add(curRefObj); } } } break; case SORTBY: curConditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(retval.getSearchObjectStr()); if (!curConditionFieldAC.validate(nextObject)) { if (!curConditionFieldAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.INVALID_SORT_FIELD, curStartPos, idx + 1); return retval; } } else { retval.addSyntaxObject(SyntaxObjectType.SORT_FIELD, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; retval.setvalid(true); } break; case PAGE: Integer pageNumber = new Integer(0); RefObject<Integer> tempRefObject5 = new RefObject<Integer>(pageNumber); boolean tempVar = !IntegerCompat.TryParse(nextObject, tempRefObject5); pageNumber = tempRefObject5.argvalue; if (tempVar) { retval.setErr(SyntaxError.INVALID_CHARECTER, curStartPos, idx + 1); return retval; } else { final StringBuilder buff = new StringBuilder(); int pos = idx; // parsing the whole page number (can be more than one char) while (pos < searchText.length() - 1 && Character.isDigit(nextObject.charAt(0))) { buff.append(nextObject); pos++; strRealObj = searchText.substring(pos, pos + 1); nextObject = strRealObj.toUpperCase(); } buff.append(nextObject); retval.addSyntaxObject(SyntaxObjectType.PAGE_VALUE, buff.toString(), curStartPos, idx + buff.length()); // update index position idx = pos + 1; retval.setvalid(true); } break; case SORT_FIELD: if (!mSortDirectionAC.validate(nextObject)) { if (!mSortDirectionAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.INVALID_SORT_DIRECTION, curStartPos, idx + 1); return retval; } } else { retval.addSyntaxObject(SyntaxObjectType.SORT_DIRECTION, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; retval.setvalid(true); } break; case PAGE_VALUE: if (curChar != ' ') { retval.setErr(SyntaxError.NOTHING_COMES_AFTER_PAGE_VALUE, curStartPos, idx + 1); return retval; } break; case SORT_DIRECTION: if (!mPageAC.validate(nextObject)) { if (!mPageAC.validateCompletion(nextObject)) { retval.setErr(SyntaxError.INVALID_PAGE_FEILD, curStartPos, idx); return retval; } } else { retval.addSyntaxObject(SyntaxObjectType.PAGE, nextObject, curStartPos, idx + 1); curStartPos = idx + 1; retval.setvalid(true); } break; default: retval.setErr(SyntaxError.UNIDENTIFIED_STATE, curStartPos, idx); return retval; } } return retval; } public SyntaxContainer getCompletion(String searchText) { SyntaxContainer retval = analyzeSyntaxState(searchText, false); if (retval.getError() == SyntaxError.NO_ERROR) { IConditionFieldAutoCompleter conditionFieldAC; IAutoCompleter conditionRelationAC; IConditionValueAutoCompleter conditionValueAC; int lastIdx = retval.getLastHandledIndex(); String curPartialWord = ""; if (lastIdx < searchText.length()) { curPartialWord = searchText.substring(lastIdx, searchText.length()); curPartialWord = curPartialWord.trim(); } SyntaxObjectType curState = retval.getState(); for (int idx = 0; idx < mStateMap.get(curState).length; idx++) { switch (mStateMap.get(curState)[idx]) { case SEARCH_OBJECT: retval.addToACList(mSearchObjectAC.getCompletion(curPartialWord)); break; case CROSS_REF_OBJ: IAutoCompleter crossRefAC = mSearchObjectAC.getCrossRefAutoCompleter(retval.getFirst().getBody()); if (crossRefAC != null) { retval.addToACList(crossRefAC.getCompletion(curPartialWord)); } break; case DOT: retval.addToACList(mDotAC.getCompletion(curPartialWord)); break; case COLON: retval.addToACList(mColonAC.getCompletion(curPartialWord)); break; case AND: retval.addToACList(mAndAC.getCompletion(curPartialWord)); break; case OR: retval.addToACList(mOrAC.getCompletion(curPartialWord)); break; case CONDITION_FIELD: String relObj = retval.getPreviousSyntaxObject(1, SyntaxObjectType.CROSS_REF_OBJ); conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(relObj); if (conditionFieldAC != null) { retval.addToACList(conditionFieldAC.getCompletion(curPartialWord)); } break; case CONDITION_RELATION: { if (curState == SyntaxObjectType.CONDITION_FIELD) { relObj = retval.getPreviousSyntaxObject(2, SyntaxObjectType.CROSS_REF_OBJ); String fldName = retval.getPreviousSyntaxObject(0, SyntaxObjectType.CONDITION_FIELD); conditionRelationAC = mSearchObjectAC.getFieldRelationshipAutoCompleter(relObj, fldName); } else // curState == SyntaxObjectType.CROSS_REF_OBJ { relObj = retval.getPreviousSyntaxObject(0, SyntaxObjectType.CROSS_REF_OBJ); conditionRelationAC = mSearchObjectAC.getObjectRelationshipAutoCompleter(relObj); } if (conditionRelationAC != null) { retval.addToACList(conditionRelationAC.getCompletion(curPartialWord)); } } break; case CONDITION_VALUE: { relObj = retval.getPreviousSyntaxObject(3, SyntaxObjectType.CROSS_REF_OBJ); String fldName = retval.getPreviousSyntaxObject(1, SyntaxObjectType.CONDITION_FIELD); conditionValueAC = mSearchObjectAC.getFieldValueAutoCompleter(relObj, fldName); if (conditionValueAC != null) { retval.addToACList(conditionValueAC.getCompletion(curPartialWord)); } } break; case SORTBY: retval.addToACList(mSortbyAC.getCompletion(curPartialWord)); break; case PAGE: retval.addToACList(mPageAC.getCompletion(curPartialWord)); break; case SORT_FIELD: conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(retval.getSearchObjectStr()); if (conditionFieldAC != null) { retval.addToACList(conditionFieldAC.getCompletion(curPartialWord)); } break; case SORT_DIRECTION: retval.addToACList(mSortDirectionAC.getCompletion(curPartialWord)); break; } } } return retval; } public String generateQueryFromSyntaxContainer(SyntaxContainer syntax, boolean isSafe) { String retval = ""; if (syntax.getvalid()) { retval = generateSqlFromSyntaxContainer(syntax, isSafe); } return retval; } private String generateFromStatement(SyntaxContainer syntax) { java.util.LinkedList<String> innerJoins = new java.util.LinkedList<String>(); java.util.ArrayList<String> refObjList = syntax.getCrossRefObjList(); String searchObjStr = syntax.getSearchObjectStr(); if (refObjList.size() > 0) { // VB & C# TO JAVA CONVERTER NOTE: The following 'switch' operated // on a string member and was converted to Java 'if-else' logic: // switch (searchObjStr) // ORIGINAL LINE: case SearchObjects.TEMPLATE_OBJ_NAME: if (StringHelper.EqOp(searchObjStr, SearchObjects.TEMPLATE_OBJ_NAME)) { innerJoins.addFirst(mSearchObjectAC.getInnerJoin(SearchObjects.TEMPLATE_OBJ_NAME, SearchObjects.VM_OBJ_NAME)); if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) { refObjList.remove(SearchObjects.VM_OBJ_NAME); } if (refObjList.contains(SearchObjects.VDC_USER_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.VDC_USER_OBJ_NAME)); refObjList.remove(SearchObjects.VDC_USER_OBJ_NAME); } if (refObjList.contains(SearchObjects.VDS_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.VDS_OBJ_NAME)); refObjList.remove(SearchObjects.VDS_OBJ_NAME); } if (refObjList.contains(SearchObjects.AUDIT_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.AUDIT_OBJ_NAME)); refObjList.remove(SearchObjects.AUDIT_OBJ_NAME); } } // ORIGINAL LINE: case SearchObjects.VDS_OBJ_NAME: else if (StringHelper.EqOp(searchObjStr, SearchObjects.VDS_OBJ_NAME)) { if ((refObjList.contains(SearchObjects.VDC_USER_OBJ_NAME)) || (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME))) { innerJoins.addFirst(mSearchObjectAC.getInnerJoin(SearchObjects.VDS_OBJ_NAME, SearchObjects.VM_OBJ_NAME)); if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) { refObjList.remove(SearchObjects.VM_OBJ_NAME); } } if (refObjList.contains(SearchObjects.VDC_USER_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.VDC_USER_OBJ_NAME)); refObjList.remove(SearchObjects.VDC_USER_OBJ_NAME); } if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.TEMPLATE_OBJ_NAME)); refObjList.remove(SearchObjects.TEMPLATE_OBJ_NAME); } } // ORIGINAL LINE: case SearchObjects.VDC_USER_OBJ_NAME: else if (StringHelper.EqOp(searchObjStr, SearchObjects.VDC_USER_OBJ_NAME)) { if ((refObjList.contains(SearchObjects.VDS_OBJ_NAME)) || (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME))) { innerJoins.addFirst(mSearchObjectAC.getInnerJoin(SearchObjects.VDC_USER_OBJ_NAME, SearchObjects.VM_OBJ_NAME)); if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) { refObjList.remove(SearchObjects.VM_OBJ_NAME); } } if (refObjList.contains(SearchObjects.VDS_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.VDS_OBJ_NAME)); refObjList.remove(SearchObjects.VDS_OBJ_NAME); } if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.TEMPLATE_OBJ_NAME)); refObjList.remove(SearchObjects.TEMPLATE_OBJ_NAME); } } // ORIGINAL LINE: case SearchObjects.AUDIT_OBJ_NAME: else if (StringHelper.EqOp(searchObjStr, SearchObjects.AUDIT_OBJ_NAME)) { if (refObjList.contains(SearchObjects.TEMPLATE_OBJ_NAME)) { innerJoins.addFirst(mSearchObjectAC.getInnerJoin(SearchObjects.AUDIT_OBJ_NAME, SearchObjects.VM_OBJ_NAME)); innerJoins.addLast(mSearchObjectAC.getInnerJoin(SearchObjects.VM_OBJ_NAME, SearchObjects.TEMPLATE_OBJ_NAME)); refObjList.remove(SearchObjects.TEMPLATE_OBJ_NAME); if (refObjList.contains(SearchObjects.VM_OBJ_NAME)) { refObjList.remove(SearchObjects.VM_OBJ_NAME); } } } } for (String cro : refObjList) { innerJoins.addLast(mSearchObjectAC.getInnerJoin(searchObjStr, cro)); } innerJoins.addFirst(mSearchObjectAC.getRelatedTableName(searchObjStr)); StringBuilder sb = new StringBuilder(); for (String part : innerJoins) { sb.append(" "); sb.append(part); sb.append(" "); } return sb.toString(); } private String generateSqlFromSyntaxContainer(SyntaxContainer syntax, boolean isSafe) { String retval = ""; if (syntax.getvalid()) { java.util.ListIterator<SyntaxObject> objIter = syntax.listIterator(0); IConditionFieldAutoCompleter conditionFieldAC; java.util.LinkedList<String> whereBuilder = new java.util.LinkedList<String>(); String searchObjStr = syntax.getSearchObjectStr(); String sortByPhrase = ""; String fromStatement = ""; String pageNumber = ""; while (objIter.hasNext()) { SyntaxObject obj = objIter.next(); switch (obj.getType()) { case SEARCH_OBJECT: fromStatement = generateFromStatement(syntax); break; case OR: case AND: whereBuilder.addLast(obj.getBody()); break; case CONDITION_VALUE: whereBuilder.addLast(generateConditionStatment(obj, syntax.listIterator(objIter.previousIndex()), searchObjStr, syntax.getCaseSensitive(),isSafe)); break; case SORTBY: break; case PAGE_VALUE: pageNumber = obj.getBody(); break; case SORT_FIELD: conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(searchObjStr); sortByPhrase = StringFormat.format(" ORDER BY %1$s", conditionFieldAC.getDbFieldName(obj.getBody())); break; case SORT_DIRECTION: sortByPhrase = StringFormat.format("%1$s %2$s", sortByPhrase, obj.getBody()); break; default: break; } } // implying precedence rules String[] lookFor = { "AND", "OR" }; for (int idx = 0; idx < lookFor.length; idx++) { boolean found = true; while (found) { found = false; java.util.ListIterator<String> iter = whereBuilder.listIterator(0); while (iter.hasNext()) { String queryPart = iter.next(); if (StringHelper.EqOp(queryPart, lookFor[idx])) { iter.remove(); String nextPart = iter.next(); iter.remove(); String prevPart = iter.previous(); iter.set(StringFormat.format("( %1$s %2$s %3$s )", prevPart, queryPart, nextPart)); found = true; break; } } } } // adding WHERE if required and All implicit AND StringBuilder wherePhrase = new StringBuilder(); if (whereBuilder.size() > 0) { wherePhrase.append(" WHERE "); java.util.ListIterator<String> iter = whereBuilder.listIterator(0); while (iter.hasNext()) { String queryPart = iter.next(); wherePhrase.append(queryPart); if (iter.hasNext()) { wherePhrase.append(" AND "); } } } // adding the sorting part if required if (StringHelper.EqOp(sortByPhrase, "")) { sortByPhrase = StringFormat.format(" ORDER BY %1$s", mSearchObjectAC.getDefaultSort(searchObjStr)); } // adding the paging phrase String pagePhrase = getPagePhrase(syntax, pageNumber); String primeryKey = mSearchObjectAC.getPrimeryKeyName(searchObjStr); String tableName = mSearchObjectAC.getRelatedTableName(searchObjStr); String tableNameWithOutTags = mSearchObjectAC.getRelatedTableNameWithOutTags(searchObjStr); String innerQuery = StringFormat.format("SELECT %1$s.%2$s FROM %3$s %4$s", tableName, primeryKey, fromStatement, wherePhrase); // only audit log search supports the SearchFrom which enables getting records starting from a certain // audit_log_id, this is done to make search queries from the client more efficient and eliminate the client // from registering to such queries and comparing last data with previous. String inQuery = (primeryKey.equals("audit_log_id") ? StringFormat.format("SELECT * FROM %1$s WHERE ( %2$s > %3$s and %2$s IN (%4$s)", tableNameWithOutTags, primeryKey, syntax.getSearchFrom(), innerQuery) : StringFormat.format("SELECT * FROM %1$s WHERE ( %2$s IN (%3$s)", tableNameWithOutTags, primeryKey, innerQuery)); retval = StringFormat.format(Config.<String> GetValue(ConfigValues.DBSearchTemplate), sortByPhrase, inQuery, pagePhrase); // Check for sql injection if query is not safe if (! isSafe) { if (sqlInjectionChecker.hasSqlInjection(retval)) { throw new SqlInjectionException(); } } log.traceFormat("Search: {0}", retval); } return retval; } private String getPagePhrase(SyntaxContainer syntax, String pageNumber) { String result = ""; Integer page = new Integer(0); RefObject<Integer> tempRefObject = new RefObject<Integer>(page); boolean tempVar = !IntegerCompat.TryParse(pageNumber, tempRefObject); page = tempRefObject.argvalue; if (tempVar) { page = 1; } String pagingTypeStr = Config.<String> GetValue(ConfigValues.DBPagingType); if (EnumCompat.IsDefined(PagingType.class, pagingTypeStr)) { PagingType pagingType = EnumUtils.valueOf(PagingType.class, pagingTypeStr, true); String pagingSyntax = Config.<String> GetValue(ConfigValues.DBPagingSyntax); switch (pagingType) { case Range: result = StringFormat .format(pagingSyntax, (page - 1) * syntax.getMaxCount() + 1, page * syntax.getMaxCount()); break; case Offset: result = StringFormat.format(pagingSyntax, (page - 1) * syntax.getMaxCount() + 1, syntax.getMaxCount()); break; } } else { log.error(StringFormat.format("Unknown paging type %1$s", pagingTypeStr)); } return result; } private enum ConditionType { None, FreeText, FreeTextSpecificObj, ConditionWithDefaultObj, ConditionwithSpesificObj; public int getValue() { return this.ordinal(); } public static ConditionType forValue(int value) { return values()[value]; } } private String generateConditionStatment(SyntaxObject obj, java.util.ListIterator<SyntaxObject> objIter, String searchObjStr, boolean caseSensitive, boolean issafe) { IConditionFieldAutoCompleter conditionFieldAC; IConditionValueAutoCompleter conditionValueAC = null; // check for sql injection String originalValue = obj.getBody(); String customizedValue = originalValue; if (!issafe) { // Enforce escape characters before special characters customizedValue = SqlInjectionChecker.enforceEscapeCharacters(originalValue); } String customizedRelation; String fieldName = ""; String objName; ConditionType conditionType; SyntaxObject prev = objIter.previous(); if (prev.getType() != SyntaxObjectType.CONDITION_RELATION) { // free text of default search object customizedRelation = "="; objName = searchObjStr; conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(searchObjStr); conditionType = ConditionType.FreeText; } else { customizedRelation = prev.getBody(); prev = objIter.previous(); if (prev.getType() == SyntaxObjectType.CROSS_REF_OBJ) { // free text // search // for some // object objName = prev.getBody(); conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(objName); conditionType = ConditionType.FreeTextSpecificObj; } else // if (prev.getType() == SyntaxObjectType.CONDITION_FIELD) { fieldName = prev.getBody(); prev = objIter.previous(); if (prev.getType() != SyntaxObjectType.DOT) { // standard condition with default AC (search obj) objName = searchObjStr; conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(searchObjStr); conditionType = ConditionType.ConditionWithDefaultObj; } else { // standard condition with specific AC prev = objIter.previous(); objName = prev.getBody(); conditionFieldAC = mSearchObjectAC.getFieldAutoCompleter(objName); conditionType = ConditionType.ConditionwithSpesificObj; } } conditionValueAC = conditionFieldAC.getFieldValueAutoCompleter(fieldName); } BaseConditionFieldAutoCompleter conditionAsBase = (BaseConditionFieldAutoCompleter) ((conditionFieldAC instanceof BaseConditionFieldAutoCompleter) ? conditionFieldAC : null); java.lang.Class curType = null; if (conditionAsBase != null && (curType = conditionAsBase.getTypeDictionary().get(fieldName)) != null && curType == String.class && !StringHelper.isNullOrEmpty(customizedValue) && !StringHelper.EqOp(customizedValue, "''") && !StringHelper.EqOp(customizedValue, "'*'")) { customizedValue = StringFormat.format(BaseConditionFieldAutoCompleter.getI18NPrefix() + "%1$s", customizedValue); } if (conditionValueAC != null) { customizedValue = StringFormat.format("'%1$s'", conditionValueAC.convertFieldEnumValueToActualValue(obj.getBody())); } else if (fieldName.equals("") /* search on all relevant fields */ || (conditionFieldAC.getDbFieldType(fieldName).equals(String.class))) { customizedValue = customizedValue.replace('*', '%'); /* enable case-insensitive search by changing operation to I/LIKE*/ if (StringHelper.EqOp(customizedRelation, "=")) { customizedRelation = BaseConditionFieldAutoCompleter.getLikeSyntax(caseSensitive); } else if (StringHelper.EqOp(customizedRelation, "!=")) { customizedRelation = "NOT " + BaseConditionFieldAutoCompleter.getLikeSyntax(caseSensitive); } } String condition = ""; String tableName = mSearchObjectAC.getRelatedTableName(objName); switch (conditionType) { case FreeText: case FreeTextSpecificObj: condition = conditionFieldAC.buildFreeTextConditionSql(tableName, customizedRelation, customizedValue, caseSensitive); break; case ConditionWithDefaultObj: case ConditionwithSpesificObj: condition = conditionFieldAC.buildConditionSql(fieldName, customizedValue, customizedRelation, tableName, caseSensitive); break; } return condition; } private static LogCompat log = LogFactoryCompat.getLog(SyntaxChecker.class); }