From 3668ef97ab432299654429965ef7f2ba73221965 Mon Sep 17 00:00:00 2001 From: Thomas Porzelt Date: Sun, 6 Jun 2010 23:54:55 +0200 Subject: [PATCH] [core] Add liPattern type and associated functions --- include/lighttpd/base.h | 2 + include/lighttpd/pattern.h | 39 ++++++++ src/CMakeLists.txt | 1 + src/main/Makefile.am | 1 + src/main/pattern.c | 192 +++++++++++++++++++++++++++++++++++++ src/main/wscript | 1 + 6 files changed, 236 insertions(+) create mode 100644 include/lighttpd/pattern.h create mode 100644 src/main/pattern.c diff --git a/include/lighttpd/base.h b/include/lighttpd/base.h index 72e12db..4a789fc 100644 --- a/include/lighttpd/base.h +++ b/include/lighttpd/base.h @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include diff --git a/include/lighttpd/pattern.h b/include/lighttpd/pattern.h new file mode 100644 index 0000000..bbde64b --- /dev/null +++ b/include/lighttpd/pattern.h @@ -0,0 +1,39 @@ +#ifndef _LIGHTTPD_PATTERN_H_ +#define _LIGHTTPD_PATTERN_H_ + +/* liPattern are a parsed representation of a string that can contain various placeholders like $n, %n, %{var} or {enc:var} */ + +typedef struct { + enum { + 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 */ + liCondLValue var; /* PATTERN_VAR and PATTERN_VAR_ENCODED */ + } data; +} liPatternPart; + +/* 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); + +/* constructs a new liPattern* by parsing the given string, returns NULL on error */ +LI_API liPattern *li_pattern_new(const gchar* str); +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); +/* default regex callback, expects a GMatchInfo* */ +LI_API void li_pattern_regex_cb(GString *pattern_result, guint8 nth_ndx, gpointer data); + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6dc0e96..cd8ab1d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -206,6 +206,7 @@ SET(LIGHTTPD_SHARED_SRC network_write.c network_writev.c network_sendfile.c options.c + pattern.c plugin.c request.c response.c diff --git a/src/main/Makefile.am b/src/main/Makefile.am index 649b976..4a533c1 100644 --- a/src/main/Makefile.am +++ b/src/main/Makefile.am @@ -28,6 +28,7 @@ lighttpd_shared_src= \ network_write.c network_writev.c \ network_sendfile.c \ options.c \ + pattern.c \ plugin.c \ request.c \ response.c \ diff --git a/src/main/pattern.c b/src/main/pattern.c new file mode 100644 index 0000000..a0e8f93 --- /dev/null +++ b/src/main/pattern.c @@ -0,0 +1,192 @@ +#include + +liPattern *li_pattern_new(const gchar* str) { + GArray *pattern; + liPatternPart part; + gchar *c; + gboolean encoded; + + pattern = g_array_new(FALSE, TRUE, sizeof(liPatternPart)); + + for (c = (gchar*)str; *c;) { + if (*c == '$') { + /* $n, PATTERN_NTH */ + c++; + if (*c >= '0' && *c <= '9') { + part.type = PATTERN_NTH; + part.data.ndx = *c - '0'; + g_array_append_val(pattern, part); + c++; + } else { + /* parse error */ + li_pattern_free((liPattern*)pattern); + return NULL; + } + } else if (*c == '%') { + c++; + if (*c >= '0' && *c <= '9') { + /* %n, PATTERN_NTH_PREV */ + part.type = PATTERN_NTH_PREV; + part.data.ndx = *c - '0'; + g_array_append_val(pattern, part); + c++; + } else if (*c == '{') { + /* %{var}, PATTERN_VAR */ + guint len; + + c++; + encoded = FALSE; + + if (g_str_has_prefix(c, "enc:")) { + /* %{enc:var}, PATTERN_VAR_ENCODED */ + c += sizeof("enc:")-1; + encoded = TRUE; + } + + for (len = 0; *c != '\0' && *c != '}'; c++) + len++; + + if (*c == '\0') { + /* parse error */ + li_pattern_free((liPattern*)pattern); + return NULL; + } + + part.data.var = li_cond_lvalue_from_string(c-len, len); + + if (part.data.var == LI_COMP_UNKNOWN) { + /* parse error */ + li_pattern_free((liPattern*)pattern); + return NULL; + } + + if (len && *c == '}') { + part.type = encoded ? PATTERN_VAR_ENCODED : PATTERN_VAR; + g_array_append_val(pattern, part); + c++; + } else { + /* parse error */ + li_pattern_free((liPattern*)pattern); + return NULL; + } + } else { + /* parse error */ + li_pattern_free((liPattern*)pattern); + return NULL; + } + } else { + /* string */ + gchar *first = c; + c++; + + for (;;) { + if (*c == '\0' || *c == '?' || *c == '$' || *c == '%') { + break; + } else if (*c == '\\') { + c++; + if (*c == '\\' || *c == '?' || *c == '$' || *c == '%') { + c++; + } else { + /* parse error */ + li_pattern_free((liPattern*)pattern); + return NULL; + } + } else { + c++; + } + } + + part.type = PATTERN_STRING; + part.data.str= g_string_new_len(first, c - first); + g_array_append_val(pattern, part); + } + } + + return (liPattern*) pattern; +} + + +void li_pattern_free(liPattern *pattern) { + guint i; + GArray *arr = (GArray*) pattern; + + for (i = 0; i < arr->len; i++) { + if (g_array_index(arr, liPatternPart, i).type == PATTERN_STRING) + g_string_free(g_array_index(arr, liPatternPart, i).data.str, TRUE); + } + + g_array_free(arr, TRUE); +} + +void li_pattern_eval(liVRequest *vr, GString *dest, liPattern *pattern, liPatternCB nth_callback, gpointer nth_data, liPatternCB nth_prev_callback, gpointer nth_prev_data) { + guint i; + gboolean encoded; + GString *str; + GString str_stack; + GArray *arr = (GArray*) pattern; + + for (i = 0; i < arr->len; i++) { + liPatternPart *part = &g_array_index(arr, liPatternPart, i); + encoded = FALSE; + + switch (part->type) { + case PATTERN_STRING: + g_string_append_len(dest, GSTR_LEN(part->data.str)); + break; + case PATTERN_NTH: + nth_callback(dest, part->data.ndx, nth_data); + break; + case PATTERN_NTH_PREV: + nth_prev_callback(dest, part->data.ndx, nth_prev_data); + break; + case PATTERN_VAR_ENCODED: + encoded = TRUE; + /* fall through */ + case PATTERN_VAR: + switch (part->data.var) { + case LI_COMP_REQUEST_LOCALIP: str = vr->con->local_addr_str; break; + case LI_COMP_REQUEST_REMOTEIP: str = vr->con->remote_addr_str; break; + case LI_COMP_REQUEST_SCHEME: + if (vr->con->is_ssl) + str_stack = li_const_gstring(CONST_STR_LEN("https")); + else + str_stack = li_const_gstring(CONST_STR_LEN("http")); + str = &str_stack; + break; + case LI_COMP_REQUEST_PATH: str = vr->request.uri.path; break; + case LI_COMP_REQUEST_HOST: str = vr->request.uri.host; break; + case LI_COMP_REQUEST_QUERY_STRING: str = vr->request.uri.query; break; + case LI_COMP_REQUEST_METHOD: str = vr->request.http_method_str; break; + case LI_COMP_REQUEST_CONTENT_LENGTH: + g_string_printf(vr->con->wrk->tmp_str, "%"L_GOFFSET_FORMAT, vr->request.content_length); + str = vr->con->wrk->tmp_str; + break; + default: continue; + } + + if (encoded) + li_string_encode_append(str->str, dest, LI_ENCODING_URI); + else + g_string_append_len(dest, GSTR_LEN(str)); + + break; + } + } +} + +void li_pattern_array_cb(GString *pattern_result, guint8 nth_ndx, gpointer data) { + GString *str = g_array_index((GArray*)data, GString*, nth_ndx); + + 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; + GMatchInfo *match_info = data; + + if (!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); +} diff --git a/src/main/wscript b/src/main/wscript index e2fb749..ecbb458 100644 --- a/src/main/wscript +++ b/src/main/wscript @@ -43,6 +43,7 @@ def build(bld): network_write.c network_writev.c options.c + pattern.c plugin.c plugin_core.c request.c