Annotation of embedaddon/lighttpd/src/mod_rewrite.c, revision 1.1
1.1 ! misho 1: #include "base.h"
! 2: #include "log.h"
! 3: #include "buffer.h"
! 4:
! 5: #include "plugin.h"
! 6: #include "stat_cache.h"
! 7:
! 8: #include <ctype.h>
! 9: #include <stdlib.h>
! 10: #include <string.h>
! 11:
! 12: #ifdef HAVE_PCRE_H
! 13: typedef struct {
! 14: pcre *key;
! 15:
! 16: buffer *value;
! 17:
! 18: int once;
! 19: } rewrite_rule;
! 20:
! 21: typedef struct {
! 22: rewrite_rule **ptr;
! 23:
! 24: size_t used;
! 25: size_t size;
! 26: } rewrite_rule_buffer;
! 27:
! 28: typedef struct {
! 29: rewrite_rule_buffer *rewrite;
! 30: rewrite_rule_buffer *rewrite_NF;
! 31: data_config *context, *context_NF; /* to which apply me */
! 32: } plugin_config;
! 33:
! 34: typedef struct {
! 35: enum { REWRITE_STATE_UNSET, REWRITE_STATE_FINISHED} state;
! 36: int loops;
! 37: } handler_ctx;
! 38:
! 39: typedef struct {
! 40: PLUGIN_DATA;
! 41: buffer *match_buf;
! 42:
! 43: plugin_config **config_storage;
! 44:
! 45: plugin_config conf;
! 46: } plugin_data;
! 47:
! 48: static handler_ctx * handler_ctx_init(void) {
! 49: handler_ctx * hctx;
! 50:
! 51: hctx = calloc(1, sizeof(*hctx));
! 52:
! 53: hctx->state = REWRITE_STATE_UNSET;
! 54: hctx->loops = 0;
! 55:
! 56: return hctx;
! 57: }
! 58:
! 59: static void handler_ctx_free(handler_ctx *hctx) {
! 60: free(hctx);
! 61: }
! 62:
! 63: static rewrite_rule_buffer *rewrite_rule_buffer_init(void) {
! 64: rewrite_rule_buffer *kvb;
! 65:
! 66: kvb = calloc(1, sizeof(*kvb));
! 67:
! 68: return kvb;
! 69: }
! 70:
! 71: static int rewrite_rule_buffer_append(rewrite_rule_buffer *kvb, buffer *key, buffer *value, int once) {
! 72: size_t i;
! 73: const char *errptr;
! 74: int erroff;
! 75:
! 76: if (!key) return -1;
! 77:
! 78: if (kvb->size == 0) {
! 79: kvb->size = 4;
! 80: kvb->used = 0;
! 81:
! 82: kvb->ptr = malloc(kvb->size * sizeof(*kvb->ptr));
! 83:
! 84: for(i = 0; i < kvb->size; i++) {
! 85: kvb->ptr[i] = calloc(1, sizeof(**kvb->ptr));
! 86: }
! 87: } else if (kvb->used == kvb->size) {
! 88: kvb->size += 4;
! 89:
! 90: kvb->ptr = realloc(kvb->ptr, kvb->size * sizeof(*kvb->ptr));
! 91:
! 92: for(i = kvb->used; i < kvb->size; i++) {
! 93: kvb->ptr[i] = calloc(1, sizeof(**kvb->ptr));
! 94: }
! 95: }
! 96:
! 97: if (NULL == (kvb->ptr[kvb->used]->key = pcre_compile(key->ptr,
! 98: 0, &errptr, &erroff, NULL))) {
! 99:
! 100: return -1;
! 101: }
! 102:
! 103: kvb->ptr[kvb->used]->value = buffer_init();
! 104: buffer_copy_string_buffer(kvb->ptr[kvb->used]->value, value);
! 105: kvb->ptr[kvb->used]->once = once;
! 106:
! 107: kvb->used++;
! 108:
! 109: return 0;
! 110: }
! 111:
! 112: static void rewrite_rule_buffer_free(rewrite_rule_buffer *kvb) {
! 113: size_t i;
! 114:
! 115: for (i = 0; i < kvb->size; i++) {
! 116: if (kvb->ptr[i]->key) pcre_free(kvb->ptr[i]->key);
! 117: if (kvb->ptr[i]->value) buffer_free(kvb->ptr[i]->value);
! 118: free(kvb->ptr[i]);
! 119: }
! 120:
! 121: if (kvb->ptr) free(kvb->ptr);
! 122:
! 123: free(kvb);
! 124: }
! 125:
! 126:
! 127: INIT_FUNC(mod_rewrite_init) {
! 128: plugin_data *p;
! 129:
! 130: p = calloc(1, sizeof(*p));
! 131:
! 132: p->match_buf = buffer_init();
! 133:
! 134: return p;
! 135: }
! 136:
! 137: FREE_FUNC(mod_rewrite_free) {
! 138: plugin_data *p = p_d;
! 139:
! 140: UNUSED(srv);
! 141:
! 142: if (!p) return HANDLER_GO_ON;
! 143:
! 144: buffer_free(p->match_buf);
! 145: if (p->config_storage) {
! 146: size_t i;
! 147: for (i = 0; i < srv->config_context->used; i++) {
! 148: plugin_config *s = p->config_storage[i];
! 149: rewrite_rule_buffer_free(s->rewrite);
! 150: rewrite_rule_buffer_free(s->rewrite_NF);
! 151:
! 152: free(s);
! 153: }
! 154: free(p->config_storage);
! 155: }
! 156:
! 157: free(p);
! 158:
! 159: return HANDLER_GO_ON;
! 160: }
! 161:
! 162: static int parse_config_entry(server *srv, array *ca, rewrite_rule_buffer *kvb, const char *option, int once) {
! 163: data_unset *du;
! 164:
! 165: if (NULL != (du = array_get_element(ca, option))) {
! 166: data_array *da;
! 167: size_t j;
! 168:
! 169: if (du->type != TYPE_ARRAY) {
! 170: log_error_write(srv, __FILE__, __LINE__, "sss",
! 171: "unexpected type for key: ", option, "array of strings");
! 172:
! 173: return HANDLER_ERROR;
! 174: }
! 175:
! 176: da = (data_array *)du;
! 177:
! 178: for (j = 0; j < da->value->used; j++) {
! 179: if (da->value->data[j]->type != TYPE_STRING) {
! 180: log_error_write(srv, __FILE__, __LINE__, "sssbs",
! 181: "unexpected type for key: ",
! 182: option,
! 183: "[", da->value->data[j]->key, "](string)");
! 184:
! 185: return HANDLER_ERROR;
! 186: }
! 187:
! 188: if (0 != rewrite_rule_buffer_append(kvb,
! 189: ((data_string *)(da->value->data[j]))->key,
! 190: ((data_string *)(da->value->data[j]))->value,
! 191: once)) {
! 192: log_error_write(srv, __FILE__, __LINE__, "sb",
! 193: "pcre-compile failed for", da->value->data[j]->key);
! 194: }
! 195: }
! 196: }
! 197:
! 198: return 0;
! 199: }
! 200: #else
! 201: static int parse_config_entry(server *srv, array *ca, const char *option) {
! 202: static int logged_message = 0;
! 203: if (logged_message) return 0;
! 204: if (NULL != array_get_element(ca, option)) {
! 205: logged_message = 1;
! 206: log_error_write(srv, __FILE__, __LINE__, "s",
! 207: "pcre support is missing, please install libpcre and the headers");
! 208: }
! 209: return 0;
! 210: }
! 211: #endif
! 212:
! 213: SETDEFAULTS_FUNC(mod_rewrite_set_defaults) {
! 214: size_t i = 0;
! 215: config_values_t cv[] = {
! 216: { "url.rewrite-repeat", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
! 217: { "url.rewrite-once", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
! 218:
! 219: /* these functions only rewrite if the target is not already in the filestore
! 220: *
! 221: * url.rewrite-repeat-if-not-file is the equivalent of url.rewrite-repeat
! 222: * url.rewrite-if-not-file is the equivalent of url.rewrite-once
! 223: *
! 224: */
! 225: { "url.rewrite-repeat-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
! 226: { "url.rewrite-if-not-file", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
! 227:
! 228: /* old names, still supported
! 229: *
! 230: * url.rewrite remapped to url.rewrite-once
! 231: * url.rewrite-final is url.rewrite-once
! 232: *
! 233: */
! 234: { "url.rewrite", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
! 235: { "url.rewrite-final", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
! 236: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
! 237: };
! 238:
! 239: #ifdef HAVE_PCRE_H
! 240: plugin_data *p = p_d;
! 241:
! 242: if (!p) return HANDLER_ERROR;
! 243:
! 244: /* 0 */
! 245: p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
! 246: #else
! 247: UNUSED(p_d);
! 248: #endif
! 249:
! 250: for (i = 0; i < srv->config_context->used; i++) {
! 251: array *ca;
! 252: #ifdef HAVE_PCRE_H
! 253: plugin_config *s;
! 254:
! 255: s = calloc(1, sizeof(plugin_config));
! 256: s->rewrite = rewrite_rule_buffer_init();
! 257: s->rewrite_NF = rewrite_rule_buffer_init();
! 258: p->config_storage[i] = s;
! 259: #endif
! 260:
! 261: ca = ((data_config *)srv->config_context->data[i])->value;
! 262:
! 263: if (0 != config_insert_values_global(srv, ca, cv)) {
! 264: return HANDLER_ERROR;
! 265: }
! 266:
! 267: #ifndef HAVE_PCRE_H
! 268: # define parse_config_entry(srv, ca, x, option, y) parse_config_entry(srv, ca, option)
! 269: #endif
! 270: parse_config_entry(srv, ca, s->rewrite, "url.rewrite-once", 1);
! 271: parse_config_entry(srv, ca, s->rewrite, "url.rewrite-final", 1);
! 272: parse_config_entry(srv, ca, s->rewrite_NF, "url.rewrite-if-not-file", 1);
! 273: parse_config_entry(srv, ca, s->rewrite_NF, "url.rewrite-repeat-if-not-file", 0);
! 274: parse_config_entry(srv, ca, s->rewrite, "url.rewrite", 1);
! 275: parse_config_entry(srv, ca, s->rewrite, "url.rewrite-repeat", 0);
! 276: }
! 277:
! 278: return HANDLER_GO_ON;
! 279: }
! 280:
! 281: #ifdef HAVE_PCRE_H
! 282:
! 283: #define PATCH(x) \
! 284: p->conf.x = s->x;
! 285: static int mod_rewrite_patch_connection(server *srv, connection *con, plugin_data *p) {
! 286: size_t i, j;
! 287: plugin_config *s = p->config_storage[0];
! 288:
! 289: PATCH(rewrite);
! 290: PATCH(rewrite_NF);
! 291: p->conf.context = NULL;
! 292: p->conf.context_NF = NULL;
! 293:
! 294: /* skip the first, the global context */
! 295: for (i = 1; i < srv->config_context->used; i++) {
! 296: data_config *dc = (data_config *)srv->config_context->data[i];
! 297: s = p->config_storage[i];
! 298:
! 299: if (COMP_HTTP_URL == dc->comp) continue;
! 300:
! 301: /* condition didn't match */
! 302: if (!config_check_cond(srv, con, dc)) continue;
! 303:
! 304: /* merge config */
! 305: for (j = 0; j < dc->value->used; j++) {
! 306: data_unset *du = dc->value->data[j];
! 307:
! 308: if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite"))) {
! 309: PATCH(rewrite);
! 310: p->conf.context = dc;
! 311: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-once"))) {
! 312: PATCH(rewrite);
! 313: p->conf.context = dc;
! 314: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat"))) {
! 315: PATCH(rewrite);
! 316: p->conf.context = dc;
! 317: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-if-not-file"))) {
! 318: PATCH(rewrite_NF);
! 319: p->conf.context_NF = dc;
! 320: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-repeat-if-not-file"))) {
! 321: PATCH(rewrite_NF);
! 322: p->conf.context_NF = dc;
! 323: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("url.rewrite-final"))) {
! 324: PATCH(rewrite);
! 325: p->conf.context = dc;
! 326: }
! 327: }
! 328: }
! 329:
! 330: return 0;
! 331: }
! 332:
! 333: URIHANDLER_FUNC(mod_rewrite_con_reset) {
! 334: plugin_data *p = p_d;
! 335:
! 336: UNUSED(srv);
! 337:
! 338: if (con->plugin_ctx[p->id]) {
! 339: handler_ctx_free(con->plugin_ctx[p->id]);
! 340: con->plugin_ctx[p->id] = NULL;
! 341: }
! 342:
! 343: return HANDLER_GO_ON;
! 344: }
! 345:
! 346: static int process_rewrite_rules(server *srv, connection *con, plugin_data *p, rewrite_rule_buffer *kvb) {
! 347: size_t i;
! 348: handler_ctx *hctx;
! 349:
! 350: if (con->plugin_ctx[p->id]) {
! 351: hctx = con->plugin_ctx[p->id];
! 352:
! 353: if (hctx->loops++ > 100) {
! 354: log_error_write(srv, __FILE__, __LINE__, "s",
! 355: "ENDLESS LOOP IN rewrite-rule DETECTED ... aborting request, perhaps you want to use url.rewrite-once instead of url.rewrite-repeat");
! 356:
! 357: return HANDLER_ERROR;
! 358: }
! 359:
! 360: if (hctx->state == REWRITE_STATE_FINISHED) return HANDLER_GO_ON;
! 361: }
! 362:
! 363: buffer_copy_string_buffer(p->match_buf, con->request.uri);
! 364:
! 365: for (i = 0; i < kvb->used; i++) {
! 366: pcre *match;
! 367: const char *pattern;
! 368: size_t pattern_len;
! 369: int n;
! 370: rewrite_rule *rule = kvb->ptr[i];
! 371: # define N 10
! 372: int ovec[N * 3];
! 373:
! 374: match = rule->key;
! 375: pattern = rule->value->ptr;
! 376: pattern_len = rule->value->used - 1;
! 377:
! 378: if ((n = pcre_exec(match, NULL, p->match_buf->ptr, p->match_buf->used - 1, 0, 0, ovec, 3 * N)) < 0) {
! 379: if (n != PCRE_ERROR_NOMATCH) {
! 380: log_error_write(srv, __FILE__, __LINE__, "sd",
! 381: "execution error while matching: ", n);
! 382: return HANDLER_ERROR;
! 383: }
! 384: } else {
! 385: const char **list;
! 386: size_t start;
! 387: size_t k;
! 388:
! 389: /* it matched */
! 390: pcre_get_substring_list(p->match_buf->ptr, ovec, n, &list);
! 391:
! 392: /* search for $[0-9] */
! 393:
! 394: buffer_reset(con->request.uri);
! 395:
! 396: start = 0;
! 397: for (k = 0; k+1 < pattern_len; k++) {
! 398: if (pattern[k] == '$' || pattern[k] == '%') {
! 399: /* got one */
! 400:
! 401: size_t num = pattern[k + 1] - '0';
! 402:
! 403: buffer_append_string_len(con->request.uri, pattern + start, k - start);
! 404:
! 405: if (!isdigit((unsigned char)pattern[k + 1])) {
! 406: /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
! 407: buffer_append_string_len(con->request.uri, pattern+k, pattern[k] == pattern[k+1] ? 1 : 2);
! 408: } else if (pattern[k] == '$') {
! 409: /* n is always > 0 */
! 410: if (num < (size_t)n) {
! 411: buffer_append_string(con->request.uri, list[num]);
! 412: }
! 413: } else if (p->conf.context == NULL) {
! 414: /* we have no context, we are global */
! 415: log_error_write(srv, __FILE__, __LINE__, "sb",
! 416: "used a redirect containing a %[0-9]+ in the global scope, ignored:",
! 417: rule->value);
! 418:
! 419: } else {
! 420: config_append_cond_match_buffer(con, p->conf.context, con->request.uri, num);
! 421: }
! 422:
! 423: k++;
! 424: start = k + 1;
! 425: }
! 426: }
! 427:
! 428: buffer_append_string_len(con->request.uri, pattern + start, pattern_len - start);
! 429:
! 430: pcre_free(list);
! 431:
! 432: if (con->plugin_ctx[p->id] == NULL) {
! 433: hctx = handler_ctx_init();
! 434: con->plugin_ctx[p->id] = hctx;
! 435: } else {
! 436: hctx = con->plugin_ctx[p->id];
! 437: }
! 438:
! 439: if (rule->once) hctx->state = REWRITE_STATE_FINISHED;
! 440:
! 441: return HANDLER_COMEBACK;
! 442: }
! 443: #undef N
! 444: }
! 445:
! 446: return HANDLER_GO_ON;
! 447: }
! 448:
! 449: URIHANDLER_FUNC(mod_rewrite_physical) {
! 450: plugin_data *p = p_d;
! 451: handler_t r;
! 452: stat_cache_entry *sce;
! 453:
! 454: if (con->mode != DIRECT) return HANDLER_GO_ON;
! 455:
! 456: mod_rewrite_patch_connection(srv, con, p);
! 457: p->conf.context = p->conf.context_NF;
! 458:
! 459: if (!p->conf.rewrite_NF) return HANDLER_GO_ON;
! 460:
! 461: /* skip if physical.path is a regular file */
! 462: sce = NULL;
! 463: if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
! 464: if (S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
! 465: }
! 466:
! 467: switch(r = process_rewrite_rules(srv, con, p, p->conf.rewrite_NF)) {
! 468: case HANDLER_COMEBACK:
! 469: buffer_reset(con->physical.path);
! 470: default:
! 471: return r;
! 472: }
! 473:
! 474: return HANDLER_GO_ON;
! 475: }
! 476:
! 477: URIHANDLER_FUNC(mod_rewrite_uri_handler) {
! 478: plugin_data *p = p_d;
! 479:
! 480: mod_rewrite_patch_connection(srv, con, p);
! 481:
! 482: if (!p->conf.rewrite) return HANDLER_GO_ON;
! 483:
! 484: return process_rewrite_rules(srv, con, p, p->conf.rewrite);
! 485:
! 486: return HANDLER_GO_ON;
! 487: }
! 488: #endif
! 489:
! 490: int mod_rewrite_plugin_init(plugin *p);
! 491: int mod_rewrite_plugin_init(plugin *p) {
! 492: p->version = LIGHTTPD_VERSION_ID;
! 493: p->name = buffer_init_string("rewrite");
! 494:
! 495: #ifdef HAVE_PCRE_H
! 496: p->init = mod_rewrite_init;
! 497: /* it has to stay _raw as we are matching on uri + querystring
! 498: */
! 499:
! 500: p->handle_uri_raw = mod_rewrite_uri_handler;
! 501: p->handle_physical = mod_rewrite_physical;
! 502: p->cleanup = mod_rewrite_free;
! 503: p->connection_reset = mod_rewrite_con_reset;
! 504: #endif
! 505: p->set_defaults = mod_rewrite_set_defaults;
! 506:
! 507: p->data = NULL;
! 508:
! 509: return 0;
! 510: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>