int ApplyRules( HTTP_FILTER_CONTEXT * pfc, char * subject, int depth, /* out */ char **result, /* out */ boolean *pRecordOriginalUrl ) { RewriteRule * current= config->rootRule; int retVal= 0; // 0 = do nothing, 1 = rewrite, 403 = forbidden, other = redirect char MsgBuffer[512]; int c=0; int RuleMatchCount, i; int *RuleMatchVector; #if _WRITE_LOG sprintf_s(MsgBuffer,512,"ApplyRules (depth=%d)", depth); LogMsg(3, MsgBuffer); #endif if (current==NULL) { #if _WRITE_LOG LogMsg(2, "ApplyRules: No configuration available."); #endif return 0; }
// The PCRE doc says vector length should be 3n?? why? seems like it ought to be 2n. or maybe 2n+1. // In any case we allocate 3n. RuleMatchVector= (int *) malloc((config->MaxMatchCount*3)*sizeof(int));
// The way it works: First we evaluate the URL request, against the RewriteRule pattern. // If there is a match, then the logic evaluates the Conditions attached to the rule. // This may be counter-intuitive, since the Conditions appear BEFORE the rule in the file, // but the Rule is evaluated FIRST.
// TODO: employ a MRU cache to map URLs while (current!=NULL) { c++;
RuleMatchCount = pcre_exec( current->RE, /* the compiled pattern */ NULL, /* no extra data - we didnt study the pattern */ subject, /* the subject string */ strlen(subject), /* the length of the subject */ 0, /* start at offset 0 in the subject */ 0, /* default options */ RuleMatchVector, /* output vector for substring position information */ config->MaxMatchCount*3); /* number of elements in the output vector */
// return code: >=0 means number of matches, <0 means error
if (RuleMatchCount < 0) { if (RuleMatchCount== PCRE_ERROR_NOMATCH) { #if _WRITE_LOG sprintf_s(MsgBuffer,512,"Rule %d : %d (No match)", c, RuleMatchCount ); LogMsg(3, MsgBuffer); #endif } else { #if _WRITE_LOG sprintf_s(MsgBuffer,512,"Rule %d : %d (unknown error)", c, RuleMatchCount); LogMsg(2, MsgBuffer); #endif } } else if (RuleMatchCount == 0) { #if _WRITE_LOG sprintf_s(MsgBuffer,512,"Rule %d : %d (The output vector (%d slots) was not large enough)", c, RuleMatchCount, config->MaxMatchCount*3); LogMsg(2, MsgBuffer); #endif } else { // we have a match and we have substrings boolean ConditionResult= FALSE;
PcreMatchResult RuleMatchResult; PcreMatchResult CondMatchResult; #if _WRITE_LOG sprintf_s(MsgBuffer,512,"Rule %d : %d matches", c, RuleMatchCount); LogMsg(2, MsgBuffer); #endif // easier to pass these as a structure RuleMatchResult.Subject= subject; RuleMatchResult.SubstringIndexes= RuleMatchVector; RuleMatchResult.MatchCount= RuleMatchCount;
// The fields in CondMatchResult may be filled by the EvaluateConditionList(), but // we must init them because the EvaluateConditionList may never be called. The // results reflect only the "last" Condition evaluated. This may or may not be // the final Condition in the file; the evaluation engine wont evaluate // Conditions unnecessarily. Check the readme for more details. CondMatchResult.Subject= NULL; CondMatchResult.SubstringIndexes= NULL; CondMatchResult.MatchCount= 0;
// evaluate the condition list, if there is one. ConditionResult= (current->Condition==NULL) || EvaluateConditionList(pfc, &RuleMatchResult, &CondMatchResult, current->Condition);
// Check that any associated Condition evaluates to true, before // applying this rule. if ( ConditionResult ) {
char *ts1; char *newString;
// generate the replacement string // step 1: substitute server variables, if any. ts1= ReplaceServerVariables(pfc, current->Replacement);
if (sizeof(MsgBuffer)-28> strlen(newString)) { #if _WRITE_LOG sprintf_s(MsgBuffer,512,"Result (length %d): %s", strlen(newString), newString); LogMsg(2,MsgBuffer); #endif } else { #if _WRITE_LOG LogMsg(2,"(Log Buffer too small to show new string)"); sprintf_s(MsgBuffer,512,"Result length: %d", strlen(newString)); LogMsg(3,MsgBuffer); #endif }
// set output params *result= newString;
// if the current rule asks to record the original URL, then set the OUT flag. *pRecordOriginalUrl |= current->RecordOriginalUrl;
// check modifiers if (current->IsRedirect) { retVal = current->RedirectCode; // = redirect } else if (current->IsForbidden) { // no recurse #if _WRITE_LOG LogMsg(2,"returning: Forbidden"); #endif retVal = 403; // = forbidden } else if (current->IsNotFound) { // no recurse #if _WRITE_LOG LogMsg(2,"returning: Not Found"); #endif retVal = 404; // = not found } else { // rewrite retVal= 1; if (current->IsLastIfMatch) { // no recurse #if _WRITE_LOG LogMsg(2,"Last if Match"); #endif break; //current= NULL; // as a way to stop the loop } else { // by default, we recurse on the RewriteRules. if (depth < config->IterationLimit) { char * t; int rv= ApplyRules(pfc, newString, depth+1, &t, pRecordOriginalUrl); if (rv) { *result= t; // a newly allocated string retVal= rv; // for return to caller free(newString); // free our string, we no longer need it } // else, no match on recursion, so dont free newString (keep the existing result). } else { #if _WRITE_LOG sprintf_s(MsgBuffer,512,"Iteration stopped; reached limit of %d cycles.", config->IterationLimit); LogMsg(2,MsgBuffer); #endif } } }
break; // break out of while loop on the first match }
}
// We did not break out of the loop. // Therefore, this rule did not apply. // Therefore, go to the next rule. if (current!=NULL) current= current->next; }