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 if (du
->type
!= TYPE_ARRAY
) {
111 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
112 "unexpected type for key: ", "url.redirect", "array of strings");
114 return HANDLER_ERROR
;
117 da
= (data_array
*)du
;
119 for (j
= 0; j
< da
->value
->used
; j
++) {
120 if (da
->value
->data
[j
]->type
!= TYPE_STRING
) {
121 log_error_write(srv
, __FILE__
, __LINE__
, "sssbs",
122 "unexpected type for key: ",
124 "[", da
->value
->data
[j
]->key
, "](string)");
126 return HANDLER_ERROR
;
129 if (0 != pcre_keyvalue_buffer_append(srv
, s
->redirect
,
130 ((data_string
*)(da
->value
->data
[j
]))->key
->ptr
,
131 ((data_string
*)(da
->value
->data
[j
]))->value
->ptr
)) {
133 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
134 "pcre-compile failed for", da
->value
->data
[j
]->key
);
135 return HANDLER_ERROR
;
140 return HANDLER_GO_ON
;
143 static int mod_redirect_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
145 plugin_config
*s
= p
->config_storage
[0];
147 p
->conf
.redirect
= s
->redirect
;
148 p
->conf
.redirect_code
= s
->redirect_code
;
149 p
->conf
.context
= NULL
;
151 /* skip the first, the global context */
152 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
153 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
154 s
= p
->config_storage
[i
];
156 /* condition didn't match */
157 if (!config_check_cond(srv
, con
, dc
)) continue;
160 for (j
= 0; j
< dc
->value
->used
; j
++) {
161 data_unset
*du
= dc
->value
->data
[j
];
163 if (0 == strcmp(du
->key
->ptr
, "url.redirect")) {
164 p
->conf
.redirect
= s
->redirect
;
165 p
->conf
.context
= dc
;
166 } else if (0 == strcmp(du
->key
->ptr
, "url.redirect-code")) {
167 p
->conf
.redirect_code
= s
->redirect_code
;
175 static handler_t
mod_redirect_uri_handler(server
*srv
, connection
*con
, void *p_data
) {
177 plugin_data
*p
= p_data
;
183 * e.g. redirect /base/ to /index.php?section=base
187 mod_redirect_patch_connection(srv
, con
, p
);
189 buffer_copy_buffer(p
->match_buf
, con
->request
.uri
);
191 for (i
= 0; i
< p
->conf
.redirect
->used
; i
++) {
197 pcre_keyvalue
*kv
= p
->conf
.redirect
->kv
[i
];
202 extra
= kv
->key_extra
;
203 pattern
= kv
->value
->ptr
;
204 pattern_len
= buffer_string_length(kv
->value
);
206 if ((n
= pcre_exec(match
, extra
, CONST_BUF_LEN(p
->match_buf
), 0, 0, ovec
, 3 * N
)) < 0) {
207 if (n
!= PCRE_ERROR_NOMATCH
) {
208 log_error_write(srv
, __FILE__
, __LINE__
, "sd",
209 "execution error while matching: ", n
);
210 return HANDLER_ERROR
;
212 } else if (0 == pattern_len
) {
213 /* short-circuit if blank replacement pattern
214 * (do not attempt to match against remaining redirect rules) */
215 return HANDLER_GO_ON
;
222 pcre_get_substring_list(p
->match_buf
->ptr
, ovec
, n
, &list
);
224 /* search for $[0-9] */
226 buffer_reset(p
->location
);
229 for (k
= 0; k
+ 1 < pattern_len
; k
++) {
230 if (pattern
[k
] == '$' || pattern
[k
] == '%') {
233 size_t num
= pattern
[k
+ 1] - '0';
235 buffer_append_string_len(p
->location
, pattern
+ start
, k
- start
);
237 if (!isdigit((unsigned char)pattern
[k
+ 1])) {
238 /* enable escape: "%%" => "%", "%a" => "%a", "$$" => "$" */
239 buffer_append_string_len(p
->location
, pattern
+k
, pattern
[k
] == pattern
[k
+1] ? 1 : 2);
240 } else if (pattern
[k
] == '$') {
241 /* n is always > 0 */
242 if (num
< (size_t)n
) {
243 buffer_append_string(p
->location
, list
[num
]);
245 } else if (p
->conf
.context
== NULL
) {
246 /* we have no context, we are global */
247 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
248 "used a rewrite containing a %[0-9]+ in the global scope, ignored:",
251 config_append_cond_match_buffer(con
, p
->conf
.context
, p
->location
, num
);
259 buffer_append_string_len(p
->location
, pattern
+ start
, pattern_len
- start
);
263 response_header_insert(srv
, con
, CONST_STR_LEN("Location"), CONST_BUF_LEN(p
->location
));
265 con
->http_status
= p
->conf
.redirect_code
> 99 && p
->conf
.redirect_code
< 1000 ? p
->conf
.redirect_code
: 301;
267 con
->file_finished
= 1;
269 return HANDLER_FINISHED
;
280 return HANDLER_GO_ON
;
284 int mod_redirect_plugin_init(plugin
*p
);
285 int mod_redirect_plugin_init(plugin
*p
) {
286 p
->version
= LIGHTTPD_VERSION_ID
;
287 p
->name
= buffer_init_string("redirect");
289 p
->init
= mod_redirect_init
;
290 p
->handle_uri_clean
= mod_redirect_uri_handler
;
291 p
->set_defaults
= mod_redirect_set_defaults
;
292 p
->cleanup
= mod_redirect_free
;