18abd06a7SGlenn Strauss #include "first.h"
28abd06a7SGlenn Strauss
33fabcc36SGlenn Strauss #include "array.h"
4bcdc6a3bSJan Kneschke #include "buffer.h"
53fabcc36SGlenn Strauss #include "log.h"
63dd3cde9SGlenn Strauss #include "http_header.h"
7bcdc6a3bSJan Kneschke #include "plugin.h"
867c0b149SGlenn Strauss #include "request.h"
9bcdc6a3bSJan Kneschke
1022e8b456SStefan Bühler #include <stdlib.h>
1122e8b456SStefan Bühler #include <string.h>
1222e8b456SStefan Bühler
13bcdc6a3bSJan Kneschke typedef struct {
143fabcc36SGlenn Strauss const array *request_header;
153fabcc36SGlenn Strauss const array *set_request_header;
163fabcc36SGlenn Strauss const array *response_header;
173fabcc36SGlenn Strauss const array *set_response_header;
183fabcc36SGlenn Strauss const array *environment;
193fabcc36SGlenn Strauss const array *set_environment;
20bcdc6a3bSJan Kneschke } plugin_config;
21bcdc6a3bSJan Kneschke
22bcdc6a3bSJan Kneschke typedef struct {
23bcdc6a3bSJan Kneschke PLUGIN_DATA;
243fabcc36SGlenn Strauss plugin_config defaults;
25bcdc6a3bSJan Kneschke plugin_config conf;
26bcdc6a3bSJan Kneschke } plugin_data;
27bcdc6a3bSJan Kneschke
284d92366aSGlenn Strauss typedef struct {
294d92366aSGlenn Strauss int handled; /* make sure that we only apply the headers once */
304d92366aSGlenn Strauss plugin_config conf;
314d92366aSGlenn Strauss } handler_ctx;
324d92366aSGlenn Strauss
handler_ctx_init(void)330c6a5645SCyril Brulebois static handler_ctx * handler_ctx_init(void) {
34*5e14db43SGlenn Strauss return ck_calloc(1, sizeof(handler_ctx));
35487723d9SJan Kneschke }
36487723d9SJan Kneschke
handler_ctx_free(handler_ctx * hctx)37487723d9SJan Kneschke static void handler_ctx_free(handler_ctx *hctx) {
38487723d9SJan Kneschke free(hctx);
39487723d9SJan Kneschke }
40487723d9SJan Kneschke
INIT_FUNC(mod_setenv_init)41bcdc6a3bSJan Kneschke INIT_FUNC(mod_setenv_init) {
42*5e14db43SGlenn Strauss return ck_calloc(1, sizeof(plugin_data));
43bcdc6a3bSJan Kneschke }
44bcdc6a3bSJan Kneschke
mod_setenv_merge_config_cpv(plugin_config * const pconf,const config_plugin_value_t * const cpv)453fabcc36SGlenn Strauss static void mod_setenv_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
463fabcc36SGlenn Strauss switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
473fabcc36SGlenn Strauss case 0: /* setenv.add-request-header */
483fabcc36SGlenn Strauss pconf->request_header = cpv->v.a;
493fabcc36SGlenn Strauss break;
503fabcc36SGlenn Strauss case 1: /* setenv.add-response-header */
513fabcc36SGlenn Strauss pconf->response_header = cpv->v.a;
523fabcc36SGlenn Strauss break;
533fabcc36SGlenn Strauss case 2: /* setenv.add-environment */
543fabcc36SGlenn Strauss pconf->environment = cpv->v.a;
553fabcc36SGlenn Strauss break;
563fabcc36SGlenn Strauss case 3: /* setenv.set-request-header */
573fabcc36SGlenn Strauss pconf->set_request_header = cpv->v.a;
583fabcc36SGlenn Strauss break;
593fabcc36SGlenn Strauss case 4: /* setenv.set-response-header */
603fabcc36SGlenn Strauss pconf->set_response_header = cpv->v.a;
613fabcc36SGlenn Strauss break;
623fabcc36SGlenn Strauss case 5: /* setenv.set-environment */
633fabcc36SGlenn Strauss pconf->set_environment = cpv->v.a;
643fabcc36SGlenn Strauss break;
653fabcc36SGlenn Strauss default:/* should not happen */
663fabcc36SGlenn Strauss return;
673fabcc36SGlenn Strauss }
683fabcc36SGlenn Strauss }
693fabcc36SGlenn Strauss
mod_setenv_merge_config(plugin_config * const pconf,const config_plugin_value_t * cpv)703fabcc36SGlenn Strauss static void mod_setenv_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
713fabcc36SGlenn Strauss do {
723fabcc36SGlenn Strauss mod_setenv_merge_config_cpv(pconf, cpv);
733fabcc36SGlenn Strauss } while ((++cpv)->k_id != -1);
743fabcc36SGlenn Strauss }
753fabcc36SGlenn Strauss
mod_setenv_patch_config(request_st * const r,plugin_data * const p,plugin_config * const pconf)767c7f8c46SGlenn Strauss static void mod_setenv_patch_config(request_st * const r, plugin_data * const p, plugin_config * const pconf) {
773fabcc36SGlenn Strauss memcpy(pconf, &p->defaults, sizeof(plugin_config));
783fabcc36SGlenn Strauss for (int i = 1, used = p->nconfig; i < used; ++i) {
797c7f8c46SGlenn Strauss if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
803fabcc36SGlenn Strauss mod_setenv_merge_config(pconf, p->cvlist + p->cvlist[i].v.u2[0]);
813fabcc36SGlenn Strauss }
823fabcc36SGlenn Strauss }
83bcdc6a3bSJan Kneschke
mod_setenv_prep_ext(const array * const ac)842e0676fdSGlenn Strauss static void mod_setenv_prep_ext (const array * const ac) {
852e0676fdSGlenn Strauss array *a;
862e0676fdSGlenn Strauss *(const array **)&a = ac;
872e0676fdSGlenn Strauss for (uint32_t i = 0; i < a->used; ++i) {
882e0676fdSGlenn Strauss data_string * const ds = (data_string *)a->data[i];
89af3df29aSGlenn Strauss ds->ext = http_header_hkey_get(BUF_PTR_LEN(&ds->key));
90083f42b9SGlenn Strauss /*(convenience: assume list, remove line ends, if line ends after ',')*/
91083f42b9SGlenn Strauss for (char *s = ds->value.ptr; (s = strchr(s, ',')); ++s) {
92083f42b9SGlenn Strauss if (s[1] == '\r') *++s = ' ';
93083f42b9SGlenn Strauss if (s[1] == '\n') *++s = ' ';
94083f42b9SGlenn Strauss }
95083f42b9SGlenn Strauss /*(strip trailing and leading whitespace)*/
96083f42b9SGlenn Strauss const char *s = ds->value.ptr;
97083f42b9SGlenn Strauss uint32_t n = buffer_clen(&ds->value);
98083f42b9SGlenn Strauss while (n-- && (s[n]==' ' || s[n]=='\t' || s[n]=='\r' || s[n]=='\n')) ;
99083f42b9SGlenn Strauss buffer_truncate(&ds->value, ++n);
100083f42b9SGlenn Strauss s = ds->value.ptr;
101083f42b9SGlenn Strauss while (*s==' ' || *s=='\t' || *s=='\r' || *s=='\n') ++s;
102083f42b9SGlenn Strauss if (s != ds->value.ptr) {
103083f42b9SGlenn Strauss n -= (uint32_t)(s - ds->value.ptr);
104083f42b9SGlenn Strauss memmove(ds->value.ptr, s, n);
105083f42b9SGlenn Strauss buffer_truncate(&ds->value, n);
106083f42b9SGlenn Strauss }
107083f42b9SGlenn Strauss /*(warning if value contains '\r' or '\n';
108083f42b9SGlenn Strauss * invalid in HTTP/2 and in updated HTTP/1.1 specs)*/
109083f42b9SGlenn Strauss s = ds->value.ptr;
110083f42b9SGlenn Strauss if (NULL != strchr(s, '\r') || NULL != strchr(s, '\n')) {
111083f42b9SGlenn Strauss log_error(NULL, __FILE__, __LINE__,
112083f42b9SGlenn Strauss "WARNING: setenv.*-header contains CR and/or NL (invalid): "
113083f42b9SGlenn Strauss "%s: %s", ds->key.ptr, s);
114083f42b9SGlenn Strauss log_error(NULL, __FILE__, __LINE__,
115083f42b9SGlenn Strauss "Use mod_magnet for finer control of request, response headers.");
116083f42b9SGlenn Strauss }
1172e0676fdSGlenn Strauss }
1182e0676fdSGlenn Strauss }
1192e0676fdSGlenn Strauss
SETDEFAULTS_FUNC(mod_setenv_set_defaults)120bcdc6a3bSJan Kneschke SETDEFAULTS_FUNC(mod_setenv_set_defaults) {
1213fabcc36SGlenn Strauss static const config_plugin_keys_t cpk[] = {
1223fabcc36SGlenn Strauss { CONST_STR_LEN("setenv.add-request-header"),
12303b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
1243fabcc36SGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
1253fabcc36SGlenn Strauss ,{ CONST_STR_LEN("setenv.add-response-header"),
12603b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
1273fabcc36SGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
1283fabcc36SGlenn Strauss ,{ CONST_STR_LEN("setenv.add-environment"),
12903b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
1303fabcc36SGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
1313fabcc36SGlenn Strauss ,{ CONST_STR_LEN("setenv.set-request-header"),
13203b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
1333fabcc36SGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
1343fabcc36SGlenn Strauss ,{ CONST_STR_LEN("setenv.set-response-header"),
13503b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
1363fabcc36SGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
1373fabcc36SGlenn Strauss ,{ CONST_STR_LEN("setenv.set-environment"),
13803b4c993SGlenn Strauss T_CONFIG_ARRAY_KVSTRING,
1393fabcc36SGlenn Strauss T_CONFIG_SCOPE_CONNECTION }
1403fabcc36SGlenn Strauss ,{ NULL, 0,
1413fabcc36SGlenn Strauss T_CONFIG_UNSET,
1423fabcc36SGlenn Strauss T_CONFIG_SCOPE_UNSET }
143bcdc6a3bSJan Kneschke };
144bcdc6a3bSJan Kneschke
1453fabcc36SGlenn Strauss plugin_data * const p = p_d;
1463fabcc36SGlenn Strauss if (!config_plugin_values_init(srv, p, cpk, "mod_setenv"))
1473fabcc36SGlenn Strauss return HANDLER_ERROR;
148bcdc6a3bSJan Kneschke
1493fabcc36SGlenn Strauss /* process and validate config directives
1503fabcc36SGlenn Strauss * (init i to 0 if global context; to 1 to skip empty global context) */
1513fabcc36SGlenn Strauss for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
1523fabcc36SGlenn Strauss const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
1533fabcc36SGlenn Strauss for (; -1 != cpv->k_id; ++cpv) {
1543fabcc36SGlenn Strauss switch (cpv->k_id) {
1553fabcc36SGlenn Strauss case 0: /* setenv.add-request-header */
1563fabcc36SGlenn Strauss case 1: /* setenv.add-response-header */
1572e0676fdSGlenn Strauss mod_setenv_prep_ext(cpv->v.a);
1582e0676fdSGlenn Strauss break;
1593fabcc36SGlenn Strauss case 2: /* setenv.add-environment */
1602e0676fdSGlenn Strauss break;
1613fabcc36SGlenn Strauss case 3: /* setenv.set-request-header */
1623fabcc36SGlenn Strauss case 4: /* setenv.set-response-header */
1632e0676fdSGlenn Strauss mod_setenv_prep_ext(cpv->v.a);
1642e0676fdSGlenn Strauss break;
1653fabcc36SGlenn Strauss case 5: /* setenv.set-environment */
1663fabcc36SGlenn Strauss break;
1673fabcc36SGlenn Strauss default:/* should not happen */
1683fabcc36SGlenn Strauss break;
1693fabcc36SGlenn Strauss }
1703fabcc36SGlenn Strauss }
171bd77abe0SGlenn Strauss }
172bd77abe0SGlenn Strauss
1733fabcc36SGlenn Strauss /* initialize p->defaults from global config context */
1743fabcc36SGlenn Strauss if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
1753fabcc36SGlenn Strauss const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
1763fabcc36SGlenn Strauss if (-1 != cpv->k_id)
1773fabcc36SGlenn Strauss mod_setenv_merge_config(&p->defaults, cpv);
178bcdc6a3bSJan Kneschke }
179bcdc6a3bSJan Kneschke
180bcdc6a3bSJan Kneschke return HANDLER_GO_ON;
181bcdc6a3bSJan Kneschke }
182bcdc6a3bSJan Kneschke
URIHANDLER_FUNC(mod_setenv_uri_handler)183bcdc6a3bSJan Kneschke URIHANDLER_FUNC(mod_setenv_uri_handler) {
184bcdc6a3bSJan Kneschke plugin_data *p = p_d;
1857c7f8c46SGlenn Strauss handler_ctx *hctx = r->plugin_ctx[p->id];
1863fabcc36SGlenn Strauss if (!hctx)
1877c7f8c46SGlenn Strauss r->plugin_ctx[p->id] = hctx = handler_ctx_init();
1883fabcc36SGlenn Strauss else if (hctx->handled)
189487723d9SJan Kneschke return HANDLER_GO_ON;
190487723d9SJan Kneschke hctx->handled = 1;
191bcdc6a3bSJan Kneschke
1927c7f8c46SGlenn Strauss mod_setenv_patch_config(r, p, &hctx->conf);
193bcdc6a3bSJan Kneschke
1943fabcc36SGlenn Strauss const array * const aa = hctx->conf.request_header;
1953fabcc36SGlenn Strauss const array * const as = hctx->conf.set_request_header;
1963fabcc36SGlenn Strauss
1973fabcc36SGlenn Strauss if (aa) {
1983fabcc36SGlenn Strauss for (uint32_t k = 0; k < aa->used; ++k) {
1993fabcc36SGlenn Strauss const data_string * const ds = (const data_string *)aa->data[k];
200af3df29aSGlenn Strauss http_header_request_append(r, ds->ext, BUF_PTR_LEN(&ds->key),
201af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value));
2023fabcc36SGlenn Strauss }
203bcdc6a3bSJan Kneschke }
204bcdc6a3bSJan Kneschke
2053fabcc36SGlenn Strauss if (as) {
2063fabcc36SGlenn Strauss for (uint32_t k = 0; k < as->used; ++k) {
2073fabcc36SGlenn Strauss const data_string * const ds = (const data_string *)as->data[k];
208af3df29aSGlenn Strauss !buffer_is_blank(&ds->value)
209af3df29aSGlenn Strauss ? http_header_request_set(r, ds->ext, BUF_PTR_LEN(&ds->key),
210af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value))
211af3df29aSGlenn Strauss : http_header_request_unset(r, ds->ext, BUF_PTR_LEN(&ds->key));
2124d92366aSGlenn Strauss }
2133fabcc36SGlenn Strauss }
2144d92366aSGlenn Strauss
2154d92366aSGlenn Strauss return HANDLER_GO_ON;
2164d92366aSGlenn Strauss }
2174d92366aSGlenn Strauss
REQUEST_FUNC(mod_setenv_handle_request_env)2187c7f8c46SGlenn Strauss REQUEST_FUNC(mod_setenv_handle_request_env) {
2194d92366aSGlenn Strauss plugin_data *p = p_d;
2207c7f8c46SGlenn Strauss handler_ctx *hctx = r->plugin_ctx[p->id];
2214d92366aSGlenn Strauss if (NULL == hctx) return HANDLER_GO_ON;
2224d92366aSGlenn Strauss if (hctx->handled > 1) return HANDLER_GO_ON;
2234d92366aSGlenn Strauss hctx->handled = 2;
2244d92366aSGlenn Strauss
2253fabcc36SGlenn Strauss const array * const aa = hctx->conf.environment;
2263fabcc36SGlenn Strauss const array * const as = hctx->conf.set_environment;
2273fabcc36SGlenn Strauss
2283fabcc36SGlenn Strauss if (aa) {
2293fabcc36SGlenn Strauss for (uint32_t k = 0; k < hctx->conf.environment->used; ++k) {
2303fabcc36SGlenn Strauss const data_string * const ds = (const data_string *)aa->data[k];
231af3df29aSGlenn Strauss http_header_env_append(r, BUF_PTR_LEN(&ds->key),
232af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value));
2333fabcc36SGlenn Strauss }
234bcdc6a3bSJan Kneschke }
235bcdc6a3bSJan Kneschke
2363fabcc36SGlenn Strauss if (as) {
2373fabcc36SGlenn Strauss for (uint32_t k = 0; k < as->used; ++k) {
2383fabcc36SGlenn Strauss const data_string * const ds = (const data_string *)as->data[k];
239af3df29aSGlenn Strauss http_header_env_set(r, BUF_PTR_LEN(&ds->key),
240af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value));
2413fabcc36SGlenn Strauss }
2424d92366aSGlenn Strauss }
243bcdc6a3bSJan Kneschke
2444d92366aSGlenn Strauss return HANDLER_GO_ON;
2454d92366aSGlenn Strauss }
2464d92366aSGlenn Strauss
REQUEST_FUNC(mod_setenv_handle_response_start)2477c7f8c46SGlenn Strauss REQUEST_FUNC(mod_setenv_handle_response_start) {
2484d92366aSGlenn Strauss plugin_data *p = p_d;
2497c7f8c46SGlenn Strauss handler_ctx *hctx = r->plugin_ctx[p->id];
2504d92366aSGlenn Strauss if (NULL == hctx) return HANDLER_GO_ON;
2514d92366aSGlenn Strauss
2523fabcc36SGlenn Strauss const array * const aa = hctx->conf.response_header;
2533fabcc36SGlenn Strauss const array * const as = hctx->conf.set_response_header;
2543fabcc36SGlenn Strauss
2553fabcc36SGlenn Strauss if (aa) {
2563fabcc36SGlenn Strauss for (uint32_t k = 0; k < aa->used; ++k) {
2573fabcc36SGlenn Strauss const data_string * const ds = (const data_string *)aa->data[k];
258af3df29aSGlenn Strauss http_header_response_insert(r, ds->ext, BUF_PTR_LEN(&ds->key),
259af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value));
2603fabcc36SGlenn Strauss }
261bcdc6a3bSJan Kneschke }
262bcdc6a3bSJan Kneschke
2633fabcc36SGlenn Strauss if (as) {
2643fabcc36SGlenn Strauss for (uint32_t k = 0; k < as->used; ++k) {
2653fabcc36SGlenn Strauss const data_string * const ds = (const data_string *)as->data[k];
266af3df29aSGlenn Strauss !buffer_is_blank(&ds->value)
267af3df29aSGlenn Strauss ? http_header_response_set(r, ds->ext, BUF_PTR_LEN(&ds->key),
268af3df29aSGlenn Strauss BUF_PTR_LEN(&ds->value))
269af3df29aSGlenn Strauss : http_header_response_unset(r, ds->ext, BUF_PTR_LEN(&ds->key));
2704d92366aSGlenn Strauss }
2713fabcc36SGlenn Strauss }
2724d92366aSGlenn Strauss
273bcdc6a3bSJan Kneschke return HANDLER_GO_ON;
274bcdc6a3bSJan Kneschke }
275bcdc6a3bSJan Kneschke
REQUEST_FUNC(mod_setenv_handle_request_reset)27633c8cf41SGlenn Strauss REQUEST_FUNC(mod_setenv_handle_request_reset) {
2777c7f8c46SGlenn Strauss void ** const hctx = r->plugin_ctx+((plugin_data_base *)p_d)->id;
2783fabcc36SGlenn Strauss if (*hctx) { handler_ctx_free(*hctx); *hctx = NULL; }
279487723d9SJan Kneschke return HANDLER_GO_ON;
280487723d9SJan Kneschke }
281487723d9SJan Kneschke
282b82d7b8aSGlenn Strauss
283b82d7b8aSGlenn Strauss __attribute_cold__
28463f785a2SStefan Bühler int mod_setenv_plugin_init(plugin *p);
mod_setenv_plugin_init(plugin * p)285bcdc6a3bSJan Kneschke int mod_setenv_plugin_init(plugin *p) {
286bcdc6a3bSJan Kneschke p->version = LIGHTTPD_VERSION_ID;
287e2de4e58SGlenn Strauss p->name = "setenv";
288bcdc6a3bSJan Kneschke
289bcdc6a3bSJan Kneschke p->init = mod_setenv_init;
29033c8cf41SGlenn Strauss p->set_defaults= mod_setenv_set_defaults;
291bcdc6a3bSJan Kneschke p->handle_uri_clean = mod_setenv_uri_handler;
2924d92366aSGlenn Strauss p->handle_request_env = mod_setenv_handle_request_env;
2934d92366aSGlenn Strauss p->handle_response_start = mod_setenv_handle_response_start;
29433c8cf41SGlenn Strauss p->handle_request_reset = mod_setenv_handle_request_reset;
295bcdc6a3bSJan Kneschke
296487723d9SJan Kneschke
297bcdc6a3bSJan Kneschke return 0;
298bcdc6a3bSJan Kneschke }
299