xref: /lighttpd1.4/src/mod_setenv.c (revision 5e14db43)
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