xref: /lighttpd1.4/src/mod_setenv.c (revision e2de4e58)
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 		if (!config_check_cond(con, i)) continue; /* condition not matched */
170 
171 		data_config *dc = (data_config *)srv->config_context->data[i];
172 		s = p->config_storage[i];
173 
174 		/* merge config */
175 		for (j = 0; j < dc->value->used; j++) {
176 			data_unset *du = dc->value->data[j];
177 
178 			if (buffer_is_equal_string(&du->key, CONST_STR_LEN("setenv.add-request-header"))) {
179 				PATCH(request_header);
180 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("setenv.set-request-header"))) {
181 				PATCH(set_request_header);
182 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("setenv.add-response-header"))) {
183 				PATCH(response_header);
184 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("setenv.set-response-header"))) {
185 				PATCH(set_response_header);
186 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("setenv.add-environment"))) {
187 				PATCH(environment);
188 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("setenv.set-environment"))) {
189 				PATCH(set_environment);
190 			}
191 		}
192 	}
193 
194 	return 0;
195 }
196 #undef PATCH
197 
198 URIHANDLER_FUNC(mod_setenv_uri_handler) {
199 	plugin_data *p = p_d;
200 	size_t k;
201 	handler_ctx *hctx;
202 
203 	if (con->plugin_ctx[p->id]) {
204 		hctx = con->plugin_ctx[p->id];
205 	} else {
206 		hctx = handler_ctx_init();
207 
208 		con->plugin_ctx[p->id] = hctx;
209 	}
210 
211 	if (hctx->handled) {
212 		return HANDLER_GO_ON;
213 	}
214 
215 	hctx->handled = 1;
216 
217 	mod_setenv_patch_connection(srv, con, p);
218 	memcpy(&hctx->conf, &p->conf, sizeof(plugin_config));
219 
220 	for (k = 0; k < p->conf.request_header->used; k++) {
221 		data_string *ds = (data_string *)p->conf.request_header->data[k];
222 		enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(&ds->key));
223 		http_header_request_append(con, id, CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value));
224 	}
225 
226 	for (k = 0; k < hctx->conf.set_request_header->used; ++k) {
227 		data_string *ds = (data_string *)hctx->conf.set_request_header->data[k];
228 		enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(&ds->key));
229 		!buffer_string_is_empty(&ds->value)
230 		  ? http_header_request_set(con, id, CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value))
231 		  : http_header_request_unset(con, id, CONST_BUF_LEN(&ds->key));
232 	}
233 
234 	return HANDLER_GO_ON;
235 }
236 
237 CONNECTION_FUNC(mod_setenv_handle_request_env) {
238 	plugin_data *p = p_d;
239 	handler_ctx *hctx = con->plugin_ctx[p->id];
240 	if (NULL == hctx) return HANDLER_GO_ON;
241 	if (hctx->handled > 1) return HANDLER_GO_ON;
242 	hctx->handled = 2;
243 	UNUSED(srv);
244 
245 	for (size_t k = 0; k < hctx->conf.environment->used; ++k) {
246 		data_string *ds = (data_string *)hctx->conf.environment->data[k];
247 		http_header_env_append(con, CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value));
248 	}
249 
250 	for (size_t k = 0; k < hctx->conf.set_environment->used; ++k) {
251 		data_string *ds = (data_string *)hctx->conf.set_environment->data[k];
252 		http_header_env_set(con, CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value));
253 	}
254 
255 	return HANDLER_GO_ON;
256 }
257 
258 CONNECTION_FUNC(mod_setenv_handle_response_start) {
259 	plugin_data *p = p_d;
260 	handler_ctx *hctx = con->plugin_ctx[p->id];
261 	if (NULL == hctx) return HANDLER_GO_ON;
262 	UNUSED(srv);
263 
264 	for (size_t k = 0; k < hctx->conf.response_header->used; ++k) {
265 		data_string *ds = (data_string *)hctx->conf.response_header->data[k];
266 		enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(&ds->key));
267 		http_header_response_insert(con, id, CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value));
268 	}
269 
270 	for (size_t k = 0; k < hctx->conf.set_response_header->used; ++k) {
271 		data_string *ds = (data_string *)hctx->conf.set_response_header->data[k];
272 		enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(&ds->key));
273 		!buffer_string_is_empty(&ds->value)
274 		  ? http_header_response_set(con, id, CONST_BUF_LEN(&ds->key), CONST_BUF_LEN(&ds->value))
275 		  : http_header_response_unset(con, id, CONST_BUF_LEN(&ds->key));
276 	}
277 
278 	return HANDLER_GO_ON;
279 }
280 
281 CONNECTION_FUNC(mod_setenv_reset) {
282 	plugin_data *p = p_d;
283 
284 	UNUSED(srv);
285 
286 	if (con->plugin_ctx[p->id]) {
287 		handler_ctx_free(con->plugin_ctx[p->id]);
288 		con->plugin_ctx[p->id] = NULL;
289 	}
290 
291 	return HANDLER_GO_ON;
292 }
293 
294 /* this function is called at dlopen() time and inits the callbacks */
295 
296 int mod_setenv_plugin_init(plugin *p);
297 int mod_setenv_plugin_init(plugin *p) {
298 	p->version     = LIGHTTPD_VERSION_ID;
299 	p->name        = "setenv";
300 
301 	p->init        = mod_setenv_init;
302 	p->handle_uri_clean  = mod_setenv_uri_handler;
303 	p->handle_request_env    = mod_setenv_handle_request_env;
304 	p->handle_response_start = mod_setenv_handle_response_start;
305 	p->set_defaults  = mod_setenv_set_defaults;
306 	p->cleanup     = mod_setenv_free;
307 
308 	p->connection_reset  = mod_setenv_reset;
309 
310 	return 0;
311 }
312