15 pcre_keyvalue_buffer
*redirect
;
16 data_config
*context
; /* to which apply me */
18 unsigned short redirect_code
;
26 plugin_config
**config_storage
;
31 INIT_FUNC(mod_redirect_init
) {
34 p
= calloc(1, sizeof(*p
));
36 p
->match_buf
= buffer_init();
37 p
->location
= buffer_init();
42 FREE_FUNC(mod_redirect_free
) {
45 if (!p
) return HANDLER_GO_ON
;
47 if (p
->config_storage
) {
49 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
50 plugin_config
*s
= p
->config_storage
[i
];
52 if (NULL
== s
) continue;
54 pcre_keyvalue_buffer_free(s
->redirect
);
58 free(p
->config_storage
);
62 buffer_free(p
->match_buf
);
63 buffer_free(p
->location
);
70 SETDEFAULTS_FUNC(mod_redirect_set_defaults
) {
74 config_values_t cv
[] = {
75 { "url.redirect", NULL
, T_CONFIG_LOCAL
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
76 { "url.redirect-code", NULL
, T_CONFIG_SHORT
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
77 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
80 if (!p
) return HANDLER_ERROR
;
83 p
->config_storage
= calloc(1, srv
->config_context
->used
* sizeof(plugin_config
*));
85 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
86 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
92 s
= calloc(1, sizeof(plugin_config
));
93 s
->redirect
= pcre_keyvalue_buffer_init();
94 s
->redirect_code
= 301;
96 cv
[0].destination
= s
->redirect
;
97 cv
[1].destination
= &(s
->redirect_code
);
99 p
->config_storage
[i
] = s
;
101 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
102 return HANDLER_ERROR
;
105 if (NULL
== (du
= array_get_element(config
->value
, "url.redirect"))) {
106 /* no url.redirect defined */
110 da
= (data_array
*)du
;
112 if (du
->type
!= TYPE_ARRAY
|| !array_is_kvstring(da
->value
)) {
113 log_error_write(srv
, __FILE__
, __LINE__
, "s",
114 "unexpected value for url.redirect; expected list of \"regex\" => \"redirect\"");
115 return HANDLER_ERROR
;
118 for (j
= 0; j
< da
->value
->used
; j
++) {
119 if (0 != pcre_keyvalue_buffer_append(srv
, s
->redirect
,
120 ((data_string
*)(da
->value
->data
[j
]))->key
->ptr
,
121 ((data_string
*)(da
->value
->data
[j
]))->value
->ptr
)) {
123 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
124 "pcre-compile failed for", da
->value
->data
[j
]->key
);
125 return HANDLER_ERROR
;
130 return HANDLER_GO_ON
;
133 static int mod_redirect_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
135 plugin_config
*s
= p
->config_storage
[0];
137 p
->conf
.redirect
= s
->redirect
;
138 p
->conf
.redirect_code
= s
->redirect_code
;
139 p
->conf
.context
= NULL
;
141 /* skip the first, the global context */
142 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
143 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
144 s
= p
->config_storage
[i
];
146 /* condition didn't match */
147 if (!config_check_cond(srv
, con
, dc
)) continue;
150 for (j
= 0; j
< dc
->value
->used
; j
++) {
151 data_unset
*du
= dc
->value
->data
[j
];
153 if (0 == strcmp(du
->key
->ptr
, "url.redirect")) {
154 p
->conf
.redirect
= s
->redirect
;
155 p
->conf
.context
= dc
;
156 } else if (0 == strcmp(du
->key
->ptr
, "url.redirect-code")) {
157 p
->conf
.redirect_code
= s
->redirect_code
;
165 static handler_t
mod_redirect_uri_handler(server
*srv
, connection
*con
, void *p_data
) {
167 plugin_data
*p
= p_data
;
173 * e.g. redirect /base/ to /index.php?section=base
177 mod_redirect_patch_connection(srv
, con
, p
);
179 buffer_copy_buffer(p
->match_buf
, con
->request
.uri
);
181 for (i
= 0; i
< p
->conf
.redirect
->used
; i
++) {
187 pcre_keyvalue
*kv
= p
->conf
.redirect
->kv
[i
];
192 extra
= kv
->key_extra
;
193 pattern
= kv
->value
->ptr
;
194 pattern_len
= buffer_string_length(kv
->value
);
196 if ((n
= pcre_exec(match
, extra
, CONST_BUF_LEN(p
->match_buf
), 0, 0, ovec
, 3 * N
)) < 0) {
197 if (n
!= PCRE_ERROR_NOMATCH
) {
198 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
199 "execution error while matching: ", n
);
200 return HANDLER_ERROR
;
202 } else if (0 == pattern_len
) {
203 /* short-circuit if blank replacement pattern
204 * (do not attempt to match against remaining redirect rules) */
205 return HANDLER_GO_ON
;
212 pcre_get_substring_list(p
->match_buf
->ptr
, ovec
, n
, &list
);
214 /* search for $[0-9] */
216 buffer_reset(p
->location
);
219 for (k
= 0; k
+ 1 < pattern_len
; k
++) {
220 if (pattern
[k
] == '$' || pattern
[k
] == '%') {
223 size_t num
= pattern
[k
+ 1] - '0';
225 buffer_append_string_len(p
->location
, pattern
+ start
, k
- start
);
227 if (!isdigit((unsigned char)pattern
[k
+ 1])) {
228 /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
229 buffer_append_string_len(p
->location
, pattern
+k
, pattern
[k
] == pattern
[k
+1] ? 1 : 2);
230 } else if (pattern
[k
] == '$') {
231 /* n is always > 0 */
232 if (num
< (size_t)n
) {
233 buffer_append_string(p
->location
, list
[num
]);
235 } else if (p
->conf
.context
== NULL
) {
236 /* we have no context, we are global */
237 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
238 "used a rewrite containing a %[0-9]+ in the global scope, ignored:",
241 config_append_cond_match_buffer(con
, p
->conf
.context
, p
->location
, num
);
249 buffer_append_string_len(p
->location
, pattern
+ start
, pattern_len
- start
);
253 response_header_insert(srv
, con
, CONST_STR_LEN("Location"), CONST_BUF_LEN(p
->location
));
255 con
->http_status
= p
->conf
.redirect_code
> 99 && p
->conf
.redirect_code
< 1000 ? p
->conf
.redirect_code
: 301;
257 con
->file_finished
= 1;
259 return HANDLER_FINISHED
;
270 return HANDLER_GO_ON
;
274 int mod_redirect_plugin_init(plugin
*p
);
275 int mod_redirect_plugin_init(plugin
*p
) {
276 p
->version
= LIGHTTPD_VERSION_ID
;
277 p
->name
= buffer_init_string("redirect");
279 p
->init
= mod_redirect_init
;
280 p
->handle_uri_clean
= mod_redirect_uri_handler
;
281 p
->set_defaults
= mod_redirect_set_defaults
;
282 p
->cleanup
= mod_redirect_free
;