1 #include "plugin.h"
2 #include "log.h"
3 #include "response.h"
4 #include "stat_cache.h"
5 
6 #include <string.h>
7 #include <errno.h>
8 #include <ctype.h>
9 
10 typedef struct {
11 	/* unparsed pieces */
12 	buffer *path_pieces_raw;
13 
14 	/* pieces for path creation */
15 	size_t len;
16 	buffer **path_pieces;
17 } plugin_config;
18 
19 typedef struct {
20 	PLUGIN_DATA;
21 	buffer *tmp_buf;
22 
23 	plugin_config **config_storage;
24 	plugin_config conf;
25 } plugin_data;
26 
INIT_FUNC(mod_evhost_init)27 INIT_FUNC(mod_evhost_init) {
28 	plugin_data *p;
29 
30 	p = calloc(1, sizeof(*p));
31 
32 	p->tmp_buf = buffer_init();
33 
34 	return p;
35 }
36 
FREE_FUNC(mod_evhost_free)37 FREE_FUNC(mod_evhost_free) {
38 	plugin_data *p = p_d;
39 
40 	UNUSED(srv);
41 
42 	if (!p) return HANDLER_GO_ON;
43 
44 	if (p->config_storage) {
45 		size_t i;
46 		for (i = 0; i < srv->config_context->used; i++) {
47 			plugin_config *s = p->config_storage[i];
48 
49 			if (!s) continue;
50 
51 			if(s->path_pieces) {
52 				size_t j;
53 				for (j = 0; j < s->len; j++) {
54 					buffer_free(s->path_pieces[j]);
55 				}
56 
57 				free(s->path_pieces);
58 			}
59 
60 			buffer_free(s->path_pieces_raw);
61 
62 			free(s);
63 		}
64 		free(p->config_storage);
65 	}
66 
67 	buffer_free(p->tmp_buf);
68 
69 	free(p);
70 
71 	return HANDLER_GO_ON;
72 }
73 
mod_evhost_parse_pattern(plugin_config * s)74 static void mod_evhost_parse_pattern(plugin_config *s) {
75 	char *ptr = s->path_pieces_raw->ptr,*pos;
76 
77 	s->path_pieces = NULL;
78 
79 	for(pos=ptr;*ptr;ptr++) {
80 		if(*ptr == '%') {
81 			s->path_pieces = realloc(s->path_pieces,(s->len+2) * sizeof(*s->path_pieces));
82 			s->path_pieces[s->len] = buffer_init();
83 			s->path_pieces[s->len+1] = buffer_init();
84 
85 			buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos);
86 			pos = ptr + 2;
87 
88 			buffer_copy_string_len(s->path_pieces[s->len+1],ptr++,2);
89 
90 			s->len += 2;
91 		}
92 	}
93 
94 	if(*pos != '\0') {
95 		s->path_pieces = realloc(s->path_pieces,(s->len+1) * sizeof(*s->path_pieces));
96 		s->path_pieces[s->len] = buffer_init();
97 
98 		buffer_copy_string_len(s->path_pieces[s->len],pos,ptr-pos);
99 
100 		s->len += 1;
101 	}
102 }
103 
SETDEFAULTS_FUNC(mod_evhost_set_defaults)104 SETDEFAULTS_FUNC(mod_evhost_set_defaults) {
105 	plugin_data *p = p_d;
106 	size_t i;
107 
108 	/**
109 	 *
110 	 * #
111 	 * # define a pattern for the host url finding
112 	 * # %% => % sign
113 	 * # %0 => domain name + tld
114 	 * # %1 => tld
115 	 * # %2 => domain name without tld
116 	 * # %3 => subdomain 1 name
117 	 * # %4 => subdomain 2 name
118 	 * # %_ => fqdn (without port info)
119 	 * #
120 	 * evhost.path-pattern = "/home/ckruse/dev/www/%3/htdocs/"
121 	 *
122 	 */
123 
124 	config_values_t cv[] = {
125 		{ "evhost.path-pattern",            NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },
126 		{ NULL,                             NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
127 	};
128 
129 	if (!p) return HANDLER_ERROR;
130 
131 	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
132 
133 	for (i = 0; i < srv->config_context->used; i++) {
134 		plugin_config *s;
135 
136 		s = calloc(1, sizeof(plugin_config));
137 		s->path_pieces_raw = buffer_init();
138 		s->path_pieces     = NULL;
139 		s->len             = 0;
140 
141 		cv[0].destination = s->path_pieces_raw;
142 
143 		p->config_storage[i] = s;
144 
145 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value,  cv)) {
146 			return HANDLER_ERROR;
147 		}
148 
149 		if (s->path_pieces_raw->used != 0) {
150 			mod_evhost_parse_pattern(s);
151 		}
152 	}
153 
154 	return HANDLER_GO_ON;
155 }
156 
157 /**
158  * assign the different parts of the domain to array-indezes (sub2.sub1.domain.tld)
159  * - %0 - domain.tld
160  * - %1 - tld
161  * - %2 - domain
162  * - %3 - sub1
163  * - ...
164  */
165 
mod_evhost_parse_host(connection * con,array * host)166 static int mod_evhost_parse_host(connection *con,array *host) {
167 	/* con->uri.authority->used is always > 0 if we come here */
168 	register char *ptr = con->uri.authority->ptr + con->uri.authority->used - 1;
169 	char *colon = ptr; /* needed to filter out the colon (if exists) */
170 	int first = 1;
171 	data_string *ds;
172 	int i;
173 
174 	/* first, find the domain + tld */
175 	for(;ptr > con->uri.authority->ptr;ptr--) {
176 		if(*ptr == '.') {
177 			if(first) first = 0;
178 			else      break;
179 		} else if(*ptr == ':') {
180 			colon = ptr;
181 			first = 1;
182 		}
183 	}
184 
185 	ds = data_string_init();
186 	buffer_copy_string_len(ds->key,CONST_STR_LEN("%0"));
187 
188 	/* if we stopped at a dot, skip the dot */
189 	if (*ptr == '.') ptr++;
190 	buffer_copy_string_len(ds->value, ptr, colon-ptr);
191 
192 	array_insert_unique(host,(data_unset *)ds);
193 
194 	/* if the : is not the start of the authority, go on parsing the hostname */
195 
196 	if (colon != con->uri.authority->ptr) {
197 		for(ptr = colon - 1, i = 1; ptr > con->uri.authority->ptr; ptr--) {
198 			if(*ptr == '.') {
199 				if (ptr != colon - 1) {
200 					/* is something between the dots */
201 					ds = data_string_init();
202 					buffer_copy_string_len(ds->key,CONST_STR_LEN("%"));
203 					buffer_append_long(ds->key, i++);
204 					buffer_copy_string_len(ds->value,ptr+1,colon-ptr-1);
205 
206 					array_insert_unique(host,(data_unset *)ds);
207 				}
208 				colon = ptr;
209 			}
210 		}
211 
212 		/* if the . is not the first charactor of the hostname */
213 		if (colon != ptr) {
214 			ds = data_string_init();
215 			buffer_copy_string_len(ds->key,CONST_STR_LEN("%"));
216 			buffer_append_long(ds->key, i /* ++ */);
217 			buffer_copy_string_len(ds->value,ptr,colon-ptr);
218 
219 			array_insert_unique(host,(data_unset *)ds);
220 		}
221 	}
222 
223 	return 0;
224 }
225 
226 #define PATCH(x) \
227 	p->conf.x = s->x;
mod_evhost_patch_connection(server * srv,connection * con,plugin_data * p)228 static int mod_evhost_patch_connection(server *srv, connection *con, plugin_data *p) {
229 	size_t i, j;
230 	plugin_config *s = p->config_storage[0];
231 
232 	PATCH(path_pieces);
233 	PATCH(len);
234 
235 	/* skip the first, the global context */
236 	for (i = 1; i < srv->config_context->used; i++) {
237 		data_config *dc = (data_config *)srv->config_context->data[i];
238 		s = p->config_storage[i];
239 
240 		/* condition didn't match */
241 		if (!config_check_cond(srv, con, dc)) continue;
242 
243 		/* merge config */
244 		for (j = 0; j < dc->value->used; j++) {
245 			data_unset *du = dc->value->data[j];
246 
247 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("evhost.path-pattern"))) {
248 				PATCH(path_pieces);
249 				PATCH(len);
250 			}
251 		}
252 	}
253 
254 	return 0;
255 }
256 #undef PATCH
257 
258 
mod_evhost_uri_handler(server * srv,connection * con,void * p_d)259 static handler_t mod_evhost_uri_handler(server *srv, connection *con, void *p_d) {
260 	plugin_data *p = p_d;
261 	size_t i;
262 	array *parsed_host;
263 	register char *ptr;
264 	int not_good = 0;
265 	stat_cache_entry *sce = NULL;
266 
267 	/* not authority set */
268 	if (con->uri.authority->used == 0) return HANDLER_GO_ON;
269 
270 	mod_evhost_patch_connection(srv, con, p);
271 
272 	/* missing even default(global) conf */
273 	if (0 == p->conf.len) {
274 		return HANDLER_GO_ON;
275 	}
276 
277 	parsed_host = array_init();
278 
279 	mod_evhost_parse_host(con, parsed_host);
280 
281 	/* build document-root */
282 	buffer_reset(p->tmp_buf);
283 
284 	for (i = 0; i < p->conf.len; i++) {
285 		ptr = p->conf.path_pieces[i]->ptr;
286 		if (*ptr == '%') {
287 			data_string *ds;
288 
289 			if (*(ptr+1) == '%') {
290 				/* %% */
291 				buffer_append_string_len(p->tmp_buf,CONST_STR_LEN("%"));
292 			} else if (*(ptr+1) == '_' ) {
293 				/* %_ == full hostname */
294 				char *colon = strchr(con->uri.authority->ptr, ':');
295 
296 				if(colon == NULL) {
297 					buffer_append_string_buffer(p->tmp_buf, con->uri.authority); /* adds fqdn */
298 				} else {
299 					/* strip the port out of the authority-part of the URI scheme */
300 					buffer_append_string_len(p->tmp_buf, con->uri.authority->ptr, colon - con->uri.authority->ptr); /* adds fqdn */
301 				}
302 			} else if (NULL != (ds = (data_string *)array_get_element(parsed_host,p->conf.path_pieces[i]->ptr))) {
303 				if (ds->value->used) {
304 					buffer_append_string_buffer(p->tmp_buf,ds->value);
305 				}
306 			} else {
307 				/* unhandled %-sequence */
308 			}
309 		} else {
310 			buffer_append_string_buffer(p->tmp_buf,p->conf.path_pieces[i]);
311 		}
312 	}
313 
314 	BUFFER_APPEND_SLASH(p->tmp_buf);
315 
316 	array_free(parsed_host);
317 
318 	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, p->tmp_buf, &sce)) {
319 		log_error_write(srv, __FILE__, __LINE__, "sb", strerror(errno), p->tmp_buf);
320 		not_good = 1;
321 	} else if(!S_ISDIR(sce->st.st_mode)) {
322 		log_error_write(srv, __FILE__, __LINE__, "sb", "not a directory:", p->tmp_buf);
323 		not_good = 1;
324 	}
325 
326 	if (!not_good) {
327 		buffer_copy_string_buffer(con->physical.doc_root, p->tmp_buf);
328 	}
329 
330 	return HANDLER_GO_ON;
331 }
332 
333 int mod_evhost_plugin_init(plugin *p);
mod_evhost_plugin_init(plugin * p)334 int mod_evhost_plugin_init(plugin *p) {
335 	p->version     = LIGHTTPD_VERSION_ID;
336 	p->name                    = buffer_init_string("evhost");
337 	p->init                    = mod_evhost_init;
338 	p->set_defaults            = mod_evhost_set_defaults;
339 	p->handle_docroot          = mod_evhost_uri_handler;
340 	p->cleanup                 = mod_evhost_free;
341 
342 	p->data                    = NULL;
343 
344 	return 0;
345 }
346 
347 /* eof */
348