/src/mod_auth_openidc/test/stub.c
Line | Count | Source |
1 | | #include "util/request_state.h" |
2 | | #include <apr_global_mutex.h> |
3 | | #include <apr_lib.h> |
4 | | #include <apr_strings.h> |
5 | | |
6 | | // clang-format off |
7 | | |
8 | | #include <httpd.h> |
9 | | #include <http_config.h> |
10 | | #include <http_core.h> |
11 | | #include <http_log.h> |
12 | | |
13 | | // clang-format on |
14 | | |
15 | | /* NB: deliberately not "util.h": including it here pulls in <http_request.h>, whose AP_DECLARE_HOOK |
16 | | * declarations collide with the hand-rolled ap_hook_* shims below. These mirror the declarations in |
17 | | * util.h instead; the two must stay in step. */ |
18 | | typedef int (*oidc_test_hook_post_config_fn)(apr_pool_t *pool, apr_pool_t *p1, apr_pool_t *p2, server_rec *s); |
19 | | typedef void (*oidc_test_hook_child_init_fn)(apr_pool_t *p, server_rec *s); |
20 | | typedef void (*oidc_test_hook_insert_filter_fn)(request_rec *r); |
21 | | typedef apr_status_t (*oidc_test_input_filter_fn)(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode, |
22 | | apr_read_type_e block, apr_off_t nbytes); |
23 | | |
24 | | static oidc_test_hook_insert_filter_fn _oidc_test_hook_insert_filter = NULL; |
25 | | static oidc_test_input_filter_fn _oidc_test_input_filter = NULL; |
26 | | static const char *_oidc_test_added_input_filter = NULL; |
27 | | static const char *_oidc_test_brigade_body = NULL; |
28 | | static apr_status_t _oidc_test_brigade_rc = APR_SUCCESS; |
29 | | static const char *_oidc_test_exec_line_output = NULL; |
30 | | |
31 | | #define ap_HOOK_check_user_id_t void |
32 | | |
33 | | AP_DECLARE(void) |
34 | | ap_hook_check_authn(ap_HOOK_check_user_id_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder, |
35 | 0 | int type) { |
36 | | // comment explaining why the method is empty |
37 | 0 | } |
38 | | |
39 | | /* the first authz provider the module registers, so its parse_require_line can be driven */ |
40 | | static const void *_oidc_test_authz_provider = NULL; |
41 | | |
42 | 0 | const void *oidc_test_authz_provider_get(void) { |
43 | 0 | return _oidc_test_authz_provider; |
44 | 0 | } |
45 | | |
46 | | AP_DECLARE(apr_status_t) |
47 | | ap_register_auth_provider(apr_pool_t *pool, const char *provider_group, const char *provider_name, |
48 | 0 | const char *provider_version, const void *provider, int type) { |
49 | 0 | if (_oidc_test_authz_provider == NULL) |
50 | 0 | _oidc_test_authz_provider = provider; |
51 | 0 | return 0; |
52 | 0 | } |
53 | | |
54 | 4 | AP_DECLARE(apr_status_t) ap_unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) { |
55 | 4 | return 0; |
56 | 4 | } |
57 | | |
58 | | /* the AuthType reported to the module; default openid-connect, overridable per |
59 | | * test via oidc_test_set_auth_type() so the OAuth / mixed dispatch paths can be |
60 | | * exercised */ |
61 | | static const char *stub_auth_type = "openid-connect"; |
62 | | |
63 | 2 | void oidc_test_set_auth_type(const char *auth_type) { |
64 | 2 | stub_auth_type = (auth_type != NULL) ? auth_type : "openid-connect"; |
65 | 2 | } |
66 | | |
67 | 0 | AP_DECLARE(const char *) ap_auth_type(request_rec *r) { |
68 | 0 | return stub_auth_type; |
69 | 0 | } |
70 | | |
71 | 0 | AP_DECLARE(const char *) ap_auth_name(request_rec *r) { |
72 | 0 | return NULL; |
73 | 0 | } |
74 | | |
75 | 0 | AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, apr_size_t bufsiz) { |
76 | | /* tests stash the POST body in r->args (and r->remaining tracks bytes left); copy |
77 | | * from there into the caller's buffer so oidc_util_read can drive the form parser */ |
78 | 0 | if (r->args == NULL || r->remaining == 0) |
79 | 0 | return 0; |
80 | | /* r->remaining is signed (apr_off_t) and is only ever positive here, but compare and |
81 | | * assign in one type so the narrowing is explicit rather than a signedness surprise */ |
82 | 0 | apr_size_t remaining = (apr_size_t)r->remaining; |
83 | 0 | apr_size_t off = strlen(r->args) - remaining; |
84 | 0 | apr_size_t n = (bufsiz < remaining) ? bufsiz : remaining; |
85 | 0 | memcpy(buffer, r->args + off, n); |
86 | 0 | r->remaining -= n; |
87 | 0 | return (long)n; |
88 | 0 | } |
89 | | |
90 | 0 | AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop) { |
91 | 0 | const char *pos = *line; |
92 | 0 | int len; |
93 | 0 | char *res; |
94 | |
|
95 | 0 | while ((*pos != stop) && *pos) { |
96 | 0 | ++pos; |
97 | 0 | } |
98 | |
|
99 | 0 | len = (int)(pos - *line); |
100 | 0 | res = apr_pstrmemdup(atrans, *line, len); |
101 | |
|
102 | 0 | if (stop) { |
103 | 0 | while (*pos == stop) { |
104 | 0 | ++pos; |
105 | 0 | } |
106 | 0 | } |
107 | 0 | *line = pos; |
108 | |
|
109 | 0 | return res; |
110 | 0 | } |
111 | | |
112 | 0 | static char *substring_conf(apr_pool_t *p, const char *start, int len, char quote) { |
113 | 0 | char *result = apr_palloc(p, len + 1); |
114 | 0 | char *resp = result; |
115 | 0 | int i; |
116 | |
|
117 | 0 | for (i = 0; i < len; ++i) { |
118 | 0 | if (start[i] == '\\' && (start[i + 1] == '\\' || (quote && start[i + 1] == quote))) |
119 | 0 | *resp++ = start[++i]; |
120 | 0 | else |
121 | 0 | *resp++ = start[i]; |
122 | 0 | } |
123 | |
|
124 | 0 | *resp++ = '\0'; |
125 | | #if RESOLVE_ENV_PER_TOKEN |
126 | | return (char *)ap_resolve_env(p, result); |
127 | | #else |
128 | 0 | return result; |
129 | 0 | #endif |
130 | 0 | } |
131 | | |
132 | 0 | AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) { |
133 | 0 | const char *str = *line, *strend; |
134 | 0 | char *res; |
135 | 0 | char quote; |
136 | |
|
137 | 0 | while (apr_isspace(*str)) |
138 | 0 | ++str; |
139 | |
|
140 | 0 | if (!*str) { |
141 | 0 | *line = str; |
142 | 0 | return ""; |
143 | 0 | } |
144 | | |
145 | 0 | if ((quote = *str) == '"' || quote == '\'') { |
146 | 0 | strend = str + 1; |
147 | 0 | while (*strend && *strend != quote) { |
148 | 0 | if (*strend == '\\' && strend[1] && (strend[1] == quote || strend[1] == '\\')) { |
149 | 0 | strend += 2; |
150 | 0 | } else { |
151 | 0 | ++strend; |
152 | 0 | } |
153 | 0 | } |
154 | 0 | res = substring_conf(p, str + 1, (int)(strend - str - 1), quote); |
155 | |
|
156 | 0 | if (*strend == quote) |
157 | 0 | ++strend; |
158 | 0 | } else { |
159 | 0 | strend = str; |
160 | 0 | while (*strend && !apr_isspace(*strend)) |
161 | 0 | ++strend; |
162 | |
|
163 | 0 | res = substring_conf(p, str, (int)(strend - str), 0); |
164 | 0 | } |
165 | |
|
166 | 0 | while (apr_isspace(*strend)) |
167 | 0 | ++strend; |
168 | 0 | *line = strend; |
169 | 0 | return res; |
170 | 0 | } |
171 | | |
172 | 0 | AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *p, const char **line, char stop) { |
173 | | /* match Apache semantics: return the word before `stop`, advance *line past |
174 | | * the separator (or to '\0' if `stop` is not present). The previous stub |
175 | | * silently returned "" and left *line untouched, which masked any callers |
176 | | * that relied on the advance — e.g. oidc_oauth_token_from_basic. */ |
177 | 0 | const char *pos = *line; |
178 | 0 | while (*pos && (*pos != stop)) |
179 | 0 | ++pos; |
180 | 0 | char *res = apr_pstrmemdup(p, *line, pos - *line); |
181 | 0 | *line = (*pos == stop) ? pos + 1 : pos; |
182 | 0 | return res; |
183 | 0 | } |
184 | | |
185 | 0 | AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line) { |
186 | 0 | const char *pos = *line; |
187 | 0 | int len; |
188 | 0 | char *res; |
189 | |
|
190 | 0 | while (!apr_isspace(*pos) && *pos) { |
191 | 0 | ++pos; |
192 | 0 | } |
193 | |
|
194 | 0 | len = (int)(pos - *line); |
195 | 0 | res = apr_pstrmemdup(atrans, *line, len); |
196 | |
|
197 | 0 | while (apr_isspace(*pos)) { |
198 | 0 | ++pos; |
199 | 0 | } |
200 | |
|
201 | 0 | *line = pos; |
202 | |
|
203 | 0 | return res; |
204 | 0 | } |
205 | | |
206 | 0 | AP_DECLARE(int) ap_hook_check_user_id(request_rec *r) { |
207 | 0 | return 0; |
208 | 0 | } |
209 | | |
210 | 0 | AP_DECLARE(int) ap_hook_auth_checker(request_rec *r) { |
211 | 0 | return 0; |
212 | 0 | } |
213 | | |
214 | | AP_DECLARE(void) |
215 | 0 | ap_hook_fixups(int (*handler)(request_rec *r), const char *const *aszPre, const char *const *aszSucc, int nOrder) { |
216 | | // comment explaining why the method is empty |
217 | 0 | } |
218 | | |
219 | | AP_DECLARE(void) |
220 | | ap_hook_insert_filter(void (*insert_filter)(request_rec *r), const char *const *aszPre, const char *const *aszSucc, |
221 | 0 | int nOrder) { |
222 | 0 | _oidc_test_hook_insert_filter = insert_filter; |
223 | 0 | } |
224 | | |
225 | | /* |
226 | | * the hooks the module registers are recorded rather than discarded, so a test can drive the |
227 | | * server-lifetime entry points (post_config, child_init) the way httpd would; see test_config.c |
228 | | */ |
229 | | static oidc_test_hook_post_config_fn _oidc_test_hook_post_config = NULL; |
230 | | static oidc_test_hook_child_init_fn _oidc_test_hook_child_init = NULL; |
231 | | |
232 | 0 | oidc_test_hook_post_config_fn oidc_test_hook_post_config_get(void) { |
233 | 0 | return _oidc_test_hook_post_config; |
234 | 0 | } |
235 | | |
236 | 0 | oidc_test_hook_child_init_fn oidc_test_hook_child_init_get(void) { |
237 | 0 | return _oidc_test_hook_child_init; |
238 | 0 | } |
239 | | |
240 | | AP_DECLARE(void) |
241 | | ap_hook_post_config(int (*post_config)(apr_pool_t *pool, apr_pool_t *p1, apr_pool_t *p2, server_rec *s), |
242 | 0 | const char *const *aszPre, const char *const *aszSucc, int nOrder) { |
243 | 0 | _oidc_test_hook_post_config = post_config; |
244 | 0 | } |
245 | | |
246 | | AP_DECLARE(void) |
247 | | ap_hook_child_init(void (*child_init)(apr_pool_t *p, server_rec *s), const char *const *aszPre, |
248 | 0 | const char *const *aszSucc, int nOrder) { |
249 | 0 | _oidc_test_hook_child_init = child_init; |
250 | 0 | } |
251 | | |
252 | | AP_DECLARE(void) |
253 | 0 | ap_hook_handler(int (*handler)(request_rec *r), const char *const *aszPre, const char *const *aszSucc, int nOrder) { |
254 | | // comment explaining why the method is empty |
255 | 0 | } |
256 | | |
257 | 0 | AP_DECLARE(int) ap_is_initial_req(request_rec *r) { |
258 | | /* Match Apache: an initial request has neither main nor prev. */ |
259 | 0 | return (r->main == NULL) && (r->prev == NULL); |
260 | 0 | } |
261 | | |
262 | | AP_DECLARE(ap_expr_info_t *) |
263 | | ap_expr_parse_cmd_mi(const cmd_parms *cmd, const char *expr, unsigned int flags, const char **err, |
264 | 0 | ap_expr_lookup_fn_t *lookup_fn, int module_index) { |
265 | 0 | if (strcmp(expr, "#") == 0) { |
266 | 0 | *err = "error"; |
267 | 0 | return NULL; |
268 | 0 | } |
269 | 0 | ap_expr_info_t *rv = apr_pcalloc(cmd->pool, sizeof(ap_expr_info_t)); |
270 | | /* echo the expression through ap_expr_str_exec below so string-valued |
271 | | * expression directives behave like literal strings in the tests */ |
272 | 0 | rv->filename = apr_pstrdup(cmd->pool, expr); |
273 | 0 | rv->root_node = NULL; |
274 | 0 | return rv; |
275 | 0 | } |
276 | | |
277 | 0 | AP_DECLARE(const char *) ap_expr_str_exec(request_rec *r, const ap_expr_info_t *expr, const char **err) { |
278 | 0 | if (err) |
279 | 0 | *err = NULL; |
280 | 0 | return expr->filename; |
281 | 0 | } |
282 | | |
283 | | /* Return the primed first command-output line; an unprimed command fails with NULL. */ |
284 | 0 | AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p, const char *cmd, const char *const *argv) { |
285 | 0 | return (_oidc_test_exec_line_output == NULL) ? NULL : apr_pstrdup(p, _oidc_test_exec_line_output); |
286 | 0 | } |
287 | | |
288 | 0 | void oidc_test_exec_line_prime(const char *output) { |
289 | 0 | _oidc_test_exec_line_output = output; |
290 | 0 | } |
291 | | |
292 | | AP_DECLARE(void) |
293 | | ap_log_error_(const char *file, int line, int module_index, int level, apr_status_t status, const server_rec *s, |
294 | 0 | const char *fmt, ...) { |
295 | 0 | if (level < APLOG_DEBUG) { |
296 | 0 | fprintf(stderr, "%s:%d [%d] [%d] ", file, line, level, status); |
297 | 0 | va_list ap; |
298 | 0 | va_start(ap, fmt); |
299 | 0 | vfprintf(stderr, fmt, ap); |
300 | 0 | va_end(ap); |
301 | 0 | fprintf(stderr, "\n"); |
302 | 0 | } |
303 | 0 | } |
304 | | |
305 | | AP_DECLARE(void) |
306 | | ap_log_rerror_(const char *file, int line, int module_index, int level, apr_status_t status, const request_rec *r, |
307 | 407 | const char *fmt, ...) { |
308 | 407 | if (level < APLOG_DEBUG) { |
309 | 407 | fprintf(stderr, "%s:%d [%d] [%d] ", file, line, level, status); |
310 | 407 | va_list ap; |
311 | 407 | va_start(ap, fmt); |
312 | 407 | vfprintf(stderr, fmt, ap); |
313 | 407 | va_end(ap); |
314 | 407 | fprintf(stderr, "\n"); |
315 | 407 | } |
316 | 407 | } |
317 | | |
318 | 0 | AP_DECLARE(void) ap_note_auth_failure(request_rec *r) { |
319 | | // comment explaining why the method is empty |
320 | 0 | } |
321 | | |
322 | | /* capture the response body passed down the output filter chain in the request |
323 | | * state under "sent_body" so tests can assert on generated content; the test |
324 | | * fixture wires request->output_filters->r for this (see test/util.c) */ |
325 | | extern void oidc_request_state_set(request_rec *r, const char *key, const char *value); |
326 | | |
327 | 0 | AP_DECLARE(apr_status_t) ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket) { |
328 | 0 | char *buf = NULL; |
329 | 0 | apr_size_t len = 0; |
330 | 0 | if ((filter != NULL) && (filter->r != NULL) && |
331 | 0 | (apr_brigade_pflatten(bucket, &buf, &len, filter->r->pool) == APR_SUCCESS) && (len > 0)) |
332 | 0 | oidc_request_state_set(filter->r, "sent_body", apr_pstrmemdup(filter->r->pool, buf, len)); |
333 | 0 | return APR_SUCCESS; |
334 | 0 | } |
335 | | |
336 | 0 | AP_DECLARE(const apr_array_header_t *) ap_requires(request_rec *r) { |
337 | 0 | return NULL; |
338 | 0 | } |
339 | | |
340 | 0 | const char *ap_run_http_scheme(const request_rec *r) { |
341 | 0 | char *rv; |
342 | 0 | apr_pool_userdata_get((void **)&rv, "scheme", r->pool); |
343 | 0 | return (const char *)rv; |
344 | 0 | } |
345 | | |
346 | 0 | AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct) { |
347 | | // comment explaining why the method is empty |
348 | 0 | } |
349 | | |
350 | 0 | AP_DECLARE_NONSTD(const char *) ap_set_flag_slot(cmd_parms *cmd, void *struct_ptr, int arg) { |
351 | 0 | return ""; |
352 | 0 | } |
353 | | |
354 | 0 | AP_DECLARE_NONSTD(const char *) ap_set_string_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { |
355 | 0 | return ""; |
356 | 0 | } |
357 | | |
358 | 0 | AP_DECLARE_NONSTD(const char *) ap_set_int_slot(cmd_parms *cmd, void *struct_ptr, const char *arg) { |
359 | 0 | return ""; |
360 | 0 | } |
361 | | |
362 | 0 | AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy) { |
363 | 0 | return 0; |
364 | 0 | } |
365 | | |
366 | 0 | AP_DECLARE(int) ap_should_client_block(request_rec *r) { |
367 | 0 | return 1; |
368 | 0 | } |
369 | | |
370 | 0 | AP_DECLARE(int) ap_unescape_url(char *url) { |
371 | 0 | return 0; |
372 | 0 | } |
373 | | |
374 | 0 | AP_DECLARE(apr_status_t) unixd_set_global_mutex_perms(apr_global_mutex_t *gmutex) { |
375 | 0 | return APR_SUCCESS; |
376 | 0 | } |
377 | | |
378 | 0 | AP_DECLARE(const char *) ap_get_server_name(request_rec *r) { |
379 | 0 | return "www.example.com"; |
380 | 0 | } |
381 | | |
382 | 0 | AP_DECLARE(char *) ap_server_root_relative(apr_pool_t *p, const char *file) { |
383 | | /* Apache's real implementation prepends ServerRoot to relative paths; in tests |
384 | | * we treat the path as already-absolute (or relative-to-cwd) and return it verbatim */ |
385 | 0 | if (file == NULL) |
386 | 0 | return NULL; |
387 | 0 | return apr_pstrdup(p, file); |
388 | 0 | } |
389 | | |
390 | 0 | AP_DECLARE(ap_filter_t *) ap_add_input_filter(const char *name, void *ctx, request_rec *r, conn_rec *c) { |
391 | 0 | _oidc_test_added_input_filter = name; |
392 | 0 | return NULL; |
393 | 0 | } |
394 | | |
395 | 0 | const char *oidc_test_added_input_filter_get(void) { |
396 | 0 | return _oidc_test_added_input_filter; |
397 | 0 | } |
398 | | |
399 | 0 | void oidc_test_added_input_filter_reset(void) { |
400 | 0 | _oidc_test_added_input_filter = NULL; |
401 | 0 | } |
402 | | |
403 | | AP_DECLARE(apr_status_t) |
404 | | ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, |
405 | 0 | apr_off_t readbytes) { |
406 | | /* Return the primed request body followed by EOS; unprimed calls return an empty brigade. */ |
407 | 0 | if (_oidc_test_brigade_body != NULL) { |
408 | 0 | apr_bucket_alloc_t *ba = bucket->bucket_alloc; |
409 | 0 | APR_BRIGADE_INSERT_TAIL( |
410 | 0 | bucket, apr_bucket_heap_create(_oidc_test_brigade_body, strlen(_oidc_test_brigade_body), NULL, ba)); |
411 | 0 | APR_BRIGADE_INSERT_TAIL(bucket, apr_bucket_eos_create(ba)); |
412 | 0 | _oidc_test_brigade_body = NULL; |
413 | 0 | } |
414 | 0 | return _oidc_test_brigade_rc; |
415 | 0 | } |
416 | | |
417 | 0 | void oidc_test_brigade_prime(const char *body, apr_status_t rc) { |
418 | 0 | _oidc_test_brigade_body = body; |
419 | 0 | _oidc_test_brigade_rc = rc; |
420 | 0 | } |
421 | | |
422 | | AP_DECLARE(ap_filter_rec_t *) |
423 | | ap_register_input_filter(const char *name, ap_in_filter_func filter_func, ap_init_filter_func filter_init, |
424 | 0 | ap_filter_type ftype) { |
425 | 0 | _oidc_test_input_filter = filter_func; |
426 | 0 | return NULL; |
427 | 0 | } |
428 | | |
429 | 0 | oidc_test_input_filter_fn oidc_test_input_filter_get(void) { |
430 | 0 | return _oidc_test_input_filter; |
431 | 0 | } |
432 | | |
433 | 0 | oidc_test_hook_insert_filter_fn oidc_test_hook_insert_filter_get(void) { |
434 | 0 | return _oidc_test_hook_insert_filter; |
435 | 0 | } |
436 | | |
437 | 0 | AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s) { |
438 | 0 | return NULL; |
439 | 0 | } |
440 | | |
441 | 0 | AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) { |
442 | 0 | *result = 1; |
443 | 0 | return APR_SUCCESS; |
444 | 0 | } |
445 | | |
446 | | #if AP_MODULE_MAGIC_AT_LEAST(20080920, 2) |
447 | | AP_DECLARE(apr_status_t) |
448 | 0 | ap_timeout_parameter_parse(const char *timeout_parameter, apr_interval_time_t *timeout, const char *default_time_unit) { |
449 | 0 | char *endp; |
450 | 0 | const char *time_str; |
451 | 0 | apr_int64_t tout; |
452 | |
|
453 | 0 | tout = apr_strtoi64(timeout_parameter, &endp, 10); |
454 | 0 | if (!endp || !*endp) { |
455 | 0 | time_str = default_time_unit; |
456 | 0 | } else { |
457 | 0 | time_str = endp; |
458 | 0 | } |
459 | |
|
460 | 0 | switch (*time_str) { |
461 | | /* Time is in seconds */ |
462 | 0 | case 's': |
463 | 0 | *timeout = (apr_interval_time_t)apr_time_from_sec(tout); |
464 | 0 | break; |
465 | 0 | case 'h': |
466 | | /* Time is in hours */ |
467 | 0 | *timeout = (apr_interval_time_t)apr_time_from_sec(tout * 3600); |
468 | 0 | break; |
469 | 0 | case 'm': |
470 | 0 | switch (*(++time_str)) { |
471 | | /* Time is in milliseconds */ |
472 | 0 | case 's': |
473 | 0 | *timeout = (apr_interval_time_t)tout * 1000; |
474 | 0 | break; |
475 | | /* Time is in minutes */ |
476 | 0 | case 'i': |
477 | 0 | *timeout = (apr_interval_time_t)apr_time_from_sec(tout * 60); |
478 | 0 | break; |
479 | 0 | default: |
480 | 0 | return APR_EGENERAL; |
481 | 0 | } |
482 | 0 | break; |
483 | 0 | default: |
484 | 0 | return APR_EGENERAL; |
485 | 0 | } |
486 | 0 | return APR_SUCCESS; |
487 | 0 | } |
488 | | #endif |
489 | | |
490 | 0 | AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *expr, const char **err) { |
491 | 0 | *err = "error"; |
492 | 0 | return 1; |
493 | 0 | } |