6 #include "stat_cache.h"
14 buffer
*path_pieces_raw
;
16 /* pieces for path creation */
25 plugin_config
**config_storage
;
29 INIT_FUNC(mod_evhost_init
) {
32 p
= calloc(1, sizeof(*p
));
34 p
->tmp_buf
= buffer_init();
39 FREE_FUNC(mod_evhost_free
) {
44 if (!p
) return HANDLER_GO_ON
;
46 if (p
->config_storage
) {
48 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
49 plugin_config
*s
= p
->config_storage
[i
];
51 if (NULL
== s
) continue;
55 for (j
= 0; j
< s
->len
; j
++) {
56 buffer_free(s
->path_pieces
[j
]);
62 buffer_free(s
->path_pieces_raw
);
66 free(p
->config_storage
);
69 buffer_free(p
->tmp_buf
);
76 static int mod_evhost_parse_pattern(plugin_config
*s
) {
77 char *ptr
= s
->path_pieces_raw
->ptr
,*pos
;
79 s
->path_pieces
= NULL
;
81 for(pos
=ptr
;*ptr
;ptr
++) {
84 s
->path_pieces
= realloc(s
->path_pieces
,(s
->len
+2) * sizeof(*s
->path_pieces
));
85 s
->path_pieces
[s
->len
] = buffer_init();
86 s
->path_pieces
[s
->len
+1] = buffer_init();
88 /* "%%" "%_" "%x" "%{x.y}" where x and y are *single digit* 0 - 9 */
89 if (ptr
[1] == '%' || ptr
[1] == '_' || light_isdigit(ptr
[1])) {
91 } else if (ptr
[1] == '{') {
92 if (!light_isdigit(ptr
[2])) return -1;
94 if (!light_isdigit(ptr
[4])) return -1;
95 if (ptr
[5] != '}') return -1;
97 } else if (ptr
[3] == '}') {
106 buffer_copy_string_len(s
->path_pieces
[s
->len
],pos
,ptr
-pos
);
109 buffer_copy_string_len(s
->path_pieces
[s
->len
+1],ptr
,len
);
110 ptr
+= len
- 1; /*(ptr++ in for() loop)*/
117 s
->path_pieces
= realloc(s
->path_pieces
,(s
->len
+1) * sizeof(*s
->path_pieces
));
118 s
->path_pieces
[s
->len
] = buffer_init();
120 buffer_copy_string_len(s
->path_pieces
[s
->len
],pos
,ptr
-pos
);
128 SETDEFAULTS_FUNC(mod_evhost_set_defaults
) {
129 plugin_data
*p
= p_d
;
135 * # define a pattern for the host url finding
137 * # %0 => domain name + tld
139 * # %2 => domain name without tld
140 * # %3 => subdomain 1 name
141 * # %4 => subdomain 2 name
142 * # %_ => fqdn (without port info)
144 * evhost.path-pattern = "/home/ckruse/dev/www/%3/htdocs/"
148 config_values_t cv
[] = {
149 { "evhost.path-pattern", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
},
150 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
153 if (!p
) return HANDLER_ERROR
;
155 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
157 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
158 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
161 s
= calloc(1, sizeof(plugin_config
));
162 s
->path_pieces_raw
= buffer_init();
163 s
->path_pieces
= NULL
;
166 cv
[0].destination
= s
->path_pieces_raw
;
168 p
->config_storage
[i
] = s
;
170 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
171 return HANDLER_ERROR
;
174 if (!buffer_string_is_empty(s
->path_pieces_raw
)) {
175 if (0 != mod_evhost_parse_pattern(s
)) {
176 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "invalid evhost.path-pattern:", s
->path_pieces_raw
);
177 return HANDLER_ERROR
;
182 return HANDLER_GO_ON
;
186 * assign the different parts of the domain to array-indezes (sub2.sub1.domain.tld)
194 static void mod_evhost_parse_host(buffer
*key
, array
*host
, buffer
*authority
) {
195 char *ptr
= authority
->ptr
+ buffer_string_length(authority
);
196 char *colon
= ptr
; /* needed to filter out the colon (if exists) */
200 /*if (ptr == authority->ptr) return;*//*(no authority checked earlier)*/
202 if (*authority
->ptr
== '[') { /* authority is IPv6 literal address */
204 if (ptr
[-1] != ']') {
205 do { --ptr
; } while (ptr
> authority
->ptr
&& ptr
[-1] != ']');
206 if (*ptr
!= ':') return; /*(should not happen for valid authority)*/
209 ptr
= authority
->ptr
;
210 array_insert_key_value(host
,CONST_STR_LEN("%0"),ptr
,colon
-ptr
);
214 /* first, find the domain + tld */
215 for(; ptr
> authority
->ptr
; --ptr
) {
219 } else if(*ptr
== ':') {
225 /* if we stopped at a dot, skip the dot */
226 if (*ptr
== '.') ptr
++;
227 array_insert_key_value(host
, CONST_STR_LEN("%0"), ptr
, colon
-ptr
);
229 /* if the : is not the start of the authority, go on parsing the hostname */
231 if (colon
!= authority
->ptr
) {
232 for(ptr
= colon
- 1, i
= 1; ptr
> authority
->ptr
; --ptr
) {
234 if (ptr
!= colon
- 1) {
235 /* is something between the dots */
236 buffer_copy_string_len(key
, CONST_STR_LEN("%"));
237 buffer_append_int(key
, i
++);
238 array_insert_key_value(host
, CONST_BUF_LEN(key
), ptr
+1, colon
-ptr
-1);
244 /* if the . is not the first charactor of the hostname */
246 buffer_copy_string_len(key
, CONST_STR_LEN("%"));
247 buffer_append_int(key
, i
/* ++ */);
248 array_insert_key_value(host
, CONST_BUF_LEN(key
), ptr
, colon
-ptr
);
255 static int mod_evhost_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
257 plugin_config
*s
= p
->config_storage
[0];
262 /* skip the first, the global context */
263 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
264 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
265 s
= p
->config_storage
[i
];
267 /* condition didn't match */
268 if (!config_check_cond(srv
, con
, dc
)) continue;
271 for (j
= 0; j
< dc
->value
->used
; j
++) {
272 data_unset
*du
= dc
->value
->data
[j
];
274 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("evhost.path-pattern"))) {
286 static void mod_evhost_build_doc_root_path(buffer
*b
, array
*parsed_host
, buffer
*authority
, buffer
**path_pieces
, const size_t npieces
) {
287 array_reset_data_strings(parsed_host
);
288 mod_evhost_parse_host(b
, parsed_host
, authority
);
291 for (size_t i
= 0; i
< npieces
; ++i
) {
292 const char *ptr
= path_pieces
[i
]->ptr
;
296 if (*(ptr
+1) == '%') {
298 buffer_append_string_len(b
, CONST_STR_LEN("%"));
299 } else if (*(ptr
+1) == '_' ) {
300 /* %_ == full hostname */
301 char *colon
= strchr(authority
->ptr
, ':');
304 buffer_append_string_buffer(b
, authority
); /* adds fqdn */
306 /* strip the port out of the authority-part of the URI scheme */
307 buffer_append_string_len(b
, authority
->ptr
, colon
- authority
->ptr
); /* adds fqdn */
309 } else if (ptr
[1] == '{' ) {
311 s
[1] = ptr
[2]; /*(assumes single digit before '.', and, optionally, '.' and single digit after '.')*/
312 if (NULL
!= (ds
= (data_string
*)array_get_element_klen(parsed_host
, s
, 2))) {
313 if (ptr
[3] != '.' || ptr
[4] == '0') {
314 buffer_append_string_buffer(b
, ds
->value
);
316 if ((size_t)(ptr
[4]-'0') <= buffer_string_length(ds
->value
)) {
317 buffer_append_string_len(b
, ds
->value
->ptr
+(ptr
[4]-'0')-1, 1);
321 /* unhandled %-sequence */
323 } else if (NULL
!= (ds
= (data_string
*)array_get_element_klen(parsed_host
, CONST_BUF_LEN(path_pieces
[i
])))) {
324 buffer_append_string_buffer(b
, ds
->value
);
326 /* unhandled %-sequence */
329 buffer_append_string_buffer(b
, path_pieces
[i
]);
333 buffer_append_slash(b
);
336 static handler_t
mod_evhost_uri_handler(server
*srv
, connection
*con
, void *p_d
) {
337 plugin_data
*p
= p_d
;
338 stat_cache_entry
*sce
= NULL
;
340 /* not authority set */
341 if (buffer_string_is_empty(con
->uri
.authority
)) return HANDLER_GO_ON
;
343 mod_evhost_patch_connection(srv
, con
, p
);
345 /* missing even default(global) conf */
346 if (0 == p
->conf
.len
) {
347 return HANDLER_GO_ON
;
350 mod_evhost_build_doc_root_path(p
->tmp_buf
, srv
->split_vals
, con
->uri
.authority
, p
->conf
.path_pieces
, p
->conf
.len
);
352 if (HANDLER_ERROR
== stat_cache_get_entry(srv
, con
, p
->tmp_buf
, &sce
)) {
353 log_error_write(srv
, __FILE__
, __LINE__
, "sb", strerror(errno
), p
->tmp_buf
);
354 } else if(!S_ISDIR(sce
->st
.st_mode
)) {
355 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "not a directory:", p
->tmp_buf
);
357 buffer_copy_buffer(con
->physical
.doc_root
, p
->tmp_buf
);
360 return HANDLER_GO_ON
;
363 int mod_evhost_plugin_init(plugin
*p
);
364 int mod_evhost_plugin_init(plugin
*p
) {
365 p
->version
= LIGHTTPD_VERSION_ID
;
366 p
->name
= buffer_init_string("evhost");
367 p
->init
= mod_evhost_init
;
368 p
->set_defaults
= mod_evhost_set_defaults
;
369 p
->handle_docroot
= mod_evhost_uri_handler
;
370 p
->cleanup
= mod_evhost_free
;