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 ck_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 = config_feature_bool(srv, "server.pcre_jit", 1); 80 pcre_keyvalue_buffer * const kvb = pcre_keyvalue_buffer_init(); 81 kvb->cfgidx = condidx; 82 buffer * const tb = srv->tmp_buf; 83 int percent = 0; 84 for (uint32_t j = 0; j < a->used; ++j) { 85 data_string *ds = (data_string *)a->data[j]; 86 if (srv->srvconf.http_url_normalize) { 87 pcre_keyvalue_burl_normalize_key(&ds->key, tb); 88 pcre_keyvalue_burl_normalize_value(&ds->value, tb); 89 } 90 for (const char *s = ds->value.ptr; (s = strchr(s, '%')); ++s) { 91 if (s[1] == '%') 92 ++s; 93 else if (light_isdigit(s[1]) || s[1] == '{') { 94 percent |= 1; 95 break; 96 } 97 } 98 if (!pcre_keyvalue_buffer_append(srv->errh, kvb, &ds->key, &ds->value, 99 pcre_jit)) { 100 log_error(srv->errh, __FILE__, __LINE__, 101 "pcre-compile failed for %s", ds->key.ptr); 102 pcre_keyvalue_buffer_free(kvb); 103 return NULL; 104 } 105 } 106 if (percent) 107 kvb->x0 = config_capture(srv, condidx); 108 return kvb; 109 } 110 111 SETDEFAULTS_FUNC(mod_redirect_set_defaults) { 112 static const config_plugin_keys_t cpk[] = { 113 { CONST_STR_LEN("url.redirect"), 114 T_CONFIG_ARRAY_KVSTRING, 115 T_CONFIG_SCOPE_CONNECTION } 116 ,{ CONST_STR_LEN("url.redirect-code"), 117 T_CONFIG_SHORT, 118 T_CONFIG_SCOPE_CONNECTION } 119 ,{ NULL, 0, 120 T_CONFIG_UNSET, 121 T_CONFIG_SCOPE_UNSET } 122 }; 123 124 plugin_data * const p = p_d; 125 if (!config_plugin_values_init(srv, p, cpk, "mod_redirect")) 126 return HANDLER_ERROR; 127 128 /* process and validate config directives 129 * (init i to 0 if global context; to 1 to skip empty global context) */ 130 for (int i = !p->cvlist[0].v.u2[1]; i < p->nconfig; ++i) { 131 config_plugin_value_t *cpv = p->cvlist + p->cvlist[i].v.u2[0]; 132 for (; -1 != cpv->k_id; ++cpv) { 133 switch (cpv->k_id) { 134 case 0: /* url.redirect */ 135 cpv->v.v = 136 mod_redirect_parse_list(srv, cpv->v.a, p->cvlist[i].k_id); 137 if (NULL == cpv->v.v) return HANDLER_ERROR; 138 cpv->vtype = T_CONFIG_LOCAL; 139 break; 140 case 1: /* url.redirect-code */ 141 if (cpv->v.shrt < 100 || cpv->v.shrt >= 1000) cpv->v.shrt = 301; 142 break; 143 default:/* should not happen */ 144 break; 145 } 146 } 147 } 148 149 p->defaults.redirect_code = 301; 150 151 /* initialize p->defaults from global config context */ 152 if (p->nconfig > 0 && p->cvlist->v.u2[1]) { 153 const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0]; 154 if (-1 != cpv->k_id) 155 mod_redirect_merge_config(&p->defaults, cpv); 156 } 157 158 return HANDLER_GO_ON; 159 } 160 161 URIHANDLER_FUNC(mod_redirect_uri_handler) { 162 plugin_data * const p = p_d; 163 struct burl_parts_t burl; 164 pcre_keyvalue_ctx ctx; 165 handler_t rc; 166 167 mod_redirect_patch_config(r, p); 168 if (!p->conf.redirect || !p->conf.redirect->used) return HANDLER_GO_ON; 169 170 ctx.cache = NULL; 171 if (p->conf.redirect->x0) { /*(p->conf.redirect->x0 is capture_idx)*/ 172 ctx.cache = r->cond_match[p->conf.redirect->x0 - 1]; 173 } 174 ctx.burl = &burl; 175 burl.scheme = &r->uri.scheme; 176 burl.authority = &r->uri.authority; 177 burl.port = sock_addr_get_port(&r->con->srv_socket->addr); 178 burl.path = &r->target; /*(uri-encoded and includes query-part)*/ 179 burl.query = &r->uri.query; 180 if (buffer_is_blank(burl.authority)) 181 burl.authority = r->server_name; 182 183 /* redirect URL on match 184 * e.g. redirect /base/ to /index.php?section=base 185 */ 186 buffer * const tb = r->tmp_buf; 187 rc = pcre_keyvalue_buffer_process(p->conf.redirect, &ctx, 188 &r->target, tb); 189 if (HANDLER_FINISHED == rc) { 190 http_header_response_set(r, HTTP_HEADER_LOCATION, 191 CONST_STR_LEN("Location"), 192 BUF_PTR_LEN(tb)); 193 r->http_status = p->conf.redirect_code; 194 r->handler_module = NULL; 195 r->resp_body_finished = 1; 196 } 197 else if (HANDLER_ERROR == rc) { 198 log_error(r->conf.errh, __FILE__, __LINE__, 199 "pcre_exec() error while processing uri: %s", 200 r->target.ptr); 201 } 202 return rc; 203 } 204 205 206 __attribute_cold__ 207 int mod_redirect_plugin_init(plugin *p); 208 int mod_redirect_plugin_init(plugin *p) { 209 p->version = LIGHTTPD_VERSION_ID; 210 p->name = "redirect"; 211 212 p->init = mod_redirect_init; 213 p->handle_uri_clean = mod_redirect_uri_handler; 214 p->set_defaults = mod_redirect_set_defaults; 215 p->cleanup = mod_redirect_free; 216 217 return 0; 218 } 219