diff --git a/include/lighttpd/pattern.h b/include/lighttpd/pattern.h index 8d92b81..eaa2239 100644 --- a/include/lighttpd/pattern.h +++ b/include/lighttpd/pattern.h @@ -6,8 +6,10 @@ /* liPattern is a GArray in disguise */ typedef GArray liPattern; -/* a pattern callback receives an integer index and a data pointer (usually an array) and must return a GString* which gets inserted into the pattern result */ -typedef void (*liPatternCB) (GString *pattern_result, guint8 nth_ndx, gpointer data); +/* a pattern callback receives an integer index range [from-to] and a data pointer (usually an array) and must return a GString* which gets inserted into the pattern result + * "from" doesn't have to be smaller than "to" (allows reverse ranges)! + */ +typedef void (*liPatternCB) (GString *pattern_result, guint from, guint to, gpointer data); /* constructs a new liPattern* by parsing the given string, returns NULL on error */ LI_API liPattern *li_pattern_new(liServer *srv, const gchar* str); @@ -16,8 +18,8 @@ LI_API void li_pattern_free(liPattern *pattern); LI_API void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatternCB nth_callback, gpointer nth_data, liPatternCB nth_prev_callback, gpointer nth_prev_data); /* default array callback, expects a GArray* containing GString* elements */ -LI_API void li_pattern_array_cb(GString *pattern_result, guint8 nth_ndx, gpointer data); +LI_API void li_pattern_array_cb(GString *pattern_result, guint from, guint to, gpointer data); /* default regex callback, expects a GMatchInfo* */ -LI_API void li_pattern_regex_cb(GString *pattern_result, guint8 nth_ndx, gpointer data); +LI_API void li_pattern_regex_cb(GString *pattern_result, guint from, guint to, gpointer data); #endif diff --git a/src/main/condition.c b/src/main/condition.c index 8b38a2a..8a80336 100644 --- a/src/main/condition.c +++ b/src/main/condition.c @@ -620,14 +620,26 @@ static liHandlerResult li_condition_check_eval_string(liVRequest *vr, liConditio *res = !g_str_has_suffix(val, cond->rvalue.string->str); break; case LI_CONFIG_COND_MATCH: - *res = g_regex_match(cond->rvalue.regex, val, 0, &arse.match_info); - arse.string = (*res) ? g_string_new(val) : NULL; - g_array_append_val(vr->action_stack.regex_stack, arse); + arse.match_info = NULL; + arse.string = g_string_new(val); /* we have to copy the value, as match-info references it */ + *res = g_regex_match(cond->rvalue.regex, arse.string->str, 0, &arse.match_info); + if (*res) { + g_array_append_val(vr->action_stack.regex_stack, arse); + } else { + g_match_info_free(arse.match_info); + g_string_free(arse.string, TRUE); + } break; case LI_CONFIG_COND_NOMATCH: - *res = !g_regex_match(cond->rvalue.regex, val, 0, &arse.match_info); - arse.string = NULL; - g_array_append_val(vr->action_stack.regex_stack, arse); + arse.match_info = NULL; + arse.string = g_string_new(val); /* we have to copy the value, as match-info references it */ + *res = !g_regex_match(cond->rvalue.regex, arse.string->str, 0, &arse.match_info); + if (*res) { + g_match_info_free(arse.match_info); + g_string_free(arse.string, TRUE); + } else { + g_array_append_val(vr->action_stack.regex_stack, arse); + } break; case LI_CONFIG_COND_IP: case LI_CONFIG_COND_NOTIP: diff --git a/src/main/pattern.c b/src/main/pattern.c index 891015c..9c32512 100644 --- a/src/main/pattern.c +++ b/src/main/pattern.c @@ -3,37 +3,105 @@ typedef struct { enum { - PATTERN_STRING, /* literal */ - PATTERN_NTH, /* $n */ - PATTERN_NTH_PREV, /* %n */ - PATTERN_VAR, /* %{req.foo} */ - PATTERN_VAR_ENCODED /* %{enc:req.foo} */ + PATTERN_STRING, /* literal */ + PATTERN_NTH, /* $n */ + PATTERN_NTH_PREV, /* %n */ + PATTERN_VAR, /* %{req.foo} */ + PATTERN_VAR_ENCODED /* %{enc:req.foo} */ } type; union { - GString *str; /* PATTERN_STRING */ - guint8 ndx; /* PATTERN_NTH and PATTERN_NTH_PREV */ - liConditionLValue *lvalue; /* PATTERN_VAR and PATTERN_VAR_ENCODED */ + /* PATTERN_STRING */ + GString *str; + /* PATTERN_NTH and PATTERN_NTH_PREV */ + struct { + guint from, to; + } range; + /* PATTERN_VAR and PATTERN_VAR_ENCODED */ + liConditionLValue *lvalue; } data; } liPatternPart; +static gboolean parse_range(liServer *srv, liPatternPart *part, const gchar **str, const gchar *origstr) { + guint64 val; + gchar *endc = NULL; + const gchar *c = *str; + + c++; /* skip '[' */ + if (*c == '-') { + if (c[1] == ']') { + /* parse error */ + ERROR(srv, "could not parse pattern, empty range %%[-]: \"%s\"", origstr); + return FALSE; + } + part->data.range.from = G_MAXUINT; + } else if (*c == ']') { + /* parse error */ + ERROR(srv, "could not parse pattern, empty range %%[]: \"%s\"", origstr); + return FALSE; + } else { + errno = 0; + val = g_ascii_strtoull(c, &endc, 10); + if (0 != errno || val > G_MAXUINT) { + ERROR(srv, "could not parse pattern, range overflow: \"%s\"", origstr); + return FALSE; + } else if (endc == c || (*endc != '-' && *endc != ']')) { + ERROR(srv, "could not parse pattern, invalid range: \"%s\"", origstr); + return FALSE; + } + part->data.range.from = val; + } + + part->data.range.to = part->data.range.from; + + if (*c == '-') { + c++; + if (*c == ']') { + part->data.range.to = G_MAXUINT; + } else { + errno = 0; + val = g_ascii_strtoull(c, &endc, 10); + if (0 != errno || val > G_MAXUINT) { + ERROR(srv, "could not parse pattern, range overflow: \"%s\"", origstr); + return FALSE; + } else if (endc == c || (*endc != ']')) { + ERROR(srv, "could not parse pattern, invalid range: \"%s\"", origstr); + return FALSE; + } + part->data.range.to = val; + } + } + + c++; /* skip ']' */ + + *str = c; + return TRUE; +} + liPattern *li_pattern_new(liServer *srv, const gchar* str) { GArray *pattern; liPatternPart part; - gchar *c; + const gchar *c; gboolean encoded; pattern = g_array_new(FALSE, TRUE, sizeof(liPatternPart)); - for (c = (gchar*)str; *c;) { + for (c = str; *c;) { if (*c == '$') { /* $n, PATTERN_NTH */ c++; if (*c >= '0' && *c <= '9') { part.type = PATTERN_NTH; - part.data.ndx = *c - '0'; + part.data.range.from = part.data.range.to = *c - '0'; g_array_append_val(pattern, part); c++; + } else if ('[' == *c) { + part.type = PATTERN_NTH; + if (!parse_range(srv, &part, &c, str)) { + li_pattern_free((liPattern*)pattern); + return NULL; + } + g_array_append_val(pattern, part); } else { /* parse error */ ERROR(srv, "could not parse pattern: \"%s\"", str); @@ -45,12 +113,19 @@ liPattern *li_pattern_new(liServer *srv, const gchar* str) { if (*c >= '0' && *c <= '9') { /* %n, PATTERN_NTH_PREV */ part.type = PATTERN_NTH_PREV; - part.data.ndx = *c - '0'; + part.data.range.from = part.data.range.to = *c - '0'; g_array_append_val(pattern, part); c++; + } else if ('[' == *c) { + part.type = PATTERN_NTH_PREV; + if (!parse_range(srv, &part, &c, str)) { + li_pattern_free((liPattern*)pattern); + return NULL; + } + g_array_append_val(pattern, part); } else if (*c == '{') { /* %{var}, PATTERN_VAR */ - gchar *lval_c, *lval_start; + const gchar *lval_c, *lval_start; guint lval_len = 0; GString *key = NULL; @@ -67,7 +142,7 @@ liPattern *li_pattern_new(liServer *srv, const gchar* str) { for (lval_start = lval_c; *lval_c != '\0' && *lval_c != '}'; lval_c++) { /* got a key */ if (*lval_c == '[') { - gchar *key_c, *key_start; + const gchar *key_c, *key_start; guint key_len = 0; /* search for closing ']' */ @@ -120,7 +195,7 @@ liPattern *li_pattern_new(liServer *srv, const gchar* str) { } } else { /* string */ - gchar *first; + const gchar *first; part.type = PATTERN_STRING; part.data.str= g_string_sized_new(0); @@ -187,23 +262,28 @@ void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatter g_string_append_len(dest, GSTR_LEN(part->data.str)); break; case PATTERN_NTH: - if (nth_callback) - nth_callback(dest, part->data.ndx, nth_data); + if (NULL != nth_callback) { + nth_callback(dest, part->data.range.from, part->data.range.to, nth_data); + } break; case PATTERN_NTH_PREV: - if (nth_prev_callback) - nth_prev_callback(dest, part->data.ndx, nth_prev_data); + if (NULL != nth_prev_callback) { + nth_prev_callback(dest, part->data.range.from, part->data.range.to, nth_prev_data); + } break; case PATTERN_VAR_ENCODED: encoded = TRUE; /* fall through */ case PATTERN_VAR: + if (vr == NULL) continue; + res = li_condition_get_value(vr, part->data.lvalue, &cond_val, LI_COND_VALUE_HINT_STRING); if (res == LI_HANDLER_GO_ON) { - if (encoded) + if (encoded) { li_string_encode_append(li_condition_value_to_string(vr, &cond_val), dest, LI_ENCODING_URI); - else + } else { g_string_append(dest, li_condition_value_to_string(vr, &cond_val)); + } } break; @@ -211,22 +291,51 @@ void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatter } } -void li_pattern_array_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) { +void li_pattern_array_cb(GString *pattern_result, guint from, guint to, gpointer data) { GArray *a = data; + guint i; + + if (NULL == a || 0 == a->len) return; - if (nth_ndx < a->len) { - GString *str = g_array_index(a, GString*, nth_ndx); - g_string_append_len(pattern_result, GSTR_LEN(str)); + if (G_LIKELY(from <= to)) { + to = MIN(to, a->len - 1); + for (i = from; i <= to; i++) { + GString *str = g_array_index(a, GString*, i); + if (NULL != str) { + g_string_append_len(pattern_result, GSTR_LEN(str)); + } + } + } else { + from = MIN(from, a->len - 1); /* => from+1 is defined */ + for (i = from + 1; i-- >= to; ) { + GString *str = g_array_index(a, GString*, i); + if (NULL != str) { + g_string_append_len(pattern_result, GSTR_LEN(str)); + } + } } } -void li_pattern_regex_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) { - gint start_pos, end_pos; +void li_pattern_regex_cb(GString *pattern_result, guint from, guint to, gpointer data) { GMatchInfo *match_info = data; + guint i; + gint start_pos, end_pos; - if (!match_info) - return; + if (NULL == match_info) return; - if (g_match_info_fetch_pos(match_info, (gint)nth_ndx, &start_pos, &end_pos)) - g_string_append_len(pattern_result, g_match_info_get_string(match_info) + start_pos, end_pos - start_pos); + if (G_LIKELY(from <= to)) { + to = MIN(to, G_MAXINT); + for (i = from; i <= to; i++) { + if (g_match_info_fetch_pos(match_info, (gint) i, &start_pos, &end_pos)) { + g_string_append_len(pattern_result, g_match_info_get_string(match_info) + start_pos, end_pos - start_pos); + } + } + } else { + from = MIN(from, G_MAXINT); /* => from+1 is defined */ + for (i = from + 1; i-- >= to; ) { + if (g_match_info_fetch_pos(match_info, (gint) i, &start_pos, &end_pos)) { + g_string_append_len(pattern_result, g_match_info_get_string(match_info) + start_pos, end_pos - start_pos); + } + } + } } diff --git a/src/main/plugin_core.c b/src/main/plugin_core.c index edeb097..f987e36 100644 --- a/src/main/plugin_core.c +++ b/src/main/plugin_core.c @@ -151,29 +151,53 @@ static gboolean core_setup_set(liServer *srv, liPlugin* p, liValue *val, gpointe return li_plugin_set_default_option(srv, val_name->data.string->str, val_val); } -static void core_docroot_nth_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) { +typedef struct docroot_split docroot_split; +struct docroot_split { + GString *hostname; + gchar** splits; + guint split_len; +}; + +static void core_docroot_nth_cb(GString *pattern_result, guint to, guint from, gpointer data) { /* $n means n-th part of hostname from end divided by dots */ - gchar *c, *end; - guint i = 0; - GString *str = data; + /* range is interpreted reversed !!! */ + gboolean first = TRUE; + guint i; + docroot_split *ctx = data; - if (nth_ndx == 0) { - g_string_append_len(pattern_result, GSTR_LEN(str)); + if (0 == ctx->hostname->len) return; + + /* ranges including 0 will only get the complete hostname */ + if (0 == from || 0 == to) { + g_string_append_len(pattern_result, GSTR_LEN(ctx->hostname)); return; } - end = str->str + str->len - 1; + if (NULL == ctx->splits) { + ctx->splits = g_strsplit_set(ctx->hostname->str, ".", 31); + ctx->split_len = g_strv_length(ctx->splits); + } - for (c = end; c > str->str; c--) { - if (*c == '.') { - i++; + if (0 == ctx->split_len) return; - if (i == nth_ndx) { - g_string_append_len(pattern_result, c+1, end - c); - return; - } + from = MAX(from, ctx->split_len); + to = MAX(to, ctx->split_len); - end = c-1; + if (from <= to) { + for (i = from; i <= to; i++) { + if (first) { + first = FALSE; + g_string_append_len(pattern_result, CONST_STR_LEN(".")); + } + g_string_append(pattern_result, ctx->splits[ctx->split_len - i]); + } + } else { + for (i = from+1; i-- >= to; ) { + if (first) { + first = FALSE; + g_string_append_len(pattern_result, CONST_STR_LEN(".")); + } + g_string_append(pattern_result, ctx->splits[ctx->split_len - i]); } } } @@ -182,6 +206,7 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin guint i; GMatchInfo *match_info = NULL; GArray *arr = param; + docroot_split dsplit = { vr->request.uri.host, NULL, 0 }; g_string_truncate(vr->physical.doc_root, 0); @@ -205,7 +230,7 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin g_string_truncate(vr->physical.doc_root, 0); - li_pattern_eval(vr, vr->physical.doc_root, g_array_index(arr, liPattern*, i), core_docroot_nth_cb, vr->request.uri.host, li_pattern_regex_cb, match_info); + li_pattern_eval(vr, vr->physical.doc_root, g_array_index(arr, liPattern*, i), core_docroot_nth_cb, &dsplit, li_pattern_regex_cb, match_info); if (i == arr->len - 1) break; /* don't stat, we'll use the last entry anyway */ @@ -217,6 +242,7 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin if (CORE_OPTION(LI_CORE_OPTION_DEBUG_REQUEST_HANDLING).boolean) { VR_DEBUG(vr, "docroot: waiting for async: \"%s\"", vr->physical.doc_root->str); } + g_strfreev(dsplit.splits); return LI_HANDLER_WAIT_FOR_EVENT; default: /* not found, try next pattern */ @@ -227,6 +253,8 @@ static liHandlerResult core_handle_docroot(liVRequest *vr, gpointer param, gpoin } } + g_strfreev(dsplit.splits); + /* build physical path: docroot + uri.path */ g_string_truncate(vr->physical.path, 0); g_string_append_len(vr->physical.path, GSTR_LEN(vr->physical.doc_root));