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