xref: /lighttpd1.4/src/mod_setenv.c (revision f2e4e83a)
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 SETDEFAULTS_FUNC(mod_setenv_set_defaults) {
88     static const config_plugin_keys_t cpk[] = {
89       { CONST_STR_LEN("setenv.add-request-header"),
90         T_CONFIG_ARRAY_KVSTRING,
91         T_CONFIG_SCOPE_CONNECTION }
92      ,{ CONST_STR_LEN("setenv.add-response-header"),
93         T_CONFIG_ARRAY_KVSTRING,
94         T_CONFIG_SCOPE_CONNECTION }
95      ,{ CONST_STR_LEN("setenv.add-environment"),
96         T_CONFIG_ARRAY_KVSTRING,
97         T_CONFIG_SCOPE_CONNECTION }
98      ,{ CONST_STR_LEN("setenv.set-request-header"),
99         T_CONFIG_ARRAY_KVSTRING,
100         T_CONFIG_SCOPE_CONNECTION }
101      ,{ CONST_STR_LEN("setenv.set-response-header"),
102         T_CONFIG_ARRAY_KVSTRING,
103         T_CONFIG_SCOPE_CONNECTION }
104      ,{ CONST_STR_LEN("setenv.set-environment"),
105         T_CONFIG_ARRAY_KVSTRING,
106         T_CONFIG_SCOPE_CONNECTION }
107      ,{ NULL, 0,
108         T_CONFIG_UNSET,
109         T_CONFIG_SCOPE_UNSET }
110     };
111 
112     plugin_data * const p = p_d;
113     if (!config_plugin_values_init(srv, p, cpk, "mod_setenv"))
114         return HANDLER_ERROR;
115 
116     /* future: might create custom data structures here
117      * then look up and store http_header_e at config time
118      *   enum http_header_e id = http_header_hkey_get(CONST_BUF_LEN(&ds->key));
119      */
120 
121     /* process and validate config directives
122      * (init i to 0 if global context; to 1 to skip empty global context) */
123     for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) {
124         const config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0];
125         for (; -1 != cpv->k_id; ++cpv) {
126             switch (cpv->k_id) {
127               case 0: /* setenv.add-request-header */
128               case 1: /* setenv.add-response-header */
129               case 2: /* setenv.add-environment */
130               case 3: /* setenv.set-request-header */
131               case 4: /* setenv.set-response-header */
132               case 5: /* setenv.set-environment */
133                 break;
134               default:/* should not happen */
135                 break;
136             }
137         }
138     }
139 
140     /* initialize p->defaults from global config context */
141     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
142         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
143         if (-1 != cpv->k_id)
144             mod_setenv_merge_config(&p->defaults, cpv);
145     }
146 
147     return HANDLER_GO_ON;
148 }
149 
150 URIHANDLER_FUNC(mod_setenv_uri_handler) {
151     plugin_data *p = p_d;
152     handler_ctx *hctx = r->plugin_ctx[p->id];
153     if (!hctx)
154         r->plugin_ctx[p->id] = hctx = handler_ctx_init();
155     else if (hctx->handled)
156         return HANDLER_GO_ON;
157     hctx->handled = 1;
158 
159     mod_setenv_patch_config(r, p, &hctx->conf);
160 
161     const array * const aa = hctx->conf.request_header;
162     const array * const as = hctx->conf.set_request_header;
163 
164     if (aa) {
165         for (uint32_t k = 0; k < aa->used; ++k) {
166             const data_string * const ds = (const data_string *)aa->data[k];
167             const enum http_header_e id =
168               http_header_hkey_get(CONST_BUF_LEN(&ds->key));
169             http_header_request_append(r, id, CONST_BUF_LEN(&ds->key),
170                                                 CONST_BUF_LEN(&ds->value));
171         }
172     }
173 
174     if (as) {
175         for (uint32_t k = 0; k < as->used; ++k) {
176             const data_string * const ds = (const data_string *)as->data[k];
177             const enum http_header_e id =
178               http_header_hkey_get(CONST_BUF_LEN(&ds->key));
179             !buffer_string_is_empty(&ds->value)
180               ? http_header_request_set(r, id, CONST_BUF_LEN(&ds->key),
181                                                CONST_BUF_LEN(&ds->value))
182               : http_header_request_unset(r, id, CONST_BUF_LEN(&ds->key));
183         }
184     }
185 
186     return HANDLER_GO_ON;
187 }
188 
189 REQUEST_FUNC(mod_setenv_handle_request_env) {
190     plugin_data *p = p_d;
191     handler_ctx *hctx = r->plugin_ctx[p->id];
192     if (NULL == hctx) return HANDLER_GO_ON;
193     if (hctx->handled > 1) return HANDLER_GO_ON;
194     hctx->handled = 2;
195 
196     const array * const aa = hctx->conf.environment;
197     const array * const as = hctx->conf.set_environment;
198 
199     if (aa) {
200         for (uint32_t k = 0; k < hctx->conf.environment->used; ++k) {
201             const data_string * const ds = (const data_string *)aa->data[k];
202             http_header_env_append(r, CONST_BUF_LEN(&ds->key),
203                                       CONST_BUF_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             http_header_env_set(r, CONST_BUF_LEN(&ds->key),
211                                    CONST_BUF_LEN(&ds->value));
212         }
213     }
214 
215     return HANDLER_GO_ON;
216 }
217 
218 REQUEST_FUNC(mod_setenv_handle_response_start) {
219     plugin_data *p = p_d;
220     handler_ctx *hctx = r->plugin_ctx[p->id];
221     if (NULL == hctx) return HANDLER_GO_ON;
222 
223     const array * const aa = hctx->conf.response_header;
224     const array * const as = hctx->conf.set_response_header;
225 
226     if (aa) {
227         for (uint32_t k = 0; k < aa->used; ++k) {
228             const data_string * const ds = (const data_string *)aa->data[k];
229             const enum http_header_e id =
230               http_header_hkey_get(CONST_BUF_LEN(&ds->key));
231             http_header_response_insert(r, id, CONST_BUF_LEN(&ds->key),
232                                                CONST_BUF_LEN(&ds->value));
233         }
234     }
235 
236     if (as) {
237         for (uint32_t k = 0; k < as->used; ++k) {
238             const data_string * const ds = (const data_string *)as->data[k];
239             const enum http_header_e id =
240               http_header_hkey_get(CONST_BUF_LEN(&ds->key));
241             !buffer_string_is_empty(&ds->value)
242               ? http_header_response_set(r, id, CONST_BUF_LEN(&ds->key),
243                                                 CONST_BUF_LEN(&ds->value))
244               : http_header_response_unset(r, id, CONST_BUF_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