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