/// <summary>
        /// Initializes a new instance of the <see cref="ConditionContext"/> class.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="ruleContext">The rule context.</param>
        /// <param name="condition">The condition.</param>
        public ConditionContext(int index, RuleContext ruleContext, ICondition condition)
            : base(ruleContext)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("condition");
            }

            ConditionIndex = index;
            LogCategory = "Condition " + index;
            CurrentCondition = condition;
        }
        public string GetValue(string input, RuleContext context)
        {
            var startIndex = 1;

            for (var i = 0; i < context.Conditions.Count; i++)
            {
                var cond = context.Conditions[i];
                var groupCount = cond.Pattern.GetGroupCount(input);
                var condContext = new ConditionContext(i, context, cond);

                if ((startIndex + groupCount) >= Index)
                {
                    var varIndex = Math.Abs(startIndex - _index);
                    return cond.Pattern.GetValue(cond.Test.GetValue(condContext), varIndex, condContext);
                }

                startIndex += groupCount;
            }

            return null;
        }
        /// <summary>
        /// Runs the rules.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        /// <param name="url">The URL.</param>
        /// <returns>
        /// Returns a rewritten <see cref="System.Uri"/>, or a value of <see langword="null"/> if no rewriting was done to <paramref name="url"/>.
        /// </returns>
        public Uri RunRules(HttpContextBase httpContext, Uri url)
        {
            var context = new RuleSetContext(this, url, httpContext);
            var currentUrl = url;

            if (!EngineEnabled)
            {
                Logger.Info("Rewrite Engine Is DISABLED");
            }

            if (_rules.Count > 0 && EngineEnabled)
            {
                Logger.InfoFormat("Input: " + currentUrl);

                // check if max number of internal transfers have been exceeded
                if (InternalTransferCount(httpContext) > MaxInternalTransfers)
                {
                    string message = "Exceeded the max number of internal transfers.";
                    throw new HttpException(500, message);
                }

                IRuleFlagProcessor temporyFlags = null;
                var skipNextChain = false;
                var initialUrl = currentUrl;

                if (!string.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
                {
                    currentUrl = RemoveBase(VirtualBase, currentUrl);
                }

                // process rules according to their settings
                for (var i = 0; i < _rules.Count; i++)
                {
                    var ruleContext = new RuleContext(i, context, currentUrl, _rules[i]);
                    temporyFlags = _rules[i].Flags;

                    // continue if this rule shouldn't be processed because it doesn't allow internal transfer requests
                    if (RuleFlagsProcessor.HasNotForInternalSubRequests(temporyFlags) && IsInternalTransfer(httpContext))
                    {
                        continue;
                    }

                    var containsChain = RuleFlagsProcessor.HasChain(_rules[i].Flags);
                    var previousContainsChain = RuleFlagsProcessor.HasChain(_rules[Math.Max(0, i - 1)].Flags);

                    // if the previous rule doesn't contain a chain flag then set the initial URL
                    // this will be used to reset a chain if one of the chain rules fail
                    if (!previousContainsChain)
                    {
                        initialUrl = currentUrl;
                    }

                    // skip if the current rule or the last rule has a chain flag
                    // and if the skip next chain is set
                    if (skipNextChain && (previousContainsChain || containsChain))
                    {
                        continue;
                    }
                    else
                    {
                        skipNextChain = false;
                    }

                    if (_rules[i].TryExecute(ruleContext))
                    {
                        var flagResponse = temporyFlags.Apply(ruleContext);
                        currentUrl = ruleContext.SubstitutedUrl;
                        i = ruleContext.RuleIndex ?? -1;
                        var breakLoop = false;

                        // apply the flags to the rules, and only do special processing
                        // for the flag responses listed in the switch statement below
                        switch (flagResponse)
                        {
                            case RuleFlagProcessorResponse.ExitRuleSet:
                                return null;
                            case RuleFlagProcessorResponse.LastRule:
                                breakLoop = true;
                                break;
                        }

                        // break the loop because we have reached the last rule as indicated by a flag
                        if (breakLoop)
                        {
                            break;
                        }
                    }
                    else if (containsChain)
                    {
                        skipNextChain = true;

                        // reset the current URL back to the initial URL from the start of the chain
                        currentUrl = initialUrl;
                    }
                    else if (previousContainsChain)
                    {
                        // reset the current URL back to the initial URL from the start of the chain
                        currentUrl = initialUrl;
                    }
                }

                if (!string.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
                {
                    currentUrl = AddBase(VirtualBase, currentUrl);
                }

                Logger.InfoFormat("Output: {0}", currentUrl);
            }

            // if the http request url matches for both the request and the rewrite no work was done so the url should be null
            if (Uri.Compare(currentUrl, url, UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0)
            {
                currentUrl = null;
            }

            return currentUrl;
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="RuleContext"/> class.
 /// </summary>
 /// <param name="copy">The copy.</param>
 internal RuleContext(RuleContext copy)
     : base(copy)
 {
     RuleIndex = copy.RuleIndex;
     LogCategory = copy.LogCategory;
     _currentContent = copy._currentContent;
     _substitutedContent = copy.SubstitutedContent;
     CurrentUrl = copy.CurrentUrl;
     _substitutedUrl = copy.SubstitutedUrl;
     CurrentRule = copy.CurrentRule;
 }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpContext"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public byte[] RunOutputRules(HttpContextBase httpContext, byte[] content)
        {
            var context = new RuleSetContext(this, content, httpContext);
            var currentContent = content;

            if (!EngineEnabled)
            {
                Logger.Info("Rewrite Engine Is DISABLED");
            }

            if (_outputRules.Count > 0 && EngineEnabled)
            {
                IRuleFlagProcessor temporyFlags = null;
                var skipNextChain = false;
                var initialContent = currentContent;

                // process rules according to their settings
                for (var i = 0; i < _outputRules.Count; i++)
                {
                    var ruleContext = new RuleContext(i, context, currentContent, _outputRules[i]);
                    temporyFlags = _outputRules[i].Flags;

                    var containsChain = RuleFlagsProcessor.HasChain(_outputRules[i].Flags);
                    var previousContainsChain = RuleFlagsProcessor.HasChain(_outputRules[Math.Max(0, i - 1)].Flags);

                    // if the previous rule doesn't contain a chain flag then set the initial URL
                    // this will be used to reset a chain if one of the chain rules fail
                    if (!previousContainsChain)
                    {
                        initialContent = currentContent;
                    }

                    // skip if the current rule or the last rule has a chain flag
                    // and if the skip next chain is set
                    if (skipNextChain && (previousContainsChain || containsChain))
                    {
                        continue;
                    }
                    else
                    {
                        skipNextChain = false;
                    }

                    if (_outputRules[i].TryExecute(ruleContext))
                    {
                        var flagResponse = temporyFlags.Apply(ruleContext);
                        currentContent = ruleContext.SubstitutedContent;
                        i = ruleContext.RuleIndex ?? -1;
                        var breakLoop = false;

                        // apply the flags to the rules, and only do special processing
                        // for the flag responses listed in the switch statement below
                        switch (flagResponse)
                        {
                            case RuleFlagProcessorResponse.ExitRuleSet:
                                return null;
                            case RuleFlagProcessorResponse.LastRule:
                                breakLoop = true;
                                break;
                        }

                        // break the loop because we have reached the last rule as indicated by a flag
                        if (breakLoop)
                        {
                            break;
                        }
                    }
                    else if (containsChain)
                    {
                        skipNextChain = true;

                        // reset the current URL back to the initial URL from the start of the chain
                        currentContent = initialContent;
                    }
                    else if (previousContainsChain)
                    {
                        // reset the current URL back to the initial URL from the start of the chain
                        currentContent = initialContent;
                    }
                }

            }

            return currentContent;
        }
 /// <summary>
 /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
 /// </summary>
 /// <returns>
 /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
 /// </returns>
 public string ToString(RuleContext context)
 {
     return Replace(ToString(false), context);
 }
        /// <summary>
        /// Replaces the specified input for a rule variable.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="replacement">The replacement.</param>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public string Replace(string input, string replacement, RuleContext context)
        {
            if (context != null)
            {
                replacement = Variables.Replace(replacement, match => {
                    if (match.Groups["condvar"].Success)
                    {
                        var condVar = GetConditionVariable(match);
                        return condVar.GetValue(input, context);
                    }
                    else if (match.Groups["servervar"].Success)
                    {
                        var serverVar = GetServerVariable(match);
                        return serverVar.GetValue(input, context);
                    }
                    return match.Value;
                });
            }

            return _pattern.Replace(input, replacement);
        }
        /// <summary>
        /// Determines whether the specified input is match.
        /// </summary>
        /// <param name="input">The input.</param>
        /// <param name="context">The context.</param>
        /// <returns>
        /// 	<see langword="true"/> if the specified input is match; otherwise, <see langword="false"/>.
        /// </returns>
        public bool IsMatch(string input, RuleContext context)
        {
            // if the input contains a rule or a condition placeholders that need to be
            // replaced from the input URL it needs to be processed by the rule pattern
            // before we can check if it is a match
            if (input.IndexOfAny(new[] { '%', '$' }) >= 0)
            {
                input = Replace(input, context);
            }

            return IsMatch(input);
        }
 /// <summary>
 /// Gets the value.
 /// </summary>
 /// <param name="input">The input.</param>
 /// <param name="index">The index.</param>
 /// <param name="context">The context.</param>
 /// <returns>Returns the value of the pattern for the <paramref name="index"/></returns>
 public string GetValue(string input, int index, RuleContext context)
 {
     return GetValue(input, index);
 }