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