Index: modules/mappers/mod_rewrite.c =================================================================== --- modules/mappers/mod_rewrite.c (revision 997855) +++ modules/mappers/mod_rewrite.c (working copy) @@ -242,9 +242,16 @@ CONDPAT_FILE_XBIT, CONDPAT_LU_URL, CONDPAT_LU_FILE, + CONDPAT_STR_LT, + CONDPAT_STR_LE, + CONDPAT_STR_EQ, CONDPAT_STR_GT, - CONDPAT_STR_LT, - CONDPAT_STR_EQ + CONDPAT_STR_GE, + CONDPAT_INT_LT, + CONDPAT_INT_LE, + CONDPAT_INT_EQ, + CONDPAT_INT_GT, + CONDPAT_INT_GE } pattern_type; typedef struct { @@ -253,6 +260,7 @@ ap_regex_t *regexp; /* the precompiled regexp */ int flags; /* Flags which control the match */ pattern_type ptype; /* pattern type */ + int pskip; /* back-index to display pattern */ } rewritecond_entry; /* single linked list for env vars and cookies */ @@ -3174,6 +3182,7 @@ } /* arg2: the pattern */ + newcond->pattern = a2; if (*a2 == '!') { newcond->flags |= CONDFLAG_NOTMATCH; ++a2; @@ -3186,28 +3195,59 @@ switch (a2[1]) { case 'f': newcond->ptype = CONDPAT_FILE_EXISTS; break; case 's': newcond->ptype = CONDPAT_FILE_SIZE; break; - case 'l': newcond->ptype = CONDPAT_FILE_LINK; break; case 'd': newcond->ptype = CONDPAT_FILE_DIR; break; case 'x': newcond->ptype = CONDPAT_FILE_XBIT; break; + case 'h': newcond->ptype = CONDPAT_FILE_LINK; break; + case 'L': newcond->ptype = CONDPAT_FILE_LINK; break; case 'U': newcond->ptype = CONDPAT_LU_URL; break; case 'F': newcond->ptype = CONDPAT_LU_FILE; break; + case 'l': if (a2[2] == 't') + a2 += 3, newcond->ptype = CONDPAT_INT_LT; + else if (a2[2] == 'e') + a2 += 3, newcond->ptype = CONDPAT_INT_LE; + else /* Historical; prefer -L or -h instead */ + newcond->ptype = CONDPAT_FILE_LINK; + break; + case 'g': if (a2[2] == 't') + a2 += 3, newcond->ptype = CONDPAT_INT_GT; + else if (a2[2] == 'e') + a2 += 3, newcond->ptype = CONDPAT_INT_GE; + break; + case 'e': if (a2[2] == 'q') + a2 += 3, newcond->ptype = CONDPAT_INT_EQ; + break; + case 'n': if (a2[2] == 'e') { + /* Inversion, ensure !-ne == -eq */ + a2 += 3, newcond->ptype = CONDPAT_INT_EQ; + newcond->flags ^= CONDFLAG_NOTMATCH; + } + break; } } else { switch (*a2) { - case '>': newcond->ptype = CONDPAT_STR_GT; break; - case '<': newcond->ptype = CONDPAT_STR_LT; break; + case '>': if (*++a2 == '=') + ++a2, newcond->ptype = CONDPAT_STR_GE; + else + newcond->ptype = CONDPAT_STR_GT; + break; + + case '<': if (*++a2 == '=') + ++a2, newcond->ptype = CONDPAT_STR_LE; + else + newcond->ptype = CONDPAT_STR_LT; + break; + case '=': newcond->ptype = CONDPAT_STR_EQ; - /* "" represents an empty string */ - if (*++a2 == '"' && a2[1] == '"' && !a2[2]) { - a2 += 2; - } - break; + /* "" represents an empty string */ + if (*++a2 == '"' && a2[1] == '"' && !a2[2]) + a2 += 2; + break; } } } - if (newcond->ptype && newcond->ptype != CONDPAT_STR_EQ && + if ((newcond->ptype < CONDPAT_STR_LT || newcond->ptype > CONDPAT_STR_GE) && (newcond->flags & CONDFLAG_NOCASE)) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "RewriteCond: NoCase option for non-regex pattern '%s' " @@ -3215,7 +3255,8 @@ newcond->flags &= ~CONDFLAG_NOCASE; } - newcond->pattern = a2; + newcond->pskip = a2 - newcond->pattern; + newcond->pattern += newcond->pskip; if (!newcond->ptype) { regexp = ap_pregcomp(cmd->pool, a2, @@ -3593,6 +3634,7 @@ request_rec *rsub, *r = ctx->r; ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; int rc = 0; + int basis; switch (p->ptype) { case CONDPAT_FILE_EXISTS: @@ -3661,15 +3703,36 @@ } break; + case CONDPAT_STR_GE: + basis = 0; + goto test_str_g; case CONDPAT_STR_GT: - rc = (compare_lexicography(input, p->pattern+1) == 1) ? 1 : 0; + basis = 1; +test_str_g: + if (p->flags & CONDFLAG_NOCASE) { + rc = (strcasecmp(input, p->pattern) >= basis) ? 1 : 0; + } + else { + rc = (compare_lexicography(input, p->pattern) >= basis) ? 1 : 0; + } break; + case CONDPAT_STR_LE: + basis = 0; + goto test_str_l; case CONDPAT_STR_LT: - rc = (compare_lexicography(input, p->pattern+1) == -1) ? 1 : 0; + basis = -1; +test_str_l: + if (p->flags & CONDFLAG_NOCASE) { + rc = (strcasecmp(input, p->pattern) <= basis) ? 1 : 0; + } + else { + rc = (compare_lexicography(input, p->pattern) <= basis) ? 1 : 0; + } break; case CONDPAT_STR_EQ: + /* Note: the only type where the operator is dropped from p->pattern */ if (p->flags & CONDFLAG_NOCASE) { rc = !strcasecmp(input, p->pattern); } @@ -3678,6 +3741,14 @@ } break; + case CONDPAT_INT_GE: rc = (atoi(input) >= atoi(p->pattern)); break; + case CONDPAT_INT_GT: rc = (atoi(input) > atoi(p->pattern)); break; + + case CONDPAT_INT_LE: rc = (atoi(input) <= atoi(p->pattern)); break; + case CONDPAT_INT_LT: rc = (atoi(input) < atoi(p->pattern)); break; + + case CONDPAT_INT_EQ: rc = (atoi(input) == atoi(p->pattern)); break; + default: /* it is really a regexp pattern, so apply it */ rc = !ap_regexec(p->regexp, input, AP_MAX_REG_MATCH, regmatch, 0); @@ -3695,9 +3766,8 @@ rc = !rc; } - rewritelog((r, 4, ctx->perdir, "RewriteCond: input='%s' pattern='%s%s%s'%s " - "=> %s", input, (p->flags & CONDFLAG_NOTMATCH) ? "!" : "", - (p->ptype == CONDPAT_STR_EQ) ? "=" : "", p->pattern, + rewritelog((r, 4, ctx->perdir, "RewriteCond: input='%s' pattern='%s'%s " + "=> %s", input, p->pattern - p->pskip, (p->flags & CONDFLAG_NOCASE) ? " [NC]" : "", rc ? "matched" : "not-matched"));