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 data_config *context; /* to which apply me */ 18 unsigned short redirect_code; 19 } plugin_config; 20 21 typedef struct { 22 PLUGIN_DATA; 23 plugin_config **config_storage; 24 plugin_config conf; 25 } plugin_data; 26 27 INIT_FUNC(mod_redirect_init) { 28 return calloc(1, sizeof(plugin_data)); 29 } 30 31 FREE_FUNC(mod_redirect_free) { 32 plugin_data *p = p_d; 33 if (!p) return HANDLER_GO_ON; 34 35 if (p->config_storage) { 36 size_t i; 37 for (i = 0; i < srv->config_context->used; i++) { 38 plugin_config *s = p->config_storage[i]; 39 if (NULL == s) continue; 40 pcre_keyvalue_buffer_free(s->redirect); 41 free(s); 42 } 43 free(p->config_storage); 44 } 45 46 free(p); 47 return HANDLER_GO_ON; 48 } 49 50 SETDEFAULTS_FUNC(mod_redirect_set_defaults) { 51 plugin_data *p = p_d; 52 size_t i = 0; 53 54 config_values_t cv[] = { 55 { "url.redirect", NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 56 { "url.redirect-code", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 57 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 58 }; 59 60 if (!p) return HANDLER_ERROR; 61 62 /* 0 */ 63 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); 64 65 for (i = 0; i < srv->config_context->used; i++) { 66 data_config const* config = (data_config const*)srv->config_context->data[i]; 67 plugin_config *s; 68 size_t j; 69 data_unset *du; 70 data_array *da; 71 72 s = calloc(1, sizeof(plugin_config)); 73 s->redirect = pcre_keyvalue_buffer_init(); 74 s->redirect_code = 301; 75 76 cv[0].destination = s->redirect; 77 cv[1].destination = &(s->redirect_code); 78 79 p->config_storage[i] = s; 80 81 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 82 return HANDLER_ERROR; 83 } 84 85 if (s->redirect_code < 100 || s->redirect_code >= 1000) s->redirect_code = 301; 86 87 if (NULL == (du = array_get_element(config->value, "url.redirect"))) { 88 /* no url.redirect defined */ 89 continue; 90 } 91 92 da = (data_array *)du; 93 94 if (du->type != TYPE_ARRAY || !array_is_kvstring(da->value)) { 95 log_error_write(srv, __FILE__, __LINE__, "s", 96 "unexpected value for url.redirect; expected list of \"regex\" => \"redirect\""); 97 return HANDLER_ERROR; 98 } 99 100 for (j = 0; j < da->value->used; j++) { 101 data_string *ds = (data_string *)da->value->data[j]; 102 if (srv->srvconf.http_url_normalize) { 103 pcre_keyvalue_burl_normalize_key(ds->key, srv->tmp_buf); 104 pcre_keyvalue_burl_normalize_value(ds->value, srv->tmp_buf); 105 } 106 if (0 != pcre_keyvalue_buffer_append(srv, s->redirect, ds->key, ds->value)) { 107 log_error_write(srv, __FILE__, __LINE__, "sb", 108 "pcre-compile failed for", ds->key); 109 return HANDLER_ERROR; 110 } 111 } 112 } 113 114 return HANDLER_GO_ON; 115 } 116 117 static int mod_redirect_patch_connection(server *srv, connection *con, plugin_data *p) { 118 size_t i, j; 119 plugin_config *s = p->config_storage[0]; 120 121 p->conf.redirect = s->redirect; 122 p->conf.redirect_code = s->redirect_code; 123 p->conf.context = NULL; 124 125 /* skip the first, the global context */ 126 for (i = 1; i < srv->config_context->used; i++) { 127 data_config *dc = (data_config *)srv->config_context->data[i]; 128 s = p->config_storage[i]; 129 130 /* condition didn't match */ 131 if (!config_check_cond(srv, con, dc)) continue; 132 133 /* merge config */ 134 for (j = 0; j < dc->value->used; j++) { 135 data_unset *du = dc->value->data[j]; 136 137 if (0 == strcmp(du->key->ptr, "url.redirect")) { 138 p->conf.redirect = s->redirect; 139 p->conf.context = dc; 140 } else if (0 == strcmp(du->key->ptr, "url.redirect-code")) { 141 p->conf.redirect_code = s->redirect_code; 142 } 143 } 144 } 145 146 return 0; 147 } 148 149 URIHANDLER_FUNC(mod_redirect_uri_handler) { 150 plugin_data *p = p_d; 151 struct burl_parts_t burl; 152 pcre_keyvalue_ctx ctx; 153 handler_t rc; 154 155 mod_redirect_patch_connection(srv, con, p); 156 if (!p->conf.redirect->used) return HANDLER_GO_ON; 157 ctx.cache = p->conf.context 158 ? &con->cond_cache[p->conf.context->context_ndx] 159 : NULL; 160 ctx.burl = &burl; 161 burl.scheme = con->uri.scheme; 162 burl.authority = con->uri.authority; 163 burl.port = sock_addr_get_port(&con->srv_socket->addr); 164 burl.path = con->uri.path_raw; 165 burl.query = con->uri.query; 166 if (buffer_string_is_empty(burl.authority)) 167 burl.authority = con->server_name; 168 169 /* redirect URL on match 170 * e.g. redirect /base/ to /index.php?section=base 171 */ 172 rc = pcre_keyvalue_buffer_process(p->conf.redirect, &ctx, 173 con->request.uri, srv->tmp_buf); 174 if (HANDLER_FINISHED == rc) { 175 http_header_response_set(con, HTTP_HEADER_LOCATION, 176 CONST_STR_LEN("Location"), 177 CONST_BUF_LEN(srv->tmp_buf)); 178 con->http_status = p->conf.redirect_code; 179 con->mode = DIRECT; 180 con->file_finished = 1; 181 } 182 else if (HANDLER_ERROR == rc) { 183 log_error_write(srv, __FILE__, __LINE__, "sb", 184 "pcre_exec() error while processing uri:", 185 con->request.uri); 186 } 187 return rc; 188 } 189 190 int mod_redirect_plugin_init(plugin *p); 191 int mod_redirect_plugin_init(plugin *p) { 192 p->version = LIGHTTPD_VERSION_ID; 193 p->name = buffer_init_string("redirect"); 194 195 p->init = mod_redirect_init; 196 p->handle_uri_clean = mod_redirect_uri_handler; 197 p->set_defaults = mod_redirect_set_defaults; 198 p->cleanup = mod_redirect_free; 199 200 p->data = NULL; 201 202 return 0; 203 } 204