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