1 #include "base.h"
2 #include "log.h"
3 #include "buffer.h"
4 
5 #include "plugin.h"
6 
7 #include "response.h"
8 
9 #include <stdlib.h>
10 #include <string.h>
11 
12 /* plugin config for all request/connections */
13 
14 typedef struct {
15 	int handled; /* make sure that we only apply the headers once */
16 } handler_ctx;
17 
18 typedef struct {
19 	array *request_header;
20 	array *response_header;
21 
22 	array *environment;
23 } plugin_config;
24 
25 typedef struct {
26 	PLUGIN_DATA;
27 
28 	plugin_config **config_storage;
29 
30 	plugin_config conf;
31 } plugin_data;
32 
handler_ctx_init(void)33 static handler_ctx * handler_ctx_init(void) {
34 	handler_ctx * hctx;
35 
36 	hctx = calloc(1, sizeof(*hctx));
37 
38 	hctx->handled = 0;
39 
40 	return hctx;
41 }
42 
handler_ctx_free(handler_ctx * hctx)43 static void handler_ctx_free(handler_ctx *hctx) {
44 	free(hctx);
45 }
46 
47 
48 /* init the plugin data */
INIT_FUNC(mod_setenv_init)49 INIT_FUNC(mod_setenv_init) {
50 	plugin_data *p;
51 
52 	p = calloc(1, sizeof(*p));
53 
54 	return p;
55 }
56 
57 /* detroy the plugin data */
FREE_FUNC(mod_setenv_free)58 FREE_FUNC(mod_setenv_free) {
59 	plugin_data *p = p_d;
60 
61 	UNUSED(srv);
62 
63 	if (!p) return HANDLER_GO_ON;
64 
65 	if (p->config_storage) {
66 		size_t i;
67 		for (i = 0; i < srv->config_context->used; i++) {
68 			plugin_config *s = p->config_storage[i];
69 
70 			array_free(s->request_header);
71 			array_free(s->response_header);
72 			array_free(s->environment);
73 
74 			free(s);
75 		}
76 		free(p->config_storage);
77 	}
78 
79 	free(p);
80 
81 	return HANDLER_GO_ON;
82 }
83 
84 /* handle plugin config and check values */
85 
SETDEFAULTS_FUNC(mod_setenv_set_defaults)86 SETDEFAULTS_FUNC(mod_setenv_set_defaults) {
87 	plugin_data *p = p_d;
88 	size_t i = 0;
89 
90 	config_values_t cv[] = {
91 		{ "setenv.add-request-header",  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
92 		{ "setenv.add-response-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
93 		{ "setenv.add-environment",     NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
94 		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
95 	};
96 
97 	if (!p) return HANDLER_ERROR;
98 
99 	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
100 
101 	for (i = 0; i < srv->config_context->used; i++) {
102 		plugin_config *s;
103 
104 		s = calloc(1, sizeof(plugin_config));
105 		s->request_header   = array_init();
106 		s->response_header  = array_init();
107 		s->environment      = array_init();
108 
109 		cv[0].destination = s->request_header;
110 		cv[1].destination = s->response_header;
111 		cv[2].destination = s->environment;
112 
113 		p->config_storage[i] = s;
114 
115 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
116 			return HANDLER_ERROR;
117 		}
118 	}
119 
120 	return HANDLER_GO_ON;
121 }
122 
123 #define PATCH(x) \
124 	p->conf.x = s->x;
mod_setenv_patch_connection(server * srv,connection * con,plugin_data * p)125 static int mod_setenv_patch_connection(server *srv, connection *con, plugin_data *p) {
126 	size_t i, j;
127 	plugin_config *s = p->config_storage[0];
128 
129 	PATCH(request_header);
130 	PATCH(response_header);
131 	PATCH(environment);
132 
133 	/* skip the first, the global context */
134 	for (i = 1; i < srv->config_context->used; i++) {
135 		data_config *dc = (data_config *)srv->config_context->data[i];
136 		s = p->config_storage[i];
137 
138 		/* condition didn't match */
139 		if (!config_check_cond(srv, con, dc)) continue;
140 
141 		/* merge config */
142 		for (j = 0; j < dc->value->used; j++) {
143 			data_unset *du = dc->value->data[j];
144 
145 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-request-header"))) {
146 				PATCH(request_header);
147 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-response-header"))) {
148 				PATCH(response_header);
149 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-environment"))) {
150 				PATCH(environment);
151 			}
152 		}
153 	}
154 
155 	return 0;
156 }
157 #undef PATCH
158 
URIHANDLER_FUNC(mod_setenv_uri_handler)159 URIHANDLER_FUNC(mod_setenv_uri_handler) {
160 	plugin_data *p = p_d;
161 	size_t k;
162 	handler_ctx *hctx;
163 
164 	if (con->plugin_ctx[p->id]) {
165 		hctx = con->plugin_ctx[p->id];
166 	} else {
167 		hctx = handler_ctx_init();
168 
169 		con->plugin_ctx[p->id] = hctx;
170 	}
171 
172 	if (hctx->handled) {
173 		return HANDLER_GO_ON;
174 	}
175 
176 	hctx->handled = 1;
177 
178 	mod_setenv_patch_connection(srv, con, p);
179 
180 	for (k = 0; k < p->conf.request_header->used; k++) {
181 		data_string *ds = (data_string *)p->conf.request_header->data[k];
182 		data_string *ds_dst;
183 
184 		if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
185 			ds_dst = data_string_init();
186 		}
187 
188 		buffer_copy_string_buffer(ds_dst->key, ds->key);
189 		buffer_copy_string_buffer(ds_dst->value, ds->value);
190 
191 		array_insert_unique(con->request.headers, (data_unset *)ds_dst);
192 	}
193 
194 	for (k = 0; k < p->conf.environment->used; k++) {
195 		data_string *ds = (data_string *)p->conf.environment->data[k];
196 		data_string *ds_dst;
197 
198 		if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
199 			ds_dst = data_string_init();
200 		}
201 
202 		buffer_copy_string_buffer(ds_dst->key, ds->key);
203 		buffer_copy_string_buffer(ds_dst->value, ds->value);
204 
205 		array_insert_unique(con->environment, (data_unset *)ds_dst);
206 	}
207 
208 	for (k = 0; k < p->conf.response_header->used; k++) {
209 		data_string *ds = (data_string *)p->conf.response_header->data[k];
210 
211 		response_header_insert(srv, con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
212 	}
213 
214 	/* not found */
215 	return HANDLER_GO_ON;
216 }
217 
CONNECTION_FUNC(mod_setenv_reset)218 CONNECTION_FUNC(mod_setenv_reset) {
219 	plugin_data *p = p_d;
220 
221 	UNUSED(srv);
222 
223 	if (con->plugin_ctx[p->id]) {
224 		handler_ctx_free(con->plugin_ctx[p->id]);
225 		con->plugin_ctx[p->id] = NULL;
226 	}
227 
228 	return HANDLER_GO_ON;
229 }
230 
231 /* this function is called at dlopen() time and inits the callbacks */
232 
233 int mod_setenv_plugin_init(plugin *p);
mod_setenv_plugin_init(plugin * p)234 int mod_setenv_plugin_init(plugin *p) {
235 	p->version     = LIGHTTPD_VERSION_ID;
236 	p->name        = buffer_init_string("setenv");
237 
238 	p->init        = mod_setenv_init;
239 	p->handle_uri_clean  = mod_setenv_uri_handler;
240 	p->set_defaults  = mod_setenv_set_defaults;
241 	p->cleanup     = mod_setenv_free;
242 
243 	p->connection_reset  = mod_setenv_reset;
244 
245 	p->data        = NULL;
246 
247 	return 0;
248 }
249