summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.m42
-rw-r--r--src/php_snuffleupagus.h1
-rw-r--r--src/snuffleupagus.c15
-rw-r--r--src/sp_config.c21
-rw-r--r--src/sp_config.h12
-rw-r--r--src/sp_config_keywords.c20
-rw-r--r--src/sp_config_keywords.h1
-rw-r--r--src/sp_wrapper.c70
-rw-r--r--src/sp_wrapper.h8
-rw-r--r--src/tests/config/config_stream_wrapper.ini1
-rw-r--r--src/tests/config/config_stream_wrapper_register.ini1
-rw-r--r--src/tests/stream_wrapper.phpt31
-rw-r--r--src/tests/stream_wrapper_register.phpt25
-rw-r--r--src/tests/stream_wrapper_restore.phpt17
14 files changed, 216 insertions, 9 deletions
diff --git a/src/config.m4 b/src/config.m4
index 0165f87..52b6d04 100644
--- a/src/config.m4
+++ b/src/config.m4
@@ -6,7 +6,7 @@ sources="$sources sp_unserialize.c sp_utils.c sp_disable_xxe.c sp_list.c"
6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c" 6sources="$sources sp_disabled_functions.c sp_execute.c sp_upload_validation.c"
7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c" 7sources="$sources sp_cookie_encryption.c sp_network_utils.c tweetnacl.c"
8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c" 8sources="$sources sp_config_keywords.c sp_var_parser.c sp_var_value.c sp_tree.c"
9sources="$sources sp_pcre_compat.c sp_crypt.c sp_session.c sp_sloppy.c" 9sources="$sources sp_pcre_compat.c sp_crypt.c sp_session.c sp_sloppy.c sp_wrapper.c"
10 10
11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support, 11PHP_ARG_ENABLE(snuffleupagus, whether to enable snuffleupagus support,
12[ --enable-snuffleupagus Enable snuffleupagus support]) 12[ --enable-snuffleupagus Enable snuffleupagus support])
diff --git a/src/php_snuffleupagus.h b/src/php_snuffleupagus.h
index 96f9dd4..41d9b77 100644
--- a/src/php_snuffleupagus.h
+++ b/src/php_snuffleupagus.h
@@ -44,6 +44,7 @@
44#include "sp_crypt.h" 44#include "sp_crypt.h"
45#include "sp_session.h" 45#include "sp_session.h"
46#include "sp_sloppy.h" 46#include "sp_sloppy.h"
47#include "sp_wrapper.h"
47 48
48extern zend_module_entry snuffleupagus_module_entry; 49extern zend_module_entry snuffleupagus_module_entry;
49#define phpext_snuffleupagus_ptr &snuffleupagus_module_entry 50#define phpext_snuffleupagus_ptr &snuffleupagus_module_entry
diff --git a/src/snuffleupagus.c b/src/snuffleupagus.c
index ff0c6c3..1a92f11 100644
--- a/src/snuffleupagus.c
+++ b/src/snuffleupagus.c
@@ -95,6 +95,7 @@ PHP_GINIT_FUNCTION(snuffleupagus) {
95 SP_INIT(snuffleupagus_globals->config.config_cookie); 95 SP_INIT(snuffleupagus_globals->config.config_cookie);
96 SP_INIT(snuffleupagus_globals->config.config_session); 96 SP_INIT(snuffleupagus_globals->config.config_session);
97 SP_INIT(snuffleupagus_globals->config.config_eval); 97 SP_INIT(snuffleupagus_globals->config.config_eval);
98 SP_INIT(snuffleupagus_globals->config.config_wrapper);
98 99
99 snuffleupagus_globals->config.config_disabled_functions_reg 100 snuffleupagus_globals->config.config_disabled_functions_reg
100 ->disabled_functions = NULL; 101 ->disabled_functions = NULL;
@@ -103,6 +104,7 @@ PHP_GINIT_FUNCTION(snuffleupagus) {
103 snuffleupagus_globals->config.config_cookie->cookies = NULL; 104 snuffleupagus_globals->config.config_cookie->cookies = NULL;
104 snuffleupagus_globals->config.config_eval->blacklist = NULL; 105 snuffleupagus_globals->config.config_eval->blacklist = NULL;
105 snuffleupagus_globals->config.config_eval->whitelist = NULL; 106 snuffleupagus_globals->config.config_eval->whitelist = NULL;
107 snuffleupagus_globals->config.config_wrapper->whitelist = NULL;
106 108
107#undef SP_INIT 109#undef SP_INIT
108#undef SP_INIT_HT 110#undef SP_INIT_HT
@@ -160,12 +162,14 @@ PHP_MSHUTDOWN_FUNCTION(snuffleupagus) {
160 sp_list_free(SNUFFLEUPAGUS_G(config).config_cookie->cookies); 162 sp_list_free(SNUFFLEUPAGUS_G(config).config_cookie->cookies);
161 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->blacklist); 163 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->blacklist);
162 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->whitelist); 164 sp_list_free(SNUFFLEUPAGUS_G(config).config_eval->whitelist);
165 sp_list_free(SNUFFLEUPAGUS_G(config).config_wrapper->whitelist);
163 166
164#undef FREE_LST_DISABLE 167#undef FREE_LST_DISABLE
165 168
166 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_reg), 1); 169 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_reg), 1);
167 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_reg_ret), 1); 170 pefree(SNUFFLEUPAGUS_G(config.config_disabled_functions_reg_ret), 1);
168 pefree(SNUFFLEUPAGUS_G(config.config_cookie), 1); 171 pefree(SNUFFLEUPAGUS_G(config.config_cookie), 1);
172 pefree(SNUFFLEUPAGUS_G(config.config_wrapper), 1);
169 173
170 UNREGISTER_INI_ENTRIES(); 174 UNREGISTER_INI_ENTRIES();
171 175
@@ -176,6 +180,14 @@ PHP_RINIT_FUNCTION(snuffleupagus) {
176#if defined(COMPILE_DL_SNUFFLEUPAGUS) && defined(ZTS) 180#if defined(COMPILE_DL_SNUFFLEUPAGUS) && defined(ZTS)
177 ZEND_TSRMLS_CACHE_UPDATE(); 181 ZEND_TSRMLS_CACHE_UPDATE();
178#endif 182#endif
183
184 // We need to disable wrappers loaded by extensions loaded after SNUFFLEUPAGUS.
185 if (SNUFFLEUPAGUS_G(config).config_wrapper->enabled &&
186 zend_hash_num_elements(php_stream_get_url_stream_wrappers_hash()) !=
187 SNUFFLEUPAGUS_G(config).config_wrapper->num_wrapper) {
188 sp_disable_wrapper();
189 }
190
179 if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) { 191 if (NULL != SNUFFLEUPAGUS_G(config).config_snuffleupagus->encryption_key) {
180 if (NULL != SNUFFLEUPAGUS_G(config).config_cookie->cookies) { 192 if (NULL != SNUFFLEUPAGUS_G(config).config_cookie->cookies) {
181 zend_hash_apply_with_arguments( 193 zend_hash_apply_with_arguments(
@@ -243,6 +255,9 @@ static PHP_INI_MH(OnUpdateConfiguration) {
243 if (SNUFFLEUPAGUS_G(config).config_disable_xxe->enable == 0) { 255 if (SNUFFLEUPAGUS_G(config).config_disable_xxe->enable == 0) {
244 hook_libxml_disable_entity_loader(); 256 hook_libxml_disable_entity_loader();
245 } 257 }
258 if (SNUFFLEUPAGUS_G(config).config_wrapper->enabled) {
259 hook_stream_wrappers();
260 }
246 hook_disabled_functions(); 261 hook_disabled_functions();
247 hook_execute(); 262 hook_execute();
248 263
diff --git a/src/sp_config.c b/src/sp_config.c
index c652984..e5cb3ce 100644
--- a/src/sp_config.c
+++ b/src/sp_config.c
@@ -23,6 +23,7 @@ sp_config_tokens const sp_func[] = {
23 {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST}, 23 {.func = parse_eval_whitelist, .token = SP_TOKEN_EVAL_WHITELIST},
24 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION}, 24 {.func = parse_session, .token = SP_TOKEN_SESSION_ENCRYPTION},
25 {.func = parse_sloppy_comparison, .token = SP_TOKEN_SLOPPY_COMPARISON}, 25 {.func = parse_sloppy_comparison, .token = SP_TOKEN_SLOPPY_COMPARISON},
26 {.func = parse_wrapper_whitelist, .token = SP_TOKEN_ALLOW_WRAPPERS},
26 {NULL, NULL}}; 27 {NULL, NULL}};
27 28
28/* Top level keyword parsing */ 29/* Top level keyword parsing */
@@ -61,6 +62,26 @@ int parse_empty(char *restrict line, char *restrict keyword, void *retval) {
61 return 0; 62 return 0;
62} 63}
63 64
65int parse_list(char *restrict line, char *restrict keyword, void *list_ptr) {
66 zend_string *value = NULL;
67 sp_list_node **list = list_ptr;
68 char *token, *tmp;
69
70 size_t consumed = 0;
71 value = get_param(&consumed, line, SP_TYPE_STR, keyword);
72 if (!value) {
73 return -1;
74 }
75
76 tmp = ZSTR_VAL(value);
77 while ((token = strtok_r(tmp, ",", &tmp))) {
78 *list = sp_list_insert(*list, zend_string_init(token, strlen(token), 1));
79 }
80
81 pefree(value, 1);
82 return consumed;
83}
84
64int parse_php_type(char *restrict line, char *restrict keyword, void *retval) { 85int parse_php_type(char *restrict line, char *restrict keyword, void *retval) {
65 size_t consumed = 0; 86 size_t consumed = 0;
66 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword); 87 zend_string *value = get_param(&consumed, line, SP_TYPE_STR, keyword);
diff --git a/src/sp_config.h b/src/sp_config.h
index d2fa64f..9d58359 100644
--- a/src/sp_config.h
+++ b/src/sp_config.h
@@ -78,6 +78,12 @@ typedef struct {
78} sp_cookie; 78} sp_cookie;
79 79
80typedef struct { 80typedef struct {
81 sp_list_node *whitelist;
82 bool enabled;
83 size_t num_wrapper; // Used to verify if wrappers were added.
84} sp_config_wrapper;
85
86typedef struct {
81 bool encrypt; 87 bool encrypt;
82 bool simulation; 88 bool simulation;
83} sp_config_session; 89} sp_config_session;
@@ -166,6 +172,7 @@ typedef struct {
166 sp_config_global_strict *config_global_strict; 172 sp_config_global_strict *config_global_strict;
167 sp_config_disable_xxe *config_disable_xxe; 173 sp_config_disable_xxe *config_disable_xxe;
168 sp_config_eval *config_eval; 174 sp_config_eval *config_eval;
175 sp_config_wrapper *config_wrapper;
169 sp_config_session *config_session; 176 sp_config_session *config_session;
170 bool hook_execute; 177 bool hook_execute;
171 178
@@ -204,6 +211,7 @@ typedef struct {
204#define SP_TOKEN_EVAL_BLACKLIST ".eval_blacklist" 211#define SP_TOKEN_EVAL_BLACKLIST ".eval_blacklist"
205#define SP_TOKEN_EVAL_WHITELIST ".eval_whitelist" 212#define SP_TOKEN_EVAL_WHITELIST ".eval_whitelist"
206#define SP_TOKEN_SLOPPY_COMPARISON ".sloppy_comparison" 213#define SP_TOKEN_SLOPPY_COMPARISON ".sloppy_comparison"
214#define SP_TOKEN_ALLOW_WRAPPERS ".wrappers_whitelist"
207 215
208// common tokens 216// common tokens
209#define SP_TOKEN_ENABLE ".enable(" 217#define SP_TOKEN_ENABLE ".enable("
@@ -256,8 +264,7 @@ typedef struct {
256// upload_validator 264// upload_validator
257#define SP_TOKEN_UPLOAD_SCRIPT ".script(" 265#define SP_TOKEN_UPLOAD_SCRIPT ".script("
258 266
259// eval blacklist 267#define SP_TOKEN_LIST ".list("
260#define SP_TOKEN_EVAL_LIST ".list("
261 268
262int sp_parse_config(const char *); 269int sp_parse_config(const char *);
263int parse_array(sp_disabled_function *); 270int parse_array(sp_disabled_function *);
@@ -267,6 +274,7 @@ int parse_regexp(char *restrict, char *restrict, void *);
267int parse_empty(char *restrict, char *restrict, void *); 274int parse_empty(char *restrict, char *restrict, void *);
268int parse_cidr(char *restrict, char *restrict, void *); 275int parse_cidr(char *restrict, char *restrict, void *);
269int parse_php_type(char *restrict, char *restrict, void *); 276int parse_php_type(char *restrict, char *restrict, void *);
277int parse_list(char *restrict, char *restrict, void *);
270 278
271// cleanup 279// cleanup
272void sp_disabled_function_list_free(sp_list_node *); 280void sp_disabled_function_list_free(sp_list_node *);
diff --git a/src/sp_config_keywords.c b/src/sp_config_keywords.c
index fb20a43..b86b9d9 100644
--- a/src/sp_config_keywords.c
+++ b/src/sp_config_keywords.c
@@ -166,12 +166,11 @@ int parse_global(char *line) {
166} 166}
167 167
168static int parse_eval_filter_conf(char *line, sp_list_node **list) { 168static int parse_eval_filter_conf(char *line, sp_list_node **list) {
169 char *token, *tmp;
170 zend_string *rest = NULL; 169 zend_string *rest = NULL;
171 sp_config_eval *eval = SNUFFLEUPAGUS_G(config).config_eval; 170 sp_config_eval *eval = SNUFFLEUPAGUS_G(config).config_eval;
172 171
173 sp_config_functions sp_config_funcs[] = { 172 sp_config_functions sp_config_funcs[] = {
174 {parse_str, SP_TOKEN_EVAL_LIST, &rest}, 173 {parse_list, SP_TOKEN_LIST, list},
175 {parse_empty, SP_TOKEN_SIMULATION, 174 {parse_empty, SP_TOKEN_SIMULATION,
176 &(SNUFFLEUPAGUS_G(config).config_eval->simulation)}, 175 &(SNUFFLEUPAGUS_G(config).config_eval->simulation)},
177 {parse_str, SP_TOKEN_DUMP, &(SNUFFLEUPAGUS_G(config).config_eval->dump)}, 176 {parse_str, SP_TOKEN_DUMP, &(SNUFFLEUPAGUS_G(config).config_eval->dump)},
@@ -184,16 +183,25 @@ static int parse_eval_filter_conf(char *line, sp_list_node **list) {
184 return ret; 183 return ret;
185 } 184 }
186 185
187 tmp = ZSTR_VAL(rest);
188 while ((token = strtok_r(tmp, ",", &tmp))) {
189 *list = sp_list_insert(*list, zend_string_init(token, strlen(token), 1));
190 }
191 if (rest != NULL) { 186 if (rest != NULL) {
192 pefree(rest, 1); 187 pefree(rest, 1);
193 } 188 }
194 return SUCCESS; 189 return SUCCESS;
195} 190}
196 191
192int parse_wrapper_whitelist(char *line) {
193 SNUFFLEUPAGUS_G(config).config_wrapper->enabled = true;
194 sp_config_functions sp_config_funcs[] = {
195 {parse_list, SP_TOKEN_LIST,
196 &SNUFFLEUPAGUS_G(config).config_wrapper->whitelist},
197 {0}};
198 int ret = parse_keywords(sp_config_funcs, line);
199 if (0 != ret) {
200 return ret;
201 }
202 return SUCCESS;
203}
204
197int parse_eval_blacklist(char *line) { 205int parse_eval_blacklist(char *line) {
198 return parse_eval_filter_conf( 206 return parse_eval_filter_conf(
199 line, &SNUFFLEUPAGUS_G(config).config_eval->blacklist); 207 line, &SNUFFLEUPAGUS_G(config).config_eval->blacklist);
diff --git a/src/sp_config_keywords.h b/src/sp_config_keywords.h
index 36c66a5..ab58456 100644
--- a/src/sp_config_keywords.h
+++ b/src/sp_config_keywords.h
@@ -16,5 +16,6 @@ int parse_eval_blacklist(char *line);
16int parse_eval_whitelist(char *line); 16int parse_eval_whitelist(char *line);
17int parse_session(char *line); 17int parse_session(char *line);
18int parse_sloppy_comparison(char *line); 18int parse_sloppy_comparison(char *line);
19int parse_wrapper_whitelist(char *line);
19 20
20#endif // __SP_CONFIG_KEYWORDS_H 21#endif // __SP_CONFIG_KEYWORDS_H
diff --git a/src/sp_wrapper.c b/src/sp_wrapper.c
new file mode 100644
index 0000000..d9cd296
--- /dev/null
+++ b/src/sp_wrapper.c
@@ -0,0 +1,70 @@
1#include "php_snuffleupagus.h"
2#include "sp_config.h"
3
4ZEND_DECLARE_MODULE_GLOBALS(snuffleupagus)
5
6static bool wrapper_is_whitelisted(const zend_string *zs) {
7 const sp_list_node *list = SNUFFLEUPAGUS_G(config).config_wrapper->whitelist;
8
9 if (!zs) {
10 return false;
11 }
12
13 while (list) {
14 if (zend_string_equals_ci(zs, (const zend_string*)list->data)) {
15 return true;
16 }
17 list = list->next;
18 }
19 return false;
20}
21
22void sp_disable_wrapper() {
23 HashTable *orig = php_stream_get_url_stream_wrappers_hash();
24 HashTable *orig_complete = pemalloc(sizeof(*orig_complete), 1);
25 zval *zv;
26 zend_string *zs;
27
28 // Copy the original hashtable into a temporary one, as I'm not sure about
29 // the behaviour of ZEND_HASH_FOREACH when element are removed from the
30 // hashtable used in the loop.
31 zend_hash_init(orig_complete, zend_hash_num_elements(orig), NULL, NULL, 1);
32 zend_hash_copy(orig_complete, orig, NULL);
33 zend_hash_clean(orig);
34
35 ZEND_HASH_FOREACH_STR_KEY_VAL(orig_complete, zs, zv) {
36 if (wrapper_is_whitelisted(zs)) {
37 zend_hash_add(orig, zs, zv);
38 }
39 }
40 ZEND_HASH_FOREACH_END();
41
42 zend_hash_destroy(orig_complete);
43 pefree(orig_complete, 1);
44 SNUFFLEUPAGUS_G(config).config_wrapper->num_wrapper = zend_hash_num_elements(orig);
45}
46
47PHP_FUNCTION(sp_stream_wrapper_register) {
48 void (*orig_handler)(INTERNAL_FUNCTION_PARAMETERS);
49 zend_string *protocol_name = NULL;
50
51 ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_QUIET, 2, EX_NUM_ARGS());
52 Z_PARAM_STR(protocol_name);
53 ZEND_PARSE_PARAMETERS_END_EX((void)0);
54
55 if (wrapper_is_whitelisted(protocol_name)) {
56 orig_handler = zend_hash_str_find_ptr(
57 SNUFFLEUPAGUS_G(sp_internal_functions_hook),
58 "stream_wrapper_register", sizeof("stream_wrapper_register") - 1);
59 orig_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
60 }
61}
62
63int hook_stream_wrappers() {
64 TSRMLS_FETCH();
65
66 HOOK_FUNCTION("stream_wrapper_register", sp_internal_functions_hook,
67 PHP_FN(sp_stream_wrapper_register));
68
69 return SUCCESS;
70}
diff --git a/src/sp_wrapper.h b/src/sp_wrapper.h
new file mode 100644
index 0000000..f57513a
--- /dev/null
+++ b/src/sp_wrapper.h
@@ -0,0 +1,8 @@
1#ifndef SP_WRAPPER_H
2#define SP_WRAPPER_H
3#include "php_snuffleupagus.h"
4
5void sp_disable_wrapper();
6int hook_stream_wrappers();
7
8#endif
diff --git a/src/tests/config/config_stream_wrapper.ini b/src/tests/config/config_stream_wrapper.ini
new file mode 100644
index 0000000..0cd7f77
--- /dev/null
+++ b/src/tests/config/config_stream_wrapper.ini
@@ -0,0 +1 @@
sp.wrappers_whitelist.list("https,FTP,does_not_exist");
diff --git a/src/tests/config/config_stream_wrapper_register.ini b/src/tests/config/config_stream_wrapper_register.ini
new file mode 100644
index 0000000..ee273a1
--- /dev/null
+++ b/src/tests/config/config_stream_wrapper_register.ini
@@ -0,0 +1 @@
sp.wrappers_whitelist.list("php,lelel");
diff --git a/src/tests/stream_wrapper.phpt b/src/tests/stream_wrapper.phpt
new file mode 100644
index 0000000..b4e49a0
--- /dev/null
+++ b/src/tests/stream_wrapper.phpt
@@ -0,0 +1,31 @@
1--TEST--
2Stream wrapper
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_stream_wrapper.ini
7--FILE--
8<?php
9file_get_contents('http://qweqwezxc');
10file_get_contents('https://qweqwezxc');
11file_get_contents('ftp://qweqwezxc');
12file_get_contents('lelel://qweqwezxc');
13?>
14--EXPECTF--
15Warning: Unknown: Unable to find the wrapper "php" - did you forget to enable it when you configured PHP? in Unknown on line 0
16
17Warning: Unknown: Unable to find the wrapper "php" - did you forget to enable it when you configured PHP? in Unknown on line 0
18
19Warning: Unknown: Unable to find the wrapper "php" - did you forget to enable it when you configured PHP? in Unknown on line 0
20
21Warning: file_get_contents(): Unable to find the wrapper "http" - did you forget to enable it when you configured PHP? in %a/tests/stream_wrapper.php on line %d
22
23Warning: file_get_contents(): php_network_getaddresses: getaddrinfo failed: Name or service not known in %a/tests/stream_wrapper.php on line %d
24
25Warning: file_get_contents(https://qweqwezxc): failed to open stream: php_network_getaddresses: getaddrinfo failed: Name or service not known in %a/tests/stream_wrapper.php on line %d
26
27Warning: file_get_contents(ftp://qweqwezxc): failed to open stream: operation failed in %a/tests/stream_wrapper.php on line %d
28
29Warning: file_get_contents(): Unable to find the wrapper "lelel" - did you forget to enable it when you configured PHP? in %a/tests/stream_wrapper.php on line %d
30
31Warning: file_get_contents(lelel://qweqwezxc): failed to open stream: No such file or directory in %a/tests/stream_wrapper.php on line %d
diff --git a/src/tests/stream_wrapper_register.phpt b/src/tests/stream_wrapper_register.phpt
new file mode 100644
index 0000000..31e53ea
--- /dev/null
+++ b/src/tests/stream_wrapper_register.phpt
@@ -0,0 +1,25 @@
1--TEST--
2Stream wrapper
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_stream_wrapper_register.ini
7--FILE--
8<?php
9class qwe {
10 function stream_open($fname) {
11 return $fname;
12 }
13}
14
15stream_wrapper_register("lelel", "qwe");
16stream_wrapper_register("lolol", "qwe");
17fopen("lelel://asdasd", "r");
18fopen("lolol://asdasd", "r");
19?>
20--EXPECTF--
21Warning: fopen(): Unable to find the wrapper "lolol" - did you forget to enable it when you configured PHP? in %a/tests/stream_wrapper_register.php on line %d
22
23Warning: fopen(): file:// wrapper is disabled in the server configuration in %a/tests/stream_wrapper_register.php on line %d
24
25Warning: fopen(lolol://asdasd): failed to open stream: no suitable wrapper could be found in %a/tests/stream_wrapper_register.php on line %d
diff --git a/src/tests/stream_wrapper_restore.phpt b/src/tests/stream_wrapper_restore.phpt
new file mode 100644
index 0000000..c1bce3b
--- /dev/null
+++ b/src/tests/stream_wrapper_restore.phpt
@@ -0,0 +1,17 @@
1--TEST--
2Stream wrapper
3--SKIPIF--
4<?php if (!extension_loaded("snuffleupagus")) print "skip"; ?>
5--INI--
6sp.configuration_file={PWD}/config/config_stream_wrapper_register.ini
7--FILE--
8<?php
9stream_wrapper_restore("file");
10fopen("file://asdasd", "r");
11?>
12--EXPECTF--
13Notice: stream_wrapper_restore(): file:// was never changed, nothing to restore in %a/tests/stream_wrapper_restore.php on line %d
14
15Warning: fopen(): Unable to find the wrapper "file" - did you forget to enable it when you configured PHP? in %a/tests/stream_wrapper_restore.php on line %d
16
17Warning: fopen(file://asdasd): failed to open stream: No such file or directory in %a/tests/stream_wrapper_restore.php on line %d