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