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