xref: /lighttpd1.4/src/mod_setenv.c (revision 2e0edb85)
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     }
93 }
94 
95 SETDEFAULTS_FUNC(mod_setenv_set_defaults) {
96     static const config_plugin_keys_t cpk[] = {
97       { CONST_STR_LEN("setenv.add-request-header"),
98         T_CONFIG_ARRAY_KVSTRING,
99         T_CONFIG_SCOPE_CONNECTION }
100      ,{ CONST_STR_LEN("setenv.add-response-header"),
101         T_CONFIG_ARRAY_KVSTRING,
102         T_CONFIG_SCOPE_CONNECTION }
103      ,{ CONST_STR_LEN("setenv.add-environment"),
104         T_CONFIG_ARRAY_KVSTRING,
105         T_CONFIG_SCOPE_CONNECTION }
106      ,{ CONST_STR_LEN("setenv.set-request-header"),
107         T_CONFIG_ARRAY_KVSTRING,
108         T_CONFIG_SCOPE_CONNECTION }
109      ,{ CONST_STR_LEN("setenv.set-response-header"),
110         T_CONFIG_ARRAY_KVSTRING,
111         T_CONFIG_SCOPE_CONNECTION }
112      ,{ CONST_STR_LEN("setenv.set-environment"),
113         T_CONFIG_ARRAY_KVSTRING,
114         T_CONFIG_SCOPE_CONNECTION }
115      ,{ NULL, 0,
116         T_CONFIG_UNSET,
117         T_CONFIG_SCOPE_UNSET }
118     };
119 
120     plugin_data * const p = p_d;
121     if (!config_plugin_values_init(srv, p, cpk, "mod_setenv"))
122         return HANDLER_ERROR;
123 
124     /* process and validate config directives
125      * (init i to 0 if global context; to 1 to skip empty global context) */
126     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
127         const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
128         for (; -1 != cpv->k_id; ++cpv) {
129             switch (cpv->k_id) {
130               case 0: /* setenv.add-request-header */
131               case 1: /* setenv.add-response-header */
132                 mod_setenv_prep_ext(cpv->v.a);
133                 break;
134               case 2: /* setenv.add-environment */
135                 break;
136               case 3: /* setenv.set-request-header */
137               case 4: /* setenv.set-response-header */
138                 mod_setenv_prep_ext(cpv->v.a);
139                 break;
140               case 5: /* setenv.set-environment */
141                 break;
142               default:/* should not happen */
143                 break;
144             }
145         }
146     }
147 
148     /* initialize p->defaults from global config context */
149     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
150         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
151         if (-1 != cpv->k_id)
152             mod_setenv_merge_config(&p->defaults, cpv);
153     }
154 
155     return HANDLER_GO_ON;
156 }
157 
158 URIHANDLER_FUNC(mod_setenv_uri_handler) {
159     plugin_data *p = p_d;
160     handler_ctx *hctx = r->plugin_ctx[p->id];
161     if (!hctx)
162         r->plugin_ctx[p->id] = hctx = handler_ctx_init();
163     else if (hctx->handled)
164         return HANDLER_GO_ON;
165     hctx->handled = 1;
166 
167     mod_setenv_patch_config(r, p, &hctx->conf);
168 
169     const array * const aa = hctx->conf.request_header;
170     const array * const as = hctx->conf.set_request_header;
171 
172     if (aa) {
173         for (uint32_t k = 0; k < aa->used; ++k) {
174             const data_string * const ds = (const data_string *)aa->data[k];
175             http_header_request_append(r, ds->ext, BUF_PTR_LEN(&ds->key),
176                                                    BUF_PTR_LEN(&ds->value));
177         }
178     }
179 
180     if (as) {
181         for (uint32_t k = 0; k < as->used; ++k) {
182             const data_string * const ds = (const data_string *)as->data[k];
183             !buffer_is_blank(&ds->value)
184               ? http_header_request_set(r, ds->ext, BUF_PTR_LEN(&ds->key),
185                                                     BUF_PTR_LEN(&ds->value))
186               : http_header_request_unset(r, ds->ext, BUF_PTR_LEN(&ds->key));
187         }
188     }
189 
190     return HANDLER_GO_ON;
191 }
192 
193 REQUEST_FUNC(mod_setenv_handle_request_env) {
194     plugin_data *p = p_d;
195     handler_ctx *hctx = r->plugin_ctx[p->id];
196     if (NULL == hctx) return HANDLER_GO_ON;
197     if (hctx->handled > 1) return HANDLER_GO_ON;
198     hctx->handled = 2;
199 
200     const array * const aa = hctx->conf.environment;
201     const array * const as = hctx->conf.set_environment;
202 
203     if (aa) {
204         for (uint32_t k = 0; k < hctx->conf.environment->used; ++k) {
205             const data_string * const ds = (const data_string *)aa->data[k];
206             http_header_env_append(r, BUF_PTR_LEN(&ds->key),
207                                       BUF_PTR_LEN(&ds->value));
208         }
209     }
210 
211     if (as) {
212         for (uint32_t k = 0; k < as->used; ++k) {
213             const data_string * const ds = (const data_string *)as->data[k];
214             http_header_env_set(r, BUF_PTR_LEN(&ds->key),
215                                    BUF_PTR_LEN(&ds->value));
216         }
217     }
218 
219     return HANDLER_GO_ON;
220 }
221 
222 REQUEST_FUNC(mod_setenv_handle_response_start) {
223     plugin_data *p = p_d;
224     handler_ctx *hctx = r->plugin_ctx[p->id];
225     if (NULL == hctx) return HANDLER_GO_ON;
226 
227     const array * const aa = hctx->conf.response_header;
228     const array * const as = hctx->conf.set_response_header;
229 
230     if (aa) {
231         for (uint32_t k = 0; k < aa->used; ++k) {
232             const data_string * const ds = (const data_string *)aa->data[k];
233             http_header_response_insert(r, ds->ext, 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             !buffer_is_blank(&ds->value)
242               ? http_header_response_set(r, ds->ext, BUF_PTR_LEN(&ds->key),
243                                                      BUF_PTR_LEN(&ds->value))
244               : http_header_response_unset(r, ds->ext, BUF_PTR_LEN(&ds->key));
245         }
246     }
247 
248     return HANDLER_GO_ON;
249 }
250 
251 REQUEST_FUNC(mod_setenv_handle_request_reset) {
252     void ** const hctx = r->plugin_ctx+((plugin_data_base *)p_d)->id;
253     if (*hctx) { handler_ctx_free(*hctx); *hctx = NULL; }
254     return HANDLER_GO_ON;
255 }
256 
257 int mod_setenv_plugin_init(plugin *p);
258 int mod_setenv_plugin_init(plugin *p) {
259 	p->version     = LIGHTTPD_VERSION_ID;
260 	p->name        = "setenv";
261 
262 	p->init        = mod_setenv_init;
263 	p->set_defaults= mod_setenv_set_defaults;
264 	p->handle_uri_clean  = mod_setenv_uri_handler;
265 	p->handle_request_env    = mod_setenv_handle_request_env;
266 	p->handle_response_start = mod_setenv_handle_response_start;
267 	p->handle_request_reset  = mod_setenv_handle_request_reset;
268 
269 
270 	return 0;
271 }
272