1#include "base.h" 2#include "log.h" 3#include "buffer.h" 4 5#include "plugin.h" 6#include "response.h" 7 8#include <ctype.h> 9#include <stdlib.h> 10#include <string.h> 11 12typedef struct { 13 pcre_keyvalue_buffer *redirect; 14 data_config *context; /* to which apply me */ 15 16 unsigned short redirect_code; 17} plugin_config; 18 19typedef struct { 20 PLUGIN_DATA; 21 buffer *match_buf; 22 buffer *location; 23 24 plugin_config **config_storage; 25 26 plugin_config conf; 27} plugin_data; 28 29INIT_FUNC(mod_redirect_init) { 30 plugin_data *p; 31 32 p = calloc(1, sizeof(*p)); 33 34 p->match_buf = buffer_init(); 35 p->location = buffer_init(); 36 37 return p; 38} 39 40FREE_FUNC(mod_redirect_free) { 41 plugin_data *p = p_d; 42 43 if (!p) return HANDLER_GO_ON; 44 45 if (p->config_storage) { 46 size_t i; 47 for (i = 0; i < srv->config_context->used; i++) { 48 plugin_config *s = p->config_storage[i]; 49 50 if (NULL == s) continue; 51 52 pcre_keyvalue_buffer_free(s->redirect); 53 54 free(s); 55 } 56 free(p->config_storage); 57 } 58 59 60 buffer_free(p->match_buf); 61 buffer_free(p->location); 62 63 free(p); 64 65 return HANDLER_GO_ON; 66} 67 68SETDEFAULTS_FUNC(mod_redirect_set_defaults) { 69 plugin_data *p = p_d; 70 size_t i = 0; 71 72 config_values_t cv[] = { 73 { "url.redirect", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 74 { "url.redirect-code", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 75 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 76 }; 77 78 if (!p) return HANDLER_ERROR; 79 80 /* 0 */ 81 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); 82 83 for (i = 0; i < srv->config_context->used; i++) { 84 data_config const* config = (data_config const*)srv->config_context->data[i]; 85 plugin_config *s; 86 size_t j; 87 data_unset *du; 88 data_array *da; 89 90 s = calloc(1, sizeof(plugin_config)); 91 s->redirect = pcre_keyvalue_buffer_init(); 92 s->redirect_code = 301; 93 94 cv[0].destination = s->redirect; 95 cv[1].destination = &(s->redirect_code); 96 97 p->config_storage[i] = s; 98 99 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 100 return HANDLER_ERROR; 101 } 102 103 if (NULL == (du = array_get_element(config->value, "url.redirect"))) { 104 /* no url.redirect defined */ 105 continue; 106 } 107 108 if (du->type != TYPE_ARRAY) { 109 log_error_write(srv, __FILE__, __LINE__, "sss", 110 "unexpected type for key: ", "url.redirect", "array of strings"); 111 112 return HANDLER_ERROR; 113 } 114 115 da = (data_array *)du; 116 117 for (j = 0; j < da->value->used; j++) { 118 if (da->value->data[j]->type != TYPE_STRING) { 119 log_error_write(srv, __FILE__, __LINE__, "sssbs", 120 "unexpected type for key: ", 121 "url.redirect", 122 "[", da->value->data[j]->key, "](string)"); 123 124 return HANDLER_ERROR; 125 } 126 127 if (0 != pcre_keyvalue_buffer_append(srv, s->redirect, 128 ((data_string *)(da->value->data[j]))->key->ptr, 129 ((data_string *)(da->value->data[j]))->value->ptr)) { 130 131 log_error_write(srv, __FILE__, __LINE__, "sb", 132 "pcre-compile failed for", da->value->data[j]->key); 133 return HANDLER_ERROR; 134 } 135 } 136 } 137 138 return HANDLER_GO_ON; 139} 140#ifdef HAVE_PCRE_H 141static int mod_redirect_patch_connection(server *srv, connection *con, plugin_data *p) { 142 size_t i, j; 143 plugin_config *s = p->config_storage[0]; 144 145 p->conf.redirect = s->redirect; 146 p->conf.redirect_code = s->redirect_code; 147 p->conf.context = NULL; 148 149 /* skip the first, the global context */ 150 for (i = 1; i < srv->config_context->used; i++) { 151 data_config *dc = (data_config *)srv->config_context->data[i]; 152 s = p->config_storage[i]; 153 154 /* condition didn't match */ 155 if (!config_check_cond(srv, con, dc)) continue; 156 157 /* merge config */ 158 for (j = 0; j < dc->value->used; j++) { 159 data_unset *du = dc->value->data[j]; 160 161 if (0 == strcmp(du->key->ptr, "url.redirect")) { 162 p->conf.redirect = s->redirect; 163 p->conf.context = dc; 164 } else if (0 == strcmp(du->key->ptr, "url.redirect-code")) { 165 p->conf.redirect_code = s->redirect_code; 166 } 167 } 168 } 169 170 return 0; 171} 172#endif 173static handler_t mod_redirect_uri_handler(server *srv, connection *con, void *p_data) { 174#ifdef HAVE_PCRE_H 175 plugin_data *p = p_data; 176 size_t i; 177 178 /* 179 * REWRITE URL 180 * 181 * e.g. redirect /base/ to /index.php?section=base 182 * 183 */ 184 185 mod_redirect_patch_connection(srv, con, p); 186 187 buffer_copy_buffer(p->match_buf, con->request.uri); 188 189 for (i = 0; i < p->conf.redirect->used; i++) { 190 pcre *match; 191 pcre_extra *extra; 192 const char *pattern; 193 size_t pattern_len; 194 int n; 195 pcre_keyvalue *kv = p->conf.redirect->kv[i]; 196# define N 10 197 int ovec[N * 3]; 198 199 match = kv->key; 200 extra = kv->key_extra; 201 pattern = kv->value->ptr; 202 pattern_len = buffer_string_length(kv->value); 203 204 if ((n = pcre_exec(match, extra, CONST_BUF_LEN(p->match_buf), 0, 0, ovec, 3 * N)) < 0) { 205 if (n != PCRE_ERROR_NOMATCH) { 206 log_error_write(srv, __FILE__, __LINE__, "sd", 207 "execution error while matching: ", n); 208 return HANDLER_ERROR; 209 } 210 } else { 211 const char **list; 212 size_t start; 213 size_t k; 214 215 /* it matched */ 216 pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list); 217 218 /* search for $[0-9] */ 219 220 buffer_reset(p->location); 221 222 start = 0; 223 for (k = 0; k + 1 < pattern_len; k++) { 224 if (pattern[k] == '$' || pattern[k] == '%') { 225 /* got one */ 226 227 size_t num = pattern[k + 1] - '0'; 228 229 buffer_append_string_len(p->location, pattern + start, k - start); 230 231 if (!isdigit((unsigned char)pattern[k + 1])) { 232 /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */ 233 buffer_append_string_len(p->location, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2); 234 } else if (pattern[k] == '$') { 235 /* n is always > 0 */ 236 if (num < (size_t)n) { 237 buffer_append_string(p->location, list[num]); 238 } 239 } else if (p->conf.context == NULL) { 240 /* we have no context, we are global */ 241 log_error_write(srv, __FILE__, __LINE__, "sb", 242 "used a rewrite containing a %[0-9]+ in the global scope, ignored:", 243 kv->value); 244 } else { 245 config_append_cond_match_buffer(con, p->conf.context, p->location, num); 246 } 247 248 k++; 249 start = k + 1; 250 } 251 } 252 253 buffer_append_string_len(p->location, pattern + start, pattern_len - start); 254 255 pcre_free(list); 256 257 response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(p->location)); 258 259 con->http_status = p->conf.redirect_code > 99 && p->conf.redirect_code < 1000 ? p->conf.redirect_code : 301; 260 con->mode = DIRECT; 261 con->file_finished = 1; 262 263 return HANDLER_FINISHED; 264 } 265 } 266#undef N 267 268#else 269 UNUSED(srv); 270 UNUSED(con); 271 UNUSED(p_data); 272#endif 273 274 return HANDLER_GO_ON; 275} 276 277 278int mod_redirect_plugin_init(plugin *p); 279int mod_redirect_plugin_init(plugin *p) { 280 281 p->version = LIGHTTPD_VERSION_ID; 282 p->name = buffer_init_string("redirect"); 283 284 p->init = mod_redirect_init; 285 p->handle_uri_clean = mod_redirect_uri_handler; 286 p->set_defaults = mod_redirect_set_defaults; 287 p->cleanup = mod_redirect_free; 288 289 p->data = NULL; 290 291 return 0; 292} 293