xref: /lighttpd1.4/src/mod_setenv.c (revision bd77abe0)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 
7 #include "plugin.h"
8 
9 #include "response.h"
10 
11 #include <stdlib.h>
12 #include <string.h>
13 
14 /* plugin config for all request/connections */
15 
16 typedef struct {
17 	array *request_header;
18 	array *set_request_header;
19 	array *response_header;
20 	array *set_response_header;
21 	array *environment;
22 	array *set_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 
33 typedef struct {
34 	int handled; /* make sure that we only apply the headers once */
35 	plugin_config conf;
36 } handler_ctx;
37 
38 static handler_ctx * handler_ctx_init(void) {
39 	handler_ctx * hctx;
40 
41 	hctx = calloc(1, sizeof(*hctx));
42 
43 	hctx->handled = 0;
44 
45 	return hctx;
46 }
47 
48 static void handler_ctx_free(handler_ctx *hctx) {
49 	free(hctx);
50 }
51 
52 
53 /* init the plugin data */
54 INIT_FUNC(mod_setenv_init) {
55 	plugin_data *p;
56 
57 	p = calloc(1, sizeof(*p));
58 
59 	return p;
60 }
61 
62 /* detroy the plugin data */
63 FREE_FUNC(mod_setenv_free) {
64 	plugin_data *p = p_d;
65 
66 	UNUSED(srv);
67 
68 	if (!p) return HANDLER_GO_ON;
69 
70 	if (p->config_storage) {
71 		size_t i;
72 		for (i = 0; i < srv->config_context->used; i++) {
73 			plugin_config *s = p->config_storage[i];
74 
75 			if (NULL == s) continue;
76 
77 			array_free(s->request_header);
78 			array_free(s->response_header);
79 			array_free(s->environment);
80 			array_free(s->set_request_header);
81 			array_free(s->set_response_header);
82 			array_free(s->set_environment);
83 
84 			free(s);
85 		}
86 		free(p->config_storage);
87 	}
88 
89 	free(p);
90 
91 	return HANDLER_GO_ON;
92 }
93 
94 /* handle plugin config and check values */
95 
96 SETDEFAULTS_FUNC(mod_setenv_set_defaults) {
97 	plugin_data *p = p_d;
98 	size_t i = 0;
99 
100 	config_values_t cv[] = {
101 		{ "setenv.add-request-header",  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
102 		{ "setenv.add-response-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
103 		{ "setenv.add-environment",     NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
104 		{ "setenv.set-request-header",  NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
105 		{ "setenv.set-response-header", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 4 */
106 		{ "setenv.set-environment",     NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 5 */
107 		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
108 	};
109 
110 	if (!p) return HANDLER_ERROR;
111 
112 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
113 
114 	for (i = 0; i < srv->config_context->used; i++) {
115 		data_config const* config = (data_config const*)srv->config_context->data[i];
116 		plugin_config *s;
117 
118 		s = calloc(1, sizeof(plugin_config));
119 		s->request_header   = array_init();
120 		s->response_header  = array_init();
121 		s->environment      = array_init();
122 		s->set_request_header  = array_init();
123 		s->set_response_header = array_init();
124 		s->set_environment     = array_init();
125 
126 		cv[0].destination = s->request_header;
127 		cv[1].destination = s->response_header;
128 		cv[2].destination = s->environment;
129 		cv[3].destination = s->set_request_header;
130 		cv[4].destination = s->set_response_header;
131 		cv[5].destination = s->set_environment;
132 
133 		p->config_storage[i] = s;
134 
135 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
136 			return HANDLER_ERROR;
137 		}
138 
139 		if (   !array_is_kvstring(s->request_header)
140 		    || !array_is_kvstring(s->response_header)
141 		    || !array_is_kvstring(s->environment)
142 		    || !array_is_kvstring(s->set_request_header)
143 		    || !array_is_kvstring(s->set_response_header)
144 		    || !array_is_kvstring(s->set_environment)) {
145 			log_error_write(srv, __FILE__, __LINE__, "s",
146 					"unexpected value for setenv.xxxxxx; expected list of \"envvar\" => \"value\"");
147 			return HANDLER_ERROR;
148 		}
149 
150 	}
151 
152 	return HANDLER_GO_ON;
153 }
154 
155 #define PATCH(x) \
156 	p->conf.x = s->x;
157 static int mod_setenv_patch_connection(server *srv, connection *con, plugin_data *p) {
158 	size_t i, j;
159 	plugin_config *s = p->config_storage[0];
160 
161 	PATCH(request_header);
162 	PATCH(set_request_header);
163 	PATCH(response_header);
164 	PATCH(set_response_header);
165 	PATCH(environment);
166 	PATCH(set_environment);
167 
168 	/* skip the first, the global context */
169 	for (i = 1; i < srv->config_context->used; i++) {
170 		data_config *dc = (data_config *)srv->config_context->data[i];
171 		s = p->config_storage[i];
172 
173 		/* condition didn't match */
174 		if (!config_check_cond(srv, con, dc)) continue;
175 
176 		/* merge config */
177 		for (j = 0; j < dc->value->used; j++) {
178 			data_unset *du = dc->value->data[j];
179 
180 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-request-header"))) {
181 				PATCH(request_header);
182 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.set-request-header"))) {
183 				PATCH(set_request_header);
184 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-response-header"))) {
185 				PATCH(response_header);
186 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.set-response-header"))) {
187 				PATCH(set_response_header);
188 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.add-environment"))) {
189 				PATCH(environment);
190 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("setenv.set-environment"))) {
191 				PATCH(set_environment);
192 			}
193 		}
194 	}
195 
196 	return 0;
197 }
198 #undef PATCH
199 
200 URIHANDLER_FUNC(mod_setenv_uri_handler) {
201 	plugin_data *p = p_d;
202 	size_t k;
203 	handler_ctx *hctx;
204 
205 	if (con->plugin_ctx[p->id]) {
206 		hctx = con->plugin_ctx[p->id];
207 	} else {
208 		hctx = handler_ctx_init();
209 
210 		con->plugin_ctx[p->id] = hctx;
211 	}
212 
213 	if (hctx->handled) {
214 		return HANDLER_GO_ON;
215 	}
216 
217 	hctx->handled = 1;
218 
219 	mod_setenv_patch_connection(srv, con, p);
220 	memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
221 
222 	for (k = 0; k < p->conf.request_header->used; k++) {
223 		data_string *ds = (data_string *)p->conf.request_header->data[k];
224 		data_string *ds_dst;
225 
226 		if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
227 			ds_dst = data_string_init();
228 		}
229 
230 		buffer_copy_buffer(ds_dst->key, ds->key);
231 		buffer_copy_buffer(ds_dst->value, ds->value);
232 
233 		array_insert_unique(con->request.headers, (data_unset *)ds_dst);
234 	}
235 
236 	for (k = 0; k < hctx->conf.set_request_header->used; ++k) {
237 		data_string *ds = (data_string *)hctx->conf.set_request_header->data[k];
238 		array_set_key_value(con->request.headers, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
239 	}
240 
241 	return HANDLER_GO_ON;
242 }
243 
244 CONNECTION_FUNC(mod_setenv_handle_request_env) {
245 	plugin_data *p = p_d;
246 	handler_ctx *hctx = con->plugin_ctx[p->id];
247 	if (NULL == hctx) return HANDLER_GO_ON;
248 	if (hctx->handled > 1) return HANDLER_GO_ON;
249 	hctx->handled = 2;
250 	UNUSED(srv);
251 
252 	for (size_t k = 0; k < hctx->conf.environment->used; ++k) {
253 		data_string *ds = (data_string *)hctx->conf.environment->data[k];
254 		data_string *ds_dst;
255 
256 		if (NULL == (ds_dst = (data_string *)array_get_unused_element(con->environment, TYPE_STRING))) {
257 			ds_dst = data_string_init();
258 		}
259 
260 		buffer_copy_buffer(ds_dst->key, ds->key);
261 		buffer_copy_buffer(ds_dst->value, ds->value);
262 
263 		array_insert_unique(con->environment, (data_unset *)ds_dst);
264 	}
265 
266 	for (size_t k = 0; k < hctx->conf.set_environment->used; ++k) {
267 		data_string *ds = (data_string *)hctx->conf.set_environment->data[k];
268 		array_set_key_value(con->environment, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
269 	}
270 
271 	return HANDLER_GO_ON;
272 }
273 
274 CONNECTION_FUNC(mod_setenv_handle_response_start) {
275 	plugin_data *p = p_d;
276 	handler_ctx *hctx = con->plugin_ctx[p->id];
277 	if (NULL == hctx) return HANDLER_GO_ON;
278 
279 	for (size_t k = 0; k < hctx->conf.response_header->used; ++k) {
280 		data_string *ds = (data_string *)hctx->conf.response_header->data[k];
281 		response_header_insert(srv, con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
282 	}
283 
284 	for (size_t k = 0; k < hctx->conf.set_response_header->used; ++k) {
285 		data_string *ds = (data_string *)hctx->conf.set_response_header->data[k];
286 		response_header_overwrite(srv, con, CONST_BUF_LEN(ds->key), CONST_BUF_LEN(ds->value));
287 	}
288 
289 	return HANDLER_GO_ON;
290 }
291 
292 CONNECTION_FUNC(mod_setenv_reset) {
293 	plugin_data *p = p_d;
294 
295 	UNUSED(srv);
296 
297 	if (con->plugin_ctx[p->id]) {
298 		handler_ctx_free(con->plugin_ctx[p->id]);
299 		con->plugin_ctx[p->id] = NULL;
300 	}
301 
302 	return HANDLER_GO_ON;
303 }
304 
305 /* this function is called at dlopen() time and inits the callbacks */
306 
307 int mod_setenv_plugin_init(plugin *p);
308 int mod_setenv_plugin_init(plugin *p) {
309 	p->version     = LIGHTTPD_VERSION_ID;
310 	p->name        = buffer_init_string("setenv");
311 
312 	p->init        = mod_setenv_init;
313 	p->handle_uri_clean  = mod_setenv_uri_handler;
314 	p->handle_request_env    = mod_setenv_handle_request_env;
315 	p->handle_response_start = mod_setenv_handle_response_start;
316 	p->set_defaults  = mod_setenv_set_defaults;
317 	p->cleanup     = mod_setenv_free;
318 
319 	p->connection_reset  = mod_setenv_reset;
320 
321 	p->data        = NULL;
322 
323 	return 0;
324 }
325