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