1 #include "first.h" 2 3 #include "base.h" 4 #include "keyvalue.h" 5 #include "log.h" 6 #include "buffer.h" 7 #include "burl.h" 8 #include "http_header.h" 9 10 #include "plugin.h" 11 12 #include <stdlib.h> 13 #include <string.h> 14 15 typedef struct { 16 pcre_keyvalue_buffer *redirect; 17 int redirect_code; 18 } plugin_config; 19 20 typedef struct { 21 PLUGIN_DATA; 22 plugin_config defaults; 23 plugin_config conf; 24 } plugin_data; 25 26 INIT_FUNC(mod_redirect_init) { 27 return calloc(1, sizeof(plugin_data)); 28 } 29 30 FREE_FUNC(mod_redirect_free) { 31 plugin_data * const p = p_d; 32 if (NULL == p->cvlist) return; 33 /* (init i to 0 if global context; to 1 to skip empty global context) */ 34 for (int i = !p->cvlist[0].v.u2[1], used = p->nconfig; i < used; ++i) { 35 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 36 for (; -1 != cpv->k_id; ++cpv) { 37 switch (cpv->k_id) { 38 case 0: /* url.redirect */ 39 if (cpv->vtype == T_CONFIG_LOCAL) 40 pcre_keyvalue_buffer_free(cpv->v.v); 41 break; 42 default: 43 break; 44 } 45 } 46 } 47 } 48 49 static void mod_redirect_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) { 50 switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */ 51 case 0: /* url.redirect */ 52 if (cpv->vtype == T_CONFIG_LOCAL) 53 pconf->redirect = cpv->v.v; 54 break; 55 case 1: /* url.redirect-code */ 56 pconf->redirect_code = cpv->v.shrt; 57 break; 58 default:/* should not happen */ 59 return; 60 } 61 } 62 63 static void mod_redirect_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) { 64 do { 65 mod_redirect_merge_config_cpv(pconf, cpv); 66 } while ((++cpv)->k_id != -1); 67 } 68 69 static void mod_redirect_patch_config(request_st * const r, plugin_data * const p) { 70 p->conf = p->defaults; /* copy small struct instead of memcpy() */ 71 /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/ 72 for (int i = 1, used = p->nconfig; i < used; ++i) { 73 if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id)) 74 mod_redirect_merge_config(&p->conf, p->cvlist+p->cvlist[i].v.u2[0]); 75 } 76 } 77 78 static pcre_keyvalue_buffer * mod_redirect_parse_list(server *srv, const array *a, const int condidx) { 79 const int pcre_jit = 80 !srv->srvconf.feature_flags 81 || config_plugin_value_tobool( 82 array_get_element_klen(srv->srvconf.feature_flags, 83 CONST_STR_LEN("server.pcre_jit")), 1); 84 pcre_keyvalue_buffer * const kvb = pcre_keyvalue_buffer_init(); 85 kvb->x0 = (unsigned short)condidx; 86 buffer * const tb = srv->tmp_buf; 87 for (uint32_t j = 0; j < a->used; ++j) { 88 data_string *ds = (data_string *)a->data[j]; 89 if (srv->srvconf.http_url_normalize) { 90 pcre_keyvalue_burl_normalize_key(&ds->key, tb); 91 pcre_keyvalue_burl_normalize_value(&ds->value, tb); 92 } 93 if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value, 94 pcre_jit)) { 95 log_error(srv->errh, __FILE__, __LINE__, 96 "pcre-compile failed for %s", ds->key.ptr); 97 pcre_keyvalue_buffer_free(kvb); 98 return NULL; 99 } 100 } 101 return kvb; 102 } 103 104 SETDEFAULTS_FUNC(mod_redirect_set_defaults) { 105 static const config_plugin_keys_t cpk[] = { 106 { CONST_STR_LEN("url.redirect"), 107 T_CONFIG_ARRAY_KVSTRING, 108 T_CONFIG_SCOPE_CONNECTION } 109 ,{ CONST_STR_LEN("url.redirect-code"), 110 T_CONFIG_SHORT, 111 T_CONFIG_SCOPE_CONNECTION } 112 ,{ NULL, 0, 113 T_CONFIG_UNSET, 114 T_CONFIG_SCOPE_UNSET } 115 }; 116 117 plugin_data * const p = p_d; 118 if (!config_plugin_values_init(srv, p, cpk, "mod_redirect")) 119 return HANDLER_ERROR; 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 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: /* url.redirect */ 128 cpv->v.v = 129 mod_redirect_parse_list(srv, cpv->v.a, p->cvlist[i].k_id); 130 if (NULL == cpv->v.v) return HANDLER_ERROR; 131 cpv->vtype = T_CONFIG_LOCAL; 132 break; 133 case 1: /* url.redirect-code */ 134 if (cpv->v.shrt < 100 || cpv->v.shrt >= 1000) cpv->v.shrt = 301; 135 break; 136 default:/* should not happen */ 137 break; 138 } 139 } 140 } 141 142 p->defaults.redirect_code = 301; 143 144 /* initialize p->defaults from global config context */ 145 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 146 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 147 if (-1 != cpv->k_id) 148 mod_redirect_merge_config(&p->defaults, cpv); 149 } 150 151 return HANDLER_GO_ON; 152 } 153 154 URIHANDLER_FUNC(mod_redirect_uri_handler) { 155 plugin_data * const p = p_d; 156 struct burl_parts_t burl; 157 pcre_keyvalue_ctx ctx; 158 handler_t rc; 159 160 mod_redirect_patch_config(r, p); 161 if (!p->conf.redirect || !p->conf.redirect->used) return HANDLER_GO_ON; 162 163 ctx.cache = NULL; 164 if (p->conf.redirect->x0) { /*(p->conf.redirect->x0 is context_idx)*/ 165 ctx.cond_match_count = 166 r->cond_cache[p->conf.redirect->x0].patterncount; 167 ctx.cache = r->cond_match + p->conf.redirect->x0; 168 } 169 ctx.burl = &burl; 170 burl.scheme = &r->uri.scheme; 171 burl.authority = &r->uri.authority; 172 burl.port = sock_addr_get_port(&r->con->srv_socket->addr); 173 burl.path = &r->target; /*(uri-encoded and includes query-part)*/ 174 burl.query = &r->uri.query; 175 if (buffer_string_is_empty(burl.authority)) 176 burl.authority = r->server_name; 177 178 /* redirect URL on match 179 * e.g. redirect /base/ to /index.php?section=base 180 */ 181 buffer * const tb = r->tmp_buf; 182 rc = pcre_keyvalue_buffer_process(p->conf.redirect, &ctx, 183 &r->target, tb); 184 if (HANDLER_FINISHED == rc) { 185 http_header_response_set(r, HTTP_HEADER_LOCATION, 186 CONST_STR_LEN("Location"), 187 CONST_BUF_LEN(tb)); 188 r->http_status = p->conf.redirect_code; 189 r->handler_module = NULL; 190 r->resp_body_finished = 1; 191 } 192 else if (HANDLER_ERROR == rc) { 193 log_error(r->conf.errh, __FILE__, __LINE__, 194 "pcre_exec() error while processing uri: %s", 195 r->target.ptr); 196 } 197 return rc; 198 } 199 200 int mod_redirect_plugin_init(plugin *p); 201 int mod_redirect_plugin_init(plugin *p) { 202 p->version = LIGHTTPD_VERSION_ID; 203 p->name = "redirect"; 204 205 p->init = mod_redirect_init; 206 p->handle_uri_clean = mod_redirect_uri_handler; 207 p->set_defaults = mod_redirect_set_defaults; 208 p->cleanup = mod_redirect_free; 209 210 return 0; 211 } 212