<?xml version="1.0" encoding="UTF-8"?> <configuration> <configSections> <section name="rewriter" requirePermission="false" type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" /> </configSections> <system.web> <httpModules> <add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule, Intelligencia.UrlRewriter" /> </httpModules> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="UrlRewriter" type="Intelligencia.UrlRewriter.RewriterHttpModule" /> </modules> <validation validateIntegratedModeConfiguration="false" /> </system.webServer> <rewriter> <rewrite url="~/products/(.+)" to="~/products.aspx?category=$1" /> </rewriter> </configuration> |
typedef struct RewriteRule { pcre * RE; char * Pattern; char * Replacement; boolean IsRedirect; int RedirectCode; boolean IsForbidden; boolean IsNotFound; boolean IsLastIfMatch; boolean IsCaseInsensitive; boolean RecordOriginalUrl; boolean IsMatchDomain; // any condition that applies struct RewriteCondition * Condition; // doubly-linked list struct RewriteRule * next; struct RewriteRule * previous; } RewriteRule, *P_RewriteRule; |
void ParseRuleModifierFlags(char * pModifiers, RewriteRule *rule) { char MsgBuffer[512]; rule->IsRedirect= FALSE; rule->IsForbidden= FALSE; rule->IsLastIfMatch= FALSE; rule->IsNotFound= FALSE; rule->IsCaseInsensitive= FALSE; rule->RecordOriginalUrl= FALSE; rule->IsMatchDomain = FALSE; if (pModifiers==NULL) return; // no flags at all sprintf_s(MsgBuffer,512,"ParseRuleModifierFlags: %s", pModifiers); LogMsg(2, MsgBuffer); if ((pModifiers[0] != [) || (pModifiers[strlen(pModifiers)-1] != ])) { LogMsg(1, "WARNING: Badly formed RewriteRule modifier flags."); return; } else { char * p1, *p2; char * StrtokContext= NULL; p1= pModifiers+1; // skip leading [ pModifiers[strlen(pModifiers)-1]=0; // remove trailing ] p2= strtok_s(p1, ",", &StrtokContext); // split by commas while (p2 != NULL) { if (config->LogLevel >= 5 ) { sprintf_s(MsgBuffer,512,"ParseRuleModifierFlags: token %s", p2); LogMsg(5, MsgBuffer); } if (p2[0]==R) { // redirect rule->IsRedirect= TRUE; rule->RedirectCode= REDIRECT_CODE_DEFAULT; // use the default redirect code if ((p2[1]!=0) && (p2[1]===) && (p2[2]!=0)) { int n= atoi(p2+2); if ((n <= REDIRECT_CODE_MAX) && (n >= REDIRECT_CODE_MIN)) rule->RedirectCode= n; } } else if ((p2[0]==F) && (p2[1]==0)) { // forbidden (403) LogMsg(5, "rule: Forbidden"); rule->IsForbidden= TRUE; } else if ((p2[0]==N) && (p2[1]==F) && (p2[2]==0)) { // not found (404) LogMsg(5, "rule: Not found"); rule->IsNotFound= TRUE; } else if ((p2[0]==L) && (p2[1]==0)) { // Last rule to process if match LogMsg(5, "rule: Last"); rule->IsLastIfMatch= TRUE; } else if ((p2[0]==I) && (p2[1]==0)) { // case-insensitive LogMsg(5, "rule: Case Insensitive match"); rule->IsCaseInsensitive= TRUE; } else if ((p2[0]==U) && (p2[1]==0)) { // Unmangle URLs LogMsg(5, "rule: Unmangle URLs"); rule->RecordOriginalUrl= TRUE; } else if ((p2[0]==D) && (p2[1]==0)) { // Match Domain Rule URLs LogMsg(5, "rule: Match Domain URLs"); rule->IsMatchDomain = TRUE; } else { sprintf_s(MsgBuffer,512,"WARNING: unsupported RewriteRule modifier flag %s", p2); LogMsg(1, MsgBuffer); } p2= strtok_s(NULL, ",", &StrtokContext); // next token } // consistency checks if (rule->IsForbidden && rule->IsRedirect) LogMsg(1, "WARNING: Conflicting modifier flags - F,R"); if (rule->IsForbidden && rule->IsLastIfMatch) LogMsg(1, "WARNING: Redundant modifier flags - F,L"); if (rule->IsForbidden && rule->IsNotFound) LogMsg(1, "WARNING: Conflicting modifier flags - F,NF"); if (rule->IsNotFound && rule->IsLastIfMatch) LogMsg(1, "WARNING: Redundant modifier flags - NF,L"); if (rule->IsNotFound && rule->IsRedirect) LogMsg(1, "WARNING: Conflicting modifier flags - NF,R"); if (rule->IsRedirect && rule->IsLastIfMatch) LogMsg(1, "WARNING: Redundant modifier flags - R,L"); } return; } |
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++; LogMsg(3, "subject"); LogMsg(3, subject); if(current->IsMatchDomain && depth==0) { char *serverHost; char *originalUrlWithDomain; serverHost= GetServerVariable("HTTP_HOST", pfc); originalUrlWithDomain = (char *) pfc->AllocMem(pfc, strlen(serverHost)+strlen(subject)+1, 0); strcpy(originalUrlWithDomain,serverHost); strcat(originalUrlWithDomain,subject); subject = originalUrlWithDomain; } 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); // step 1: substitute back-references as appropriate. newString= GenerateReplacementString(ts1, // current->Replacement, &RuleMatchResult, &CondMatchResult); free(ts1); FreeMatchResult(&CondMatchResult); 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; } free(RuleMatchVector); #if _WRITE_LOG sprintf_s(MsgBuffer,512,"ApplyRules: returning %d", retVal); LogMsg(3,MsgBuffer); #endif return retVal; } |