xref: /lighttpd1.4/src/mod_setenv.c (revision 66698469)
1 #include "first.h"
2 
3 #include "array.h"
4 #include "buffer.h"
5 #include "log.h"
6 #include "http_header.h"
7 #include "plugin.h"
8 #include "request.h"
9 
10 #include <stdlib.h>
11 #include <string.h>
12 
13 typedef struct {
14     const array *request_header;
15     const array *set_request_header;
16     const array *response_header;
17     const array *set_response_header;
18     const array *environment;
19     const array *set_environment;
20 } plugin_config;
21 
22 typedef struct {
23     PLUGIN_DATA;
24     plugin_config defaults;
25     plugin_config conf;
26 } plugin_data;
27 
28 typedef struct {
29     int handled; /* make sure that we only apply the headers once */
30     plugin_config conf;
31 } handler_ctx;
32 
33 static handler_ctx * handler_ctx_init(void) {
34     handler_ctx * const hctx = calloc(1, sizeof(handler_ctx));
35     force_assert(hctx);
36     return hctx;
37 }
38 
39 static void handler_ctx_free(handler_ctx *hctx) {
40     free(hctx);
41 }
42 
43 INIT_FUNC(mod_setenv_init) {
44     return calloc(1, sizeof(plugin_data));
45 }
46 
47 static void mod_setenv_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
48     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
49       case 0: /* setenv.add-request-header */
50         pconf->request_header = cpv->v.a;
51         break;
52       case 1: /* setenv.add-response-header */
53         pconf->response_header = cpv->v.a;
54         break;
55       case 2: /* setenv.add-environment */
56         pconf->environment = cpv->v.a;
57         break;
58       case 3: /* setenv.set-request-header */
59         pconf->set_request_header = cpv->v.a;
60         break;
61       case 4: /* setenv.set-response-header */
62         pconf->set_response_header = cpv->v.a;
63         break;
64       case 5: /* setenv.set-environment */
65         pconf->set_environment = cpv->v.a;
66         break;
67       default:/* should not happen */
68         return;
69     }
70 }
71 
72 static void mod_setenv_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
73     do {
74         mod_setenv_merge_config_cpv(pconf, cpv);
75     } while ((++cpv)->k_id != -1);
76 }
77 
78 static void mod_setenv_patch_config(request_st * const r, plugin_data * const p, plugin_config * const pconf) {
79     memcpy(pconf, &p->defaults, sizeof(plugin_config));
80     for (int i = 1, used = p->nconfig; i < used; ++i) {
81         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
82             mod_setenv_merge_config(pconf, p->cvlist + p->cvlist[i].v.u2[0]);
83     }
84 }
85 
86 static void mod_setenv_prep_ext (const array * const ac) {
87     array *a;
88     *(const array **)&a = ac;
89     for (uint32_t i = 0; i < a->used; ++i) {
90         data_string * const ds = (data_string *)a->data[i];
91         ds->ext = http_header_hkey_get(BUF_PTR_LEN(&ds->key));
92         /*(convenience: assume list, remove line ends, if line ends after ',')*/
93         for (char *s = ds->value.ptr; (s = strchr(s, ',')); ++s) {
94             if (s[1] == '\r') *++s = ' ';
95             if (s[1] == '\n') *++s = ' ';
96         }
97         /*(strip trailing and leading whitespace)*/
98         const char *s = ds->value.ptr;
99         uint32_t n = buffer_clen(&ds->value);
100         while (n-- && (s[n]==' ' || s[n]=='\t' || s[n]=='\r' || s[n]=='\n')) ;
101         buffer_truncate(&ds->value, ++n);
102         s = ds->value.ptr;
103         while (*s==' ' || *s=='\t' || *s=='\r' || *s=='\n') ++s;
104         if (s != ds->value.ptr) {
105             n -= (uint32_t)(s - ds->value.ptr);
106             memmove(ds->value.ptr, s, n);
107             buffer_truncate(&ds->value, n);
108         }
109         /*(warning if value contains '\r' or '\n';
110          * invalid in HTTP/2 and in updated HTTP/1.1 specs)*/
111         s = ds->value.ptr;
112         if (NULL != strchr(s, '\r') || NULL != strchr(s, '\n')) {
113             log_error(NULL, __FILE__, __LINE__,
114                "WARNING: setenv.*-header contains CR and/or NL (invalid): "
115                "%s: %s", ds->key.ptr, s);
116             log_error(NULL, __FILE__, __LINE__,
117               "Use mod_magnet for finer control of request, response headers.");
118         }
119     }
120 }
121 
122 SETDEFAULTS_FUNC(mod_setenv_set_defaults) {
123     static const config_plugin_keys_t cpk[] = {
124       { CONST_STR_LEN("setenv.add-request-header"),
125         T_CONFIG_ARRAY_KVSTRING,
126         T_CONFIG_SCOPE_CONNECTION }
127      ,{ CONST_STR_LEN("setenv.add-response-header"),
128         T_CONFIG_ARRAY_KVSTRING,
129         T_CONFIG_SCOPE_CONNECTION }
130      ,{ CONST_STR_LEN("setenv.add-environment"),
131         T_CONFIG_ARRAY_KVSTRING,
132         T_CONFIG_SCOPE_CONNECTION }
133      ,{ CONST_STR_LEN("setenv.set-request-header"),
134         T_CONFIG_ARRAY_KVSTRING,
135         T_CONFIG_SCOPE_CONNECTION }
136      ,{ CONST_STR_LEN("setenv.set-response-header"),
137         T_CONFIG_ARRAY_KVSTRING,
138         T_CONFIG_SCOPE_CONNECTION }
139      ,{ CONST_STR_LEN("setenv.set-environment"),
140         T_CONFIG_ARRAY_KVSTRING,
141         T_CONFIG_SCOPE_CONNECTION }
142      ,{ NULL, 0,
143         T_CONFIG_UNSET,
144         T_CONFIG_SCOPE_UNSET }
145     };
146 
147     plugin_data * const p = p_d;
148     if (!config_plugin_values_init(srv, p, cpk, "mod_setenv"))
149         return HANDLER_ERROR;
150 
151     /* process and validate config directives
152      * (init i to 0 if global context; to 1 to skip empty global context) */
153     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
154         const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
155         for (; -1 != cpv->k_id; ++cpv) {
156             switch (cpv->k_id) {
157               case 0: /* setenv.add-request-header */
158               case 1: /* setenv.add-response-header */
159                 mod_setenv_prep_ext(cpv->v.a);
160                 break;
161               case 2: /* setenv.add-environment */
162                 break;
163               case 3: /* setenv.set-request-header */
164               case 4: /* setenv.set-response-header */
165                 mod_setenv_prep_ext(cpv->v.a);
166                 break;
167               case 5: /* setenv.set-environment */
168                 break;
169               default:/* should not happen */
170                 break;
171             }
172         }
173     }
174 
175     /* initialize p->defaults from global config context */
176     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
177         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
178         if (-1 != cpv->k_id)
179             mod_setenv_merge_config(&p->defaults, cpv);
180     }
181 
182     return HANDLER_GO_ON;
183 }
184 
185 URIHANDLER_FUNC(mod_setenv_uri_handler) {
186     plugin_data *p = p_d;
187     handler_ctx *hctx = r->plugin_ctx[p->id];
188     if (!hctx)
189         r->plugin_ctx[p->id] = hctx = handler_ctx_init();
190     else if (hctx->handled)
191         return HANDLER_GO_ON;
192     hctx->handled = 1;
193 
194     mod_setenv_patch_config(r, p, &hctx->conf);
195 
196     const array * const aa = hctx->conf.request_header;
197     const array * const as = hctx->conf.set_request_header;
198 
199     if (aa) {
200         for (uint32_t k = 0; k < aa->used; ++k) {
201             const data_string * const ds = (const data_string *)aa->data[k];
202             http_header_request_append(r, ds->ext, BUF_PTR_LEN(&ds->key),
203                                                    BUF_PTR_LEN(&ds->value));
204         }
205     }
206 
207     if (as) {
208         for (uint32_t k = 0; k < as->used; ++k) {
209             const data_string * const ds = (const data_string *)as->data[k];
210             !buffer_is_blank(&ds->value)
211               ? http_header_request_set(r, ds->ext, BUF_PTR_LEN(&ds->key),
212                                                     BUF_PTR_LEN(&ds->value))
213               : http_header_request_unset(r, ds->ext, BUF_PTR_LEN(&ds->key));
214         }
215     }
216 
217     return HANDLER_GO_ON;
218 }
219 
220 REQUEST_FUNC(mod_setenv_handle_request_env) {
221     plugin_data *p = p_d;
222     handler_ctx *hctx = r->plugin_ctx[p->id];
223     if (NULL == hctx) return HANDLER_GO_ON;
224     if (hctx->handled > 1) return HANDLER_GO_ON;
225     hctx->handled = 2;
226 
227     const array * const aa = hctx->conf.environment;
228     const array * const as = hctx->conf.set_environment;
229 
230     if (aa) {
231         for (uint32_t k = 0; k < hctx->conf.environment->used; ++k) {
232             const data_string * const ds = (const data_string *)aa->data[k];
233             http_header_env_append(r, BUF_PTR_LEN(&ds->key),
234                                       BUF_PTR_LEN(&ds->value));
235         }
236     }
237 
238     if (as) {
239         for (uint32_t k = 0; k < as->used; ++k) {
240             const data_string * const ds = (const data_string *)as->data[k];
241             http_header_env_set(r, BUF_PTR_LEN(&ds->key),
242                                    BUF_PTR_LEN(&ds->value));
243         }
244     }
245 
246     return HANDLER_GO_ON;
247 }
248 
249 REQUEST_FUNC(mod_setenv_handle_response_start) {
250     plugin_data *p = p_d;
251     handler_ctx *hctx = r->plugin_ctx[p->id];
252     if (NULL == hctx) return HANDLER_GO_ON;
253 
254     const array * const aa = hctx->conf.response_header;
255     const array * const as = hctx->conf.set_response_header;
256 
257     if (aa) {
258         for (uint32_t k = 0; k < aa->used; ++k) {
259             const data_string * const ds = (const data_string *)aa->data[k];
260             http_header_response_insert(r, ds->ext, BUF_PTR_LEN(&ds->key),
261                                                     BUF_PTR_LEN(&ds->value));
262         }
263     }
264 
265     if (as) {
266         for (uint32_t k = 0; k < as->used; ++k) {
267             const data_string * const ds = (const data_string *)as->data[k];
268             !buffer_is_blank(&ds->value)
269               ? http_header_response_set(r, ds->ext, BUF_PTR_LEN(&ds->key),
270                                                      BUF_PTR_LEN(&ds->value))
271               : http_header_response_unset(r, ds->ext, BUF_PTR_LEN(&ds->key));
272         }
273     }
274 
275     return HANDLER_GO_ON;
276 }
277 
278 REQUEST_FUNC(mod_setenv_handle_request_reset) {
279     void ** const hctx = r->plugin_ctx+((plugin_data_base *)p_d)->id;
280     if (*hctx) { handler_ctx_free(*hctx); *hctx = NULL; }
281     return HANDLER_GO_ON;
282 }
283 
284 int mod_setenv_plugin_init(plugin *p);
285 int mod_setenv_plugin_init(plugin *p) {
286 	p->version     = LIGHTTPD_VERSION_ID;
287 	p->name        = "setenv";
288 
289 	p->init        = mod_setenv_init;
290 	p->set_defaults= mod_setenv_set_defaults;
291 	p->handle_uri_clean  = mod_setenv_uri_handler;
292 	p->handle_request_env    = mod_setenv_handle_request_env;
293 	p->handle_response_start = mod_setenv_handle_response_start;
294 	p->handle_request_reset  = mod_setenv_handle_request_reset;
295 
296 
297 	return 0;
298 }
299