1 #include "buffer.h"
2 #include "server.h"
3 #include "log.h"
4 #include "plugin.h"
5 #include "response.h"
6 
7 #include "stream.h"
8 
9 #include "mod_cml.h"
10 
11 #include <sys/stat.h>
12 #include <time.h>
13 
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 
20 /* init the plugin data */
INIT_FUNC(mod_cml_init)21 INIT_FUNC(mod_cml_init) {
22 	plugin_data *p;
23 
24 	p = calloc(1, sizeof(*p));
25 
26 	p->basedir         = buffer_init();
27 	p->baseurl         = buffer_init();
28 	p->trigger_handler = buffer_init();
29 
30 	return p;
31 }
32 
33 /* detroy the plugin data */
FREE_FUNC(mod_cml_free)34 FREE_FUNC(mod_cml_free) {
35 	plugin_data *p = p_d;
36 
37 	UNUSED(srv);
38 
39 	if (!p) return HANDLER_GO_ON;
40 
41 	if (p->config_storage) {
42 		size_t i;
43 		for (i = 0; i < srv->config_context->used; i++) {
44 			plugin_config *s = p->config_storage[i];
45 
46 			buffer_free(s->ext);
47 
48 			buffer_free(s->mc_namespace);
49 			buffer_free(s->power_magnet);
50 			array_free(s->mc_hosts);
51 
52 #if defined(HAVE_MEMCACHE_H)
53 			if (s->mc) mc_free(s->mc);
54 #endif
55 
56 			free(s);
57 		}
58 		free(p->config_storage);
59 	}
60 
61 	buffer_free(p->trigger_handler);
62 	buffer_free(p->basedir);
63 	buffer_free(p->baseurl);
64 
65 	free(p);
66 
67 	return HANDLER_GO_ON;
68 }
69 
70 /* handle plugin config and check values */
71 
SETDEFAULTS_FUNC(mod_cml_set_defaults)72 SETDEFAULTS_FUNC(mod_cml_set_defaults) {
73 	plugin_data *p = p_d;
74 	size_t i = 0;
75 
76 	config_values_t cv[] = {
77 		{ "cml.extension",              NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
78 		{ "cml.memcache-hosts",         NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },        /* 1 */
79 		{ "cml.memcache-namespace",     NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
80 		{ "cml.power-magnet",           NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
81 		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
82 	};
83 
84 	if (!p) return HANDLER_ERROR;
85 
86 	p->config_storage = malloc(srv->config_context->used * sizeof(specific_config *));
87 
88 	for (i = 0; i < srv->config_context->used; i++) {
89 		plugin_config *s;
90 
91 		s = malloc(sizeof(plugin_config));
92 		s->ext    = buffer_init();
93 		s->mc_hosts       = array_init();
94 		s->mc_namespace   = buffer_init();
95 		s->power_magnet   = buffer_init();
96 #if defined(HAVE_MEMCACHE_H)
97 		s->mc = NULL;
98 #endif
99 
100 		cv[0].destination = s->ext;
101 		cv[1].destination = s->mc_hosts;
102 		cv[2].destination = s->mc_namespace;
103 		cv[3].destination = s->power_magnet;
104 
105 		p->config_storage[i] = s;
106 
107 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
108 			return HANDLER_ERROR;
109 		}
110 
111 		if (s->mc_hosts->used) {
112 #if defined(HAVE_MEMCACHE_H)
113 			size_t k;
114 			s->mc = mc_new();
115 
116 			for (k = 0; k < s->mc_hosts->used; k++) {
117 				data_string *ds = (data_string *)s->mc_hosts->data[k];
118 
119 				if (0 != mc_server_add4(s->mc, ds->value->ptr)) {
120 					log_error_write(srv, __FILE__, __LINE__, "sb",
121 							"connection to host failed:",
122 							ds->value);
123 
124 					return HANDLER_ERROR;
125 				}
126 			}
127 #else
128 			log_error_write(srv, __FILE__, __LINE__, "s",
129 					"memcache support is not compiled in but cml.memcache-hosts is set, aborting");
130 			return HANDLER_ERROR;
131 #endif
132 		}
133 	}
134 
135 	return HANDLER_GO_ON;
136 }
137 
138 #define PATCH(x) \
139 	p->conf.x = s->x;
mod_cml_patch_connection(server * srv,connection * con,plugin_data * p)140 static int mod_cml_patch_connection(server *srv, connection *con, plugin_data *p) {
141 	size_t i, j;
142 	plugin_config *s = p->config_storage[0];
143 
144 	PATCH(ext);
145 #if defined(HAVE_MEMCACHE_H)
146 	PATCH(mc);
147 #endif
148 	PATCH(mc_namespace);
149 	PATCH(power_magnet);
150 
151 	/* skip the first, the global context */
152 	for (i = 1; i < srv->config_context->used; i++) {
153 		data_config *dc = (data_config *)srv->config_context->data[i];
154 		s = p->config_storage[i];
155 
156 		/* condition didn't match */
157 		if (!config_check_cond(srv, con, dc)) continue;
158 
159 		/* merge config */
160 		for (j = 0; j < dc->value->used; j++) {
161 			data_unset *du = dc->value->data[j];
162 
163 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.extension"))) {
164 				PATCH(ext);
165 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-hosts"))) {
166 #if defined(HAVE_MEMCACHE_H)
167 				PATCH(mc);
168 #endif
169 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.memcache-namespace"))) {
170 				PATCH(mc_namespace);
171 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cml.power-magnet"))) {
172 				PATCH(power_magnet);
173 			}
174 		}
175 	}
176 
177 	return 0;
178 }
179 #undef PATCH
180 
cache_call_lua(server * srv,connection * con,plugin_data * p,buffer * cml_file)181 static int cache_call_lua(server *srv, connection *con, plugin_data *p, buffer *cml_file) {
182 	buffer *b;
183 	char *c;
184 
185 	/* cleanup basedir */
186 	b = p->baseurl;
187 	buffer_copy_string_buffer(b, con->uri.path);
188 	for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--);
189 
190 	if (*c == '/') {
191 		b->used = c - b->ptr + 2;
192 		*(c+1) = '\0';
193 	}
194 
195 	b = p->basedir;
196 	buffer_copy_string_buffer(b, con->physical.path);
197 	for (c = b->ptr + b->used - 1; c > b->ptr && *c != '/'; c--);
198 
199 	if (*c == '/') {
200 		b->used = c - b->ptr + 2;
201 		*(c+1) = '\0';
202 	}
203 
204 
205 	/* prepare variables
206 	 *   - cookie-based
207 	 *   - get-param-based
208 	 */
209 	return cache_parse_lua(srv, con, p, cml_file);
210 }
211 
URIHANDLER_FUNC(mod_cml_power_magnet)212 URIHANDLER_FUNC(mod_cml_power_magnet) {
213 	plugin_data *p = p_d;
214 
215 	mod_cml_patch_connection(srv, con, p);
216 
217 	buffer_reset(p->basedir);
218 	buffer_reset(p->baseurl);
219 	buffer_reset(p->trigger_handler);
220 
221 	if (buffer_is_empty(p->conf.power_magnet)) return HANDLER_GO_ON;
222 
223 	/*
224 	 * power-magnet:
225 	 * cml.power-magnet = server.docroot + "/rewrite.cml"
226 	 *
227 	 * is called on EACH request, take the original REQUEST_URI and modifies the
228 	 * request header as neccesary.
229 	 *
230 	 * First use:
231 	 * if file_exists("/maintainance.html") {
232 	 *   output_include = ( "/maintainance.html" )
233 	 *   return CACHE_HIT
234 	 * }
235 	 *
236 	 * as we only want to rewrite HTML like requests we should cover it in a conditional
237 	 *
238 	 * */
239 
240 	switch(cache_call_lua(srv, con, p, p->conf.power_magnet)) {
241 	case -1:
242 		/* error */
243 		if (con->conf.log_request_handling) {
244 			log_error_write(srv, __FILE__, __LINE__, "s", "cache-error");
245 		}
246 		con->http_status = 500;
247 		return HANDLER_COMEBACK;
248 	case 0:
249 		if (con->conf.log_request_handling) {
250 			log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit");
251 		}
252 		/* cache-hit */
253 		buffer_reset(con->physical.path);
254 		return HANDLER_FINISHED;
255 	case 1:
256 		/* cache miss */
257 		return HANDLER_GO_ON;
258 	default:
259 		con->http_status = 500;
260 		return HANDLER_COMEBACK;
261 	}
262 }
263 
URIHANDLER_FUNC(mod_cml_is_handled)264 URIHANDLER_FUNC(mod_cml_is_handled) {
265 	plugin_data *p = p_d;
266 
267 	if (buffer_is_empty(con->physical.path)) return HANDLER_ERROR;
268 
269 	mod_cml_patch_connection(srv, con, p);
270 
271 	buffer_reset(p->basedir);
272 	buffer_reset(p->baseurl);
273 	buffer_reset(p->trigger_handler);
274 
275 	if (buffer_is_empty(p->conf.ext)) return HANDLER_GO_ON;
276 
277 	if (!buffer_is_equal_right_len(con->physical.path, p->conf.ext, p->conf.ext->used - 1)) {
278 		return HANDLER_GO_ON;
279 	}
280 
281 	switch(cache_call_lua(srv, con, p, con->physical.path)) {
282 	case -1:
283 		/* error */
284 		if (con->conf.log_request_handling) {
285 			log_error_write(srv, __FILE__, __LINE__, "s", "cache-error");
286 		}
287 		con->http_status = 500;
288 		return HANDLER_COMEBACK;
289 	case 0:
290 		if (con->conf.log_request_handling) {
291 			log_error_write(srv, __FILE__, __LINE__, "s", "cache-hit");
292 		}
293 		/* cache-hit */
294 		buffer_reset(con->physical.path);
295 		return HANDLER_FINISHED;
296 	case 1:
297 		if (con->conf.log_request_handling) {
298 			log_error_write(srv, __FILE__, __LINE__, "s", "cache-miss");
299 		}
300 		/* cache miss */
301 		return HANDLER_COMEBACK;
302 	default:
303 		con->http_status = 500;
304 		return HANDLER_COMEBACK;
305 	}
306 }
307 
308 int mod_cml_plugin_init(plugin *p);
mod_cml_plugin_init(plugin * p)309 int mod_cml_plugin_init(plugin *p) {
310 	p->version     = LIGHTTPD_VERSION_ID;
311 	p->name        = buffer_init_string("cache");
312 
313 	p->init        = mod_cml_init;
314 	p->cleanup     = mod_cml_free;
315 	p->set_defaults  = mod_cml_set_defaults;
316 
317 	p->handle_subrequest_start = mod_cml_is_handled;
318 	p->handle_physical         = mod_cml_power_magnet;
319 
320 	p->data        = NULL;
321 
322 	return 0;
323 }
324