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