2009-06-05 17:29:28 +00:00
|
|
|
/*
|
|
|
|
* mod_rewrite - modify request path and querystring with support for regular expressions
|
|
|
|
*
|
|
|
|
* Todo:
|
|
|
|
* - implement rewrite_optimized which reorders rules according to hitcount
|
|
|
|
* - implement rewrite_raw which uses the raw uri
|
|
|
|
*
|
2010-09-25 23:04:50 +00:00
|
|
|
* Authors:
|
2009-06-05 17:29:28 +00:00
|
|
|
* Copyright (c) 2009 Thomas Porzelt
|
2010-09-25 23:04:50 +00:00
|
|
|
* Copyright (c) 2010 Stefan Bühler
|
2009-06-05 17:29:28 +00:00
|
|
|
* License:
|
|
|
|
* MIT, see COPYING file in the lighttpd 2 tree
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <lighttpd/base.h>
|
2009-07-08 19:06:07 +00:00
|
|
|
#include <lighttpd/encoding.h>
|
2010-10-07 13:58:46 +00:00
|
|
|
#include <lighttpd/pattern.h>
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
LI_API gboolean mod_rewrite_init(liModules *mods, liModule *mod);
|
|
|
|
LI_API gboolean mod_rewrite_free(liModules *mods, liModule *mod);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
typedef struct rewrite_rule rewrite_rule;
|
2009-06-05 17:29:28 +00:00
|
|
|
struct rewrite_rule {
|
2010-09-25 23:04:50 +00:00
|
|
|
liPattern *path, *querystring;
|
2009-06-05 17:29:28 +00:00
|
|
|
GRegex *regex;
|
|
|
|
};
|
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
typedef struct rewrite_data rewrite_data;
|
2009-06-05 17:29:28 +00:00
|
|
|
struct rewrite_data {
|
|
|
|
GArray *rules;
|
2009-07-08 19:06:07 +00:00
|
|
|
liPlugin *p;
|
2010-09-26 10:09:26 +00:00
|
|
|
gboolean raw;
|
2009-06-05 17:29:28 +00:00
|
|
|
};
|
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
static gboolean rewrite_rule_parse(liServer *srv, GString *regex, GString *str, rewrite_rule *rule) {
|
|
|
|
gchar *qs;
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
rule->path = rule->querystring = NULL;
|
|
|
|
rule->regex = NULL;
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
/* find "not-escaped" ? */
|
|
|
|
for (qs = str->str; *qs; qs++) {
|
|
|
|
if ('\\' == *qs) {
|
|
|
|
qs++;
|
|
|
|
if (!*qs) break;
|
|
|
|
} else if ('?' == *qs) break;
|
|
|
|
}
|
|
|
|
if (!*qs) qs = NULL;
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (NULL != qs) {
|
|
|
|
*qs = '\0'; /* restore later */
|
|
|
|
rule->querystring = li_pattern_new(srv, qs+1);
|
|
|
|
if (NULL == rule->querystring) {
|
|
|
|
goto error;
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
rule->path = li_pattern_new(srv, str->str);
|
|
|
|
if (NULL == rule->path) {
|
|
|
|
goto error;
|
|
|
|
}
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (NULL != regex) {
|
|
|
|
GError *err = NULL;
|
|
|
|
rule->regex = g_regex_new(regex->str, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, &err);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (NULL == rule->regex || NULL != err) {
|
|
|
|
ERROR(srv, "rewrite: error compiling regex \"%s\": %s", regex->str, NULL != err ? err->message : "unknown error");
|
|
|
|
g_error_free(err);
|
|
|
|
goto error;
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (NULL != qs) {
|
|
|
|
*qs = '?';
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (NULL != rule->querystring) {
|
|
|
|
li_pattern_free(rule->querystring);
|
|
|
|
rule->querystring = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != rule->path) {
|
|
|
|
li_pattern_free(rule->path);
|
|
|
|
rule->path = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != rule->regex) {
|
|
|
|
g_regex_unref(rule->regex);
|
|
|
|
rule->regex = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NULL != qs) {
|
|
|
|
*qs = '?';
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
2010-09-26 10:09:26 +00:00
|
|
|
static gboolean rewrite_internal(liVRequest *vr, GString *dest_path, GString *dest_query, rewrite_rule *rule, gboolean raw) {
|
2009-06-05 17:29:28 +00:00
|
|
|
gchar *path;
|
2009-07-23 10:27:56 +00:00
|
|
|
GMatchInfo *match_info = NULL;
|
2010-09-25 23:04:50 +00:00
|
|
|
GMatchInfo *prev_match_info = NULL;
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-26 10:09:26 +00:00
|
|
|
if (raw) {
|
|
|
|
path = vr->request.uri.raw_path->str;
|
|
|
|
} else {
|
|
|
|
path = vr->request.uri.path->str;
|
|
|
|
}
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (NULL != rule->regex && !g_regex_match(rule->regex, path, 0, &match_info)) {
|
|
|
|
if (NULL != match_info) {
|
2009-07-23 10:27:56 +00:00
|
|
|
g_match_info_free(match_info);
|
2010-09-25 23:04:50 +00:00
|
|
|
}
|
2009-07-23 10:27:56 +00:00
|
|
|
|
2009-06-05 17:29:28 +00:00
|
|
|
return FALSE;
|
2009-07-23 10:27:56 +00:00
|
|
|
}
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (vr->action_stack.regex_stack->len) {
|
|
|
|
GArray *rs = vr->action_stack.regex_stack;
|
|
|
|
prev_match_info = g_array_index(rs, liActionRegexStackElement, rs->len - 1).match_info;
|
2009-07-23 10:27:56 +00:00
|
|
|
}
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
g_string_truncate(dest_path, 0);
|
|
|
|
g_string_truncate(dest_query, 0);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
li_pattern_eval(vr, dest_path, rule->path, li_pattern_regex_cb, match_info, li_pattern_regex_cb, prev_match_info);
|
|
|
|
if (NULL != rule->querystring) {
|
|
|
|
li_pattern_eval(vr, dest_query, rule->querystring, li_pattern_regex_cb, match_info, li_pattern_regex_cb, prev_match_info);
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_match_info_free(match_info);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static liHandlerResult rewrite(liVRequest *vr, gpointer param, gpointer *context) {
|
2009-06-05 17:29:28 +00:00
|
|
|
guint i;
|
|
|
|
rewrite_rule *rule;
|
|
|
|
rewrite_data *rd = param;
|
|
|
|
gboolean debug = _OPTION(vr, rd->p, 0).boolean;
|
2013-09-06 12:36:55 +00:00
|
|
|
GString *dest_query = g_string_sized_new(31);
|
2009-06-05 17:29:28 +00:00
|
|
|
UNUSED(context);
|
|
|
|
|
|
|
|
for (i = 0; i < rd->rules->len; i++) {
|
2010-09-25 23:04:50 +00:00
|
|
|
GString *dest_path = vr->wrk->tmp_str;
|
|
|
|
|
2009-06-05 17:29:28 +00:00
|
|
|
rule = &g_array_index(rd->rules, rewrite_rule, i);
|
|
|
|
|
2010-09-26 10:09:26 +00:00
|
|
|
if (rewrite_internal(vr, dest_path, dest_query, rule, rd->raw)) {
|
2009-06-05 17:29:28 +00:00
|
|
|
/* regex matched */
|
2009-07-23 10:27:56 +00:00
|
|
|
if (debug) {
|
|
|
|
VR_DEBUG(vr, "rewrite: path \"%s\" => \"%s\", query \"%s\" => \"%s\"",
|
2010-09-25 23:04:50 +00:00
|
|
|
vr->request.uri.path->str, dest_path->str,
|
|
|
|
vr->request.uri.query->str, dest_query->str
|
2009-07-23 10:27:56 +00:00
|
|
|
);
|
|
|
|
}
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2009-09-28 11:19:02 +00:00
|
|
|
/* change request path */
|
2009-06-05 17:29:28 +00:00
|
|
|
g_string_truncate(vr->request.uri.path, 0);
|
2010-09-25 23:04:50 +00:00
|
|
|
g_string_append_len(vr->request.uri.path, GSTR_LEN(dest_path));
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2009-09-28 11:19:02 +00:00
|
|
|
/* change request query */
|
2010-09-25 23:04:50 +00:00
|
|
|
if (NULL != rule->querystring) {
|
2009-06-05 17:29:28 +00:00
|
|
|
g_string_truncate(vr->request.uri.query, 0);
|
2010-09-25 23:04:50 +00:00
|
|
|
g_string_append_len(vr->request.uri.query, GSTR_LEN(dest_query));
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* stop at first matching regex */
|
2013-09-06 12:36:55 +00:00
|
|
|
goto out;
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-06 12:36:55 +00:00
|
|
|
out:
|
|
|
|
g_string_free(dest_query, TRUE);
|
2009-07-08 19:06:07 +00:00
|
|
|
return LI_HANDLER_GO_ON;
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static void rewrite_free(liServer *srv, gpointer param) {
|
2009-06-05 17:29:28 +00:00
|
|
|
guint i;
|
|
|
|
rewrite_data *rd = param;
|
|
|
|
|
|
|
|
UNUSED(srv);
|
|
|
|
|
|
|
|
for (i = 0; i < rd->rules->len; i++) {
|
|
|
|
rewrite_rule *rule = &g_array_index(rd->rules, rewrite_rule, i);
|
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
li_pattern_free(rule->path);
|
|
|
|
li_pattern_free(rule->querystring);
|
2009-07-23 10:27:56 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (rule->regex) {
|
2009-07-23 10:27:56 +00:00
|
|
|
g_regex_unref(rule->regex);
|
2010-09-25 23:04:50 +00:00
|
|
|
}
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
g_array_free(rd->rules, TRUE);
|
|
|
|
g_slice_free(rewrite_data, rd);
|
|
|
|
}
|
|
|
|
|
2010-05-07 18:54:50 +00:00
|
|
|
static liAction* rewrite_create(liServer *srv, liWorker *wrk, liPlugin* p, liValue *val, gpointer userdata) {
|
2009-06-05 17:29:28 +00:00
|
|
|
rewrite_data *rd;
|
2010-05-07 18:54:50 +00:00
|
|
|
UNUSED(wrk);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2013-09-06 12:36:55 +00:00
|
|
|
val = li_value_get_single_argument(val);
|
|
|
|
|
|
|
|
if (LI_VALUE_STRING != li_value_type(val) && LI_VALUE_LIST != li_value_type(val)) {
|
2009-07-23 10:27:56 +00:00
|
|
|
ERROR(srv, "%s", "rewrite expects a either a string, a tuple of strings or a list of string tuples");
|
2009-06-05 17:29:28 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
rd = g_slice_new(rewrite_data);
|
|
|
|
rd->p = p;
|
|
|
|
rd->rules = g_array_new(FALSE, FALSE, sizeof(rewrite_rule));
|
2010-09-26 10:09:26 +00:00
|
|
|
rd->raw = GPOINTER_TO_INT(userdata);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2013-09-06 12:36:55 +00:00
|
|
|
if (LI_VALUE_STRING == li_value_type(val)) {
|
2009-07-23 10:27:56 +00:00
|
|
|
/* rewrite "/foo/bar"; */
|
2010-09-25 23:04:50 +00:00
|
|
|
rewrite_rule rule = { NULL, NULL, NULL };
|
2009-07-23 10:27:56 +00:00
|
|
|
|
2010-09-25 23:04:50 +00:00
|
|
|
if (!rewrite_rule_parse(srv, NULL, val->data.string, &rule)) {
|
2009-07-23 10:27:56 +00:00
|
|
|
rewrite_free(NULL, rd);
|
|
|
|
ERROR(srv, "rewrite: error parsing rule \"%s\"", val->data.string->str);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_append_val(rd->rules, rule);
|
2013-09-06 12:36:55 +00:00
|
|
|
} else if (li_value_list_has_len(val, 2) && LI_VALUE_STRING == li_value_list_type_at(val, 0) && LI_VALUE_STRING == li_value_list_type_at(val, 1)) {
|
2009-06-05 17:29:28 +00:00
|
|
|
/* only one rule */
|
2010-09-25 23:04:50 +00:00
|
|
|
rewrite_rule rule = { NULL, NULL, NULL };
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2013-09-06 12:36:55 +00:00
|
|
|
if (!rewrite_rule_parse(srv, li_value_list_at(val, 0)->data.string, li_value_list_at(val, 1)->data.string, &rule)) {
|
2009-06-05 17:29:28 +00:00
|
|
|
rewrite_free(NULL, rd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_append_val(rd->rules, rule);
|
|
|
|
} else {
|
|
|
|
/* probably multiple rules */
|
2013-09-06 12:36:55 +00:00
|
|
|
LI_VALUE_FOREACH(v, val)
|
2010-09-25 23:04:50 +00:00
|
|
|
rewrite_rule rule = { NULL, NULL, NULL };
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2013-09-06 12:36:55 +00:00
|
|
|
if (!li_value_list_has_len(v, 2)
|
|
|
|
|| LI_VALUE_STRING != li_value_list_type_at(v, 0) || LI_VALUE_STRING != li_value_list_type_at(v, 1)) {
|
2009-06-05 17:29:28 +00:00
|
|
|
rewrite_free(NULL, rd);
|
|
|
|
ERROR(srv, "%s", "rewrite expects a either a tuple of strings or a list of those");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-09-06 12:36:55 +00:00
|
|
|
if (!rewrite_rule_parse(srv, li_value_list_at(v, 0)->data.string, li_value_list_at(v, 1)->data.string, &rule)) {
|
2009-06-05 17:29:28 +00:00
|
|
|
rewrite_free(NULL, rd);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
g_array_append_val(rd->rules, rule);
|
2013-09-06 12:36:55 +00:00
|
|
|
LI_VALUE_END_FOREACH()
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
2009-07-09 20:17:24 +00:00
|
|
|
return li_action_new_function(rewrite, NULL, rewrite_free, rd);
|
2009-06-05 17:29:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static const liPluginOption options[] = {
|
2010-01-24 20:30:41 +00:00
|
|
|
{ "rewrite.debug", LI_VALUE_BOOLEAN, FALSE, NULL },
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2010-01-24 20:30:41 +00:00
|
|
|
{ NULL, 0, 0, NULL }
|
2009-06-05 17:29:28 +00:00
|
|
|
};
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
static const liPluginAction actions[] = {
|
2010-09-26 10:09:26 +00:00
|
|
|
{ "rewrite", rewrite_create, GINT_TO_POINTER(FALSE) },
|
|
|
|
{ "rewrite_raw", rewrite_create, GINT_TO_POINTER(TRUE) },
|
2009-06-05 17:29:28 +00:00
|
|
|
|
2009-12-21 11:29:14 +00:00
|
|
|
{ NULL, NULL, NULL }
|
2009-06-05 17:29:28 +00:00
|
|
|
};
|
|
|
|
|
2009-08-30 17:25:01 +00:00
|
|
|
static const liPluginSetup setups[] = {
|
2009-12-21 11:29:14 +00:00
|
|
|
{ NULL, NULL, NULL }
|
2009-06-05 17:29:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2009-12-21 11:29:14 +00:00
|
|
|
static void plugin_rewrite_init(liServer *srv, liPlugin *p, gpointer userdata) {
|
|
|
|
UNUSED(srv); UNUSED(userdata);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
|
|
|
p->options = options;
|
|
|
|
p->actions = actions;
|
|
|
|
p->setups = setups;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
gboolean mod_rewrite_init(liModules *mods, liModule *mod) {
|
2009-06-05 17:29:28 +00:00
|
|
|
UNUSED(mod);
|
|
|
|
|
|
|
|
MODULE_VERSION_CHECK(mods);
|
|
|
|
|
2009-12-21 11:29:14 +00:00
|
|
|
mod->config = li_plugin_register(mods->main, "mod_rewrite", plugin_rewrite_init, NULL);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
|
|
|
return mod->config != NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-08 19:06:07 +00:00
|
|
|
gboolean mod_rewrite_free(liModules *mods, liModule *mod) {
|
2009-06-05 17:29:28 +00:00
|
|
|
if (mod->config)
|
2009-07-09 20:17:24 +00:00
|
|
|
li_plugin_free(mods->main, mod->config);
|
2009-06-05 17:29:28 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|