Ejemplo n.º 1
0
        /// <summary>
        /// Obtains the condition parameter, which could either be a condition variable or a
        /// server variable. Assumes the current character is immediately after the '%'.
        /// context, on return will be on the last character of variable captured, such that after
        /// Next() is called, it will be on the character immediately after the condition parameter.
        /// </summary>
        /// <param name="context">The ParserContext</param>
        /// <param name="results">The List of results which the new condition parameter will be added.</param>
        /// <returns>true </returns>
        private static void ParseConditionParameter(ParserContext context, IList <PatternSegment> results)
        {
            // Parse { }
            if (context.Current == OpenBrace)
            {
                // Start of a server variable
                if (!context.Next())
                {
                    // Dangling {
                    throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
                }
                context.Mark();
                while (context.Current != CloseBrace)
                {
                    if (!context.Next())
                    {
                        throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
                    }
                    else if (context.Current == Colon)
                    {
                        // Have a segmented look up Ex: HTTP:xxxx
                        // Most of these we can't handle
                        throw new NotImplementedException("Segmented Lookups no implemented");
                    }
                }

                // Need to verify server variable captured exists
                var rawServerVariable = context.Capture();
                results.Add(ServerVariables.FindServerVariable(rawServerVariable, context));
            }
            else if (context.Current >= '0' && context.Current <= '9')
            {
                // means we have a segmented lookup
                // store information in the testString result to know what to look up.
                context.Mark();
                context.Next();
                var rawConditionParameter = context.Capture();

                // Once we leave this method, the while loop will call next again. Because
                // capture is exclusive, we need to go one past the end index, capture, and then go back.
                context.Back();
                var parsedIndex = int.Parse(rawConditionParameter);
                results.Add(new ConditionMatchSegment(parsedIndex));
            }
            else
            {
                // illegal escape of a character
                throw new FormatException(Resources.FormatError_InputParserInvalidInteger(context.Template, context.Index));
            }
        }
Ejemplo n.º 2
0
        private static int GetBackReferenceIndex(ParserContext context)
        {
            if (!context.Next())
            {
                throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
            }

            context.Mark();
            while (context.Current != CloseBrace)
            {
                if (!context.Next())
                {
                    throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
                }
            }

            var res = context.Capture();
            int index;

            if (!int.TryParse(res, NumberStyles.None, CultureInfo.InvariantCulture, out index))
            {
                throw new FormatException(Resources.FormatError_InputParserInvalidInteger(res, context.Index));
            }

            if (index > 9 || index < 0)
            {
                throw new FormatException(Resources.FormatError_InputParserIndexOutOfRange(res, context.Index));
            }
            return(index);
        }
Ejemplo n.º 3
0
        private static int GetBackReferenceIndex(ParserContext context)
        {
            if (!context.Next())
            {
                throw new FormatException($"Missing backreference for parameter at string index: '{context.Index}'");
            }

            context.Mark();
            while (context.Current != CloseBrace)
            {
                if (!context.Next())
                {
                    throw new FormatException($"Missing close brace for parameter at string index: '{context.Index}'");
                }
            }

            var res = context.Capture();
            int index;

            if (!int.TryParse(res, NumberStyles.None, CultureInfo.InvariantCulture, out index))
            {
                throw new FormatException($"Cannot parse '{res}' to integer at string index: '{context.Index}'");
            }

            if (index > 9 || index < 0)
            {
                throw new FormatException($"Index out of range for backreference: '{res}' at string index: '{context.Index}'");
            }
            return(index);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Creates a pattern, which is a template to create a new test string to
        /// compare to the condition pattern. Can contain server variables, back references, etc.
        /// </summary>
        /// <param name="testString">The test string portion of the RewriteCond
        /// Examples:
        /// %{REMOTE_ADDR}
        /// /var/www/%{REQUEST_URI}
        /// %1
        /// $1</param>
        /// <returns>A new <see cref="Pattern"/>, containing a list of <see cref="PatternSegment"/></returns>
        /// http://httpd.apache.org/docs/current/mod/mod_rewrite.html
        public Pattern Parse(string testString)
        {
            if (testString == null)
            {
                testString = string.Empty;
            }
            var context = new ParserContext(testString);
            var results = new List <PatternSegment>();

            while (context.Next())
            {
                switch (context.Current)
                {
                case Percent:
                    // This is a server parameter, parse for a condition variable
                    if (!context.Next())
                    {
                        throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(testString, context.Index));
                    }
                    ParseConditionParameter(context, results);
                    break;

                case Dollar:
                    // This is a parameter from the rule, verify that it is a number from 0 to 9 directly after it
                    // and create a new Pattern Segment.
                    if (!context.Next())
                    {
                        throw new FormatException(Resources.FormatError_InputParserNoBackreference(context.Index));
                    }
                    context.Mark();
                    if (context.Current >= '0' && context.Current <= '9')
                    {
                        context.Next();
                        var ruleVariable = context.Capture();
                        context.Back();
                        var parsedIndex = int.Parse(ruleVariable);

                        results.Add(new RuleMatchSegment(parsedIndex));
                    }
                    else
                    {
                        throw new FormatException(Resources.FormatError_InputParserInvalidInteger(testString, context.Index));
                    }
                    break;

                default:
                    ParseLiteral(context, results);
                    break;
                }
            }
            return(new Pattern(results));
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Parse a string literal in the test string. Continues capturing until the start of a new variable type.
        /// </summary>
        /// <param name="context"></param>
        /// <param name="results"></param>
        /// <returns></returns>
        private static void ParseLiteral(ParserContext context, IList <PatternSegment> results)
        {
            context.Mark();
            string literal;

            while (true)
            {
                if (context.Current == Percent || context.Current == Dollar)
                {
                    literal = context.Capture();
                    context.Back();
                    break;
                }
                if (!context.Next())
                {
                    literal = context.Capture();
                    break;
                }
            }
            // add results
            results.Add(new LiteralSegment(literal));
        }
Ejemplo n.º 6
0
        private static void ParseLiteral(ParserContext context, IList <PatternSegment> results)
        {
            context.Mark();
            string literal;

            while (true)
            {
                if (context.Current == OpenBrace || context.Current == CloseBrace)
                {
                    literal = context.Capture();
                    context.Back();
                    break;
                }

                if (!context.Next())
                {
                    literal = context.Capture();
                    break;
                }
            }

            results.Add(new LiteralSegment(literal));
        }
Ejemplo n.º 7
0
        private void ParseParameter(ParserContext context, IList <PatternSegment> results, UriMatchPart uriMatchPart)
        {
            context.Mark();
            // Four main cases:
            // 1. {NAME} - Server Variable, create lambda to get the part of the context
            // 2. {R:1}  - IRule parameter
            // 3. {C:1}  - Condition Parameter
            // 4. {function:xxx} - String function
            // (unless we support Reload)
            string parameter;

            while (context.Next())
            {
                if (context.Current == CloseBrace)
                {
                    // This is just a server variable, so we do a lookup and verify the server variable exists.
                    parameter = context.Capture();
                    results.Add(ServerVariables.FindServerVariable(parameter, context, uriMatchPart));
                    return;
                }
                else if (context.Current == Colon)
                {
                    parameter = context.Capture();

                    // Only 5 strings to expect here. Case sensitive.
                    switch (parameter)
                    {
                    case "ToLower":
                    {
                        var pattern = ParseString(context, uriMatchPart);
                        results.Add(new ToLowerSegment(pattern));

                        // at this point, we expect our context to be on the ending closing brace,
                        // because the ParseString() call will increment the context until it
                        // has processed the new string.
                        if (context.Current != CloseBrace)
                        {
                            throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
                        }
                        return;
                    }

                    case "UrlDecode":
                    {
                        throw new NotImplementedException("UrlDecode is not implemented because of no great library available");
                    }

                    case "UrlEncode":
                    {
                        var pattern = ParseString(context, uriMatchPart);
                        results.Add(new UrlEncodeSegment(pattern));

                        if (context.Current != CloseBrace)
                        {
                            throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
                        }
                        return;
                    }

                    case "R":
                    {
                        var index = GetBackReferenceIndex(context);
                        results.Add(new RuleMatchSegment(index));
                        return;
                    }

                    case "C":
                    {
                        var index = GetBackReferenceIndex(context);
                        results.Add(new ConditionMatchSegment(index));
                        return;
                    }

                    default:
                        var rewriteMap = _rewriteMaps?[parameter];
                        if (rewriteMap != null)
                        {
                            var pattern = ParseString(context, uriMatchPart);
                            results.Add(new RewriteMapSegment(rewriteMap, pattern));
                            return;
                        }
                        throw new FormatException(Resources.FormatError_InputParserUnrecognizedParameter(parameter, context.Index));
                    }
                }
            }
            throw new FormatException(Resources.FormatError_InputParserMissingCloseBrace(context.Index));
        }
Ejemplo n.º 8
0
    /// <summary>
    /// Splits a string on whitespace, ignoring spaces, creating into a list of strings.
    /// </summary>
    /// <param name="rule">The rule to tokenize.</param>
    /// <returns>A list of tokens.</returns>
    public static IList <string>?Tokenize(string rule)
    {
        // TODO make list of strings a reference to the original rule? (run into problems with escaped spaces).
        // TODO handle "s and probably replace \ character with no slash.
        if (string.IsNullOrEmpty(rule))
        {
            return(null);
        }
        var context = new ParserContext(rule);

        context.Next();

        var tokens = new List <string>();

        context.Mark();
        while (true)
        {
            switch (context.Current)
            {
            case Escape:
                // Need to progress such that the next character is not evaluated.
                if (!context.Next())
                {
                    // Means that a character was not escaped appropriately Ex: "foo\"
                    throw new FormatException($"Invalid escaper character in string: {rule}");
                }
                break;

            case Quote:
                // Ignore all characters until the next quote is hit
                if (!context.Next())
                {
                    throw new FormatException($"Mismatched number of quotes: {rule}");
                }

                while (context.Current != Quote)
                {
                    if (!context.Next())
                    {
                        throw new FormatException($"Mismatched number of quotes: {rule}");
                    }
                }
                break;

            case Space:
            case Tab:
                // time to capture!
                var token = context.Capture();
                if (!string.IsNullOrEmpty(token))
                {
                    tokens.Add(token);
                    do
                    {
                        if (!context.Next())
                        {
                            // At end of string, we can return at this point.
                            RemoveQuotesAndEscapeCharacters(tokens);
                            return(tokens);
                        }
                    } while (context.Current == Space || context.Current == Tab);
                    context.Mark();
                    context.Back();
                }
                break;
            }
            if (!context.Next())
            {
                // End of string. Capture.
                break;
            }
        }
        var done = context.Capture();

        if (!string.IsNullOrEmpty(done))
        {
            tokens.Add(done);
        }

        RemoveQuotesAndEscapeCharacters(tokens);
        return(tokens);
    }