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