/// <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)); }
/// <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)); } }
/// <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)); }
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)); }
/// <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); }