de-dup THREAD_LOCAL macros
[hiphop-php.git] / hphp / runtime / ext / filter / ext_filter.cpp
bloba88d1a6431e9be27883f837d0d7677ffa072ee00
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/ext/filter/ext_filter.h"
20 #include "hphp/runtime/ext/filter/logical_filters.h"
21 #include "hphp/runtime/ext/filter/sanitizing_filters.h"
23 #include "hphp/runtime/base/array-init.h"
24 #include "hphp/runtime/base/comparisons.h"
25 #include "hphp/runtime/base/init-fini-node.h"
26 #include "hphp/runtime/base/request-local.h"
27 #include "hphp/runtime/base/php-globals.h"
29 namespace HPHP {
31 ///////////////////////////////////////////////////////////////////////////////
33 const int64_t k_INPUT_POST = 0;
34 const int64_t k_INPUT_GET = 1;
35 const int64_t k_INPUT_COOKIE = 2;
36 const int64_t k_INPUT_ENV = 4;
37 const int64_t k_INPUT_SERVER = 5;
38 const int64_t k_INPUT_SESSION = 6;
39 const int64_t k_INPUT_REQUEST = 99;
40 const int64_t k_FILTER_FLAG_NONE = 0;
41 const int64_t k_FILTER_REQUIRE_SCALAR = 33554432;
42 const int64_t k_FILTER_REQUIRE_ARRAY = 16777216;
43 const int64_t k_FILTER_FORCE_ARRAY = 67108864;
44 const int64_t k_FILTER_NULL_ON_FAILURE = 134217728;
45 const int64_t k_FILTER_VALIDATE_INT = 257;
46 const int64_t k_FILTER_VALIDATE_BOOLEAN = 258;
47 const int64_t k_FILTER_VALIDATE_FLOAT = 259;
48 const int64_t k_FILTER_VALIDATE_REGEXP = 272;
49 const int64_t k_FILTER_VALIDATE_URL = 273;
50 const int64_t k_FILTER_VALIDATE_EMAIL = 274;
51 const int64_t k_FILTER_VALIDATE_IP = 275;
52 const int64_t k_FILTER_VALIDATE_MAC = 276;
53 const int64_t k_FILTER_DEFAULT = 516;
54 const int64_t k_FILTER_UNSAFE_RAW = 516;
55 const int64_t k_FILTER_SANITIZE_STRING = 513;
56 const int64_t k_FILTER_SANITIZE_STRIPPED = 513;
57 const int64_t k_FILTER_SANITIZE_ENCODED = 514;
58 const int64_t k_FILTER_SANITIZE_SPECIAL_CHARS = 515;
59 const int64_t k_FILTER_SANITIZE_FULL_SPECIAL_CHARS = 515;
60 const int64_t k_FILTER_SANITIZE_EMAIL = 517;
61 const int64_t k_FILTER_SANITIZE_URL = 518;
62 const int64_t k_FILTER_SANITIZE_NUMBER_INT = 519;
63 const int64_t k_FILTER_SANITIZE_NUMBER_FLOAT = 520;
64 const int64_t k_FILTER_SANITIZE_MAGIC_QUOTES = 521;
65 const int64_t k_FILTER_CALLBACK = 1024;
66 const int64_t k_FILTER_FLAG_ALLOW_OCTAL = 1;
67 const int64_t k_FILTER_FLAG_ALLOW_HEX = 2;
68 const int64_t k_FILTER_FLAG_STRIP_LOW = 4;
69 const int64_t k_FILTER_FLAG_STRIP_HIGH = 8;
70 const int64_t k_FILTER_FLAG_ENCODE_LOW = 16;
71 const int64_t k_FILTER_FLAG_ENCODE_HIGH = 32;
72 const int64_t k_FILTER_FLAG_ENCODE_AMP = 64;
73 const int64_t k_FILTER_FLAG_NO_ENCODE_QUOTES = 128;
74 const int64_t k_FILTER_FLAG_EMPTY_STRING_NULL = 256;
75 const int64_t k_FILTER_FLAG_STRIP_BACKTICK = 512;
76 const int64_t k_FILTER_FLAG_ALLOW_FRACTION = 4096;
77 const int64_t k_FILTER_FLAG_ALLOW_THOUSAND = 8192;
78 const int64_t k_FILTER_FLAG_ALLOW_SCIENTIFIC = 16384;
79 const int64_t k_FILTER_FLAG_SCHEME_REQUIRED = 65536;
80 const int64_t k_FILTER_FLAG_HOST_REQUIRED = 131072;
81 const int64_t k_FILTER_FLAG_PATH_REQUIRED = 262144;
82 const int64_t k_FILTER_FLAG_QUERY_REQUIRED = 524288;
83 const int64_t k_FILTER_FLAG_IPV4 = 1048576;
84 const int64_t k_FILTER_FLAG_IPV6 = 2097152;
85 const int64_t k_FILTER_FLAG_NO_RES_RANGE = 4194304;
86 const int64_t k_FILTER_FLAG_NO_PRIV_RANGE = 8388608;
88 const StaticString
89 s_GET("_GET"),
90 s_POST("_POST"),
91 s_COOKIE("_COOKIE"),
92 s_SERVER("_SERVER"),
93 s_ENV("_ENV");
95 struct FilterRequestData final {
96 void requestInit() {
97 // This doesn't copy them yet, but will do COW if they are modified
98 m_GET = php_global(s_GET).toArray();
99 m_POST = php_global(s_POST).toArray();
100 m_COOKIE = php_global(s_COOKIE).toArray();
101 m_SERVER = php_global(s_SERVER).toArray();
102 m_ENV = php_global(s_ENV).toArray();
105 void requestShutdown() {
106 m_GET.detach();
107 m_POST.detach();
108 m_COOKIE.detach();
109 m_SERVER.detach();
110 m_ENV.detach();
113 Array getVar(int64_t type) {
114 switch (type) {
115 case k_INPUT_GET: return m_GET;
116 case k_INPUT_POST: return m_POST;
117 case k_INPUT_COOKIE: return m_COOKIE;
118 case k_INPUT_SERVER: return m_SERVER;
119 case k_INPUT_ENV: return m_ENV;
121 return empty_array();
124 private:
125 Array m_GET;
126 Array m_POST;
127 Array m_COOKIE;
128 Array m_SERVER;
129 Array m_ENV;
131 THREAD_LOCAL_NO_CHECK(FilterRequestData, s_filter_request_data);
133 static struct FilterExtension final : Extension {
134 FilterExtension() : Extension("filter", "0.11.0") {}
136 void moduleLoad(const IniSetting::Map& /*ini*/, Hdf /*config*/) override {
137 HHVM_FE(__SystemLib_filter_input_get_var);
138 HHVM_FE(_filter_snapshot_globals);
141 void moduleInit() override {
142 HHVM_RC_INT(INPUT_POST, k_INPUT_POST);
143 HHVM_RC_INT(INPUT_GET, k_INPUT_GET);
144 HHVM_RC_INT(INPUT_COOKIE, k_INPUT_COOKIE);
145 HHVM_RC_INT(INPUT_ENV, k_INPUT_ENV);
146 HHVM_RC_INT(INPUT_SERVER, k_INPUT_SERVER);
147 HHVM_RC_INT(INPUT_SESSION, k_INPUT_SESSION);
148 HHVM_RC_INT(INPUT_REQUEST, k_INPUT_REQUEST);
149 HHVM_RC_INT(FILTER_FLAG_NONE, k_FILTER_FLAG_NONE);
150 HHVM_RC_INT(FILTER_REQUIRE_SCALAR, k_FILTER_REQUIRE_SCALAR);
151 HHVM_RC_INT(FILTER_REQUIRE_ARRAY, k_FILTER_REQUIRE_ARRAY);
152 HHVM_RC_INT(FILTER_FORCE_ARRAY, k_FILTER_FORCE_ARRAY);
153 HHVM_RC_INT(FILTER_NULL_ON_FAILURE, k_FILTER_NULL_ON_FAILURE);
154 HHVM_RC_INT(FILTER_VALIDATE_INT, k_FILTER_VALIDATE_INT);
155 HHVM_RC_INT(FILTER_VALIDATE_BOOLEAN, k_FILTER_VALIDATE_BOOLEAN);
156 HHVM_RC_INT(FILTER_VALIDATE_FLOAT, k_FILTER_VALIDATE_FLOAT);
157 HHVM_RC_INT(FILTER_VALIDATE_REGEXP, k_FILTER_VALIDATE_REGEXP);
158 HHVM_RC_INT(FILTER_VALIDATE_URL, k_FILTER_VALIDATE_URL);
159 HHVM_RC_INT(FILTER_VALIDATE_EMAIL, k_FILTER_VALIDATE_EMAIL);
160 HHVM_RC_INT(FILTER_VALIDATE_IP, k_FILTER_VALIDATE_IP);
161 HHVM_RC_INT(FILTER_VALIDATE_MAC, k_FILTER_VALIDATE_MAC);
162 HHVM_RC_INT(FILTER_DEFAULT, k_FILTER_DEFAULT);
163 HHVM_RC_INT(FILTER_UNSAFE_RAW, k_FILTER_UNSAFE_RAW);
164 HHVM_RC_INT(FILTER_SANITIZE_STRING, k_FILTER_SANITIZE_STRING);
165 HHVM_RC_INT(FILTER_SANITIZE_STRIPPED, k_FILTER_SANITIZE_STRIPPED);
166 HHVM_RC_INT(FILTER_SANITIZE_ENCODED, k_FILTER_SANITIZE_ENCODED);
167 HHVM_RC_INT(FILTER_SANITIZE_SPECIAL_CHARS, k_FILTER_SANITIZE_SPECIAL_CHARS);
168 HHVM_RC_INT(FILTER_SANITIZE_FULL_SPECIAL_CHARS,
169 k_FILTER_SANITIZE_FULL_SPECIAL_CHARS);
170 HHVM_RC_INT(FILTER_SANITIZE_EMAIL, k_FILTER_SANITIZE_EMAIL);
171 HHVM_RC_INT(FILTER_SANITIZE_URL, k_FILTER_SANITIZE_URL);
172 HHVM_RC_INT(FILTER_SANITIZE_NUMBER_INT, k_FILTER_SANITIZE_NUMBER_INT);
173 HHVM_RC_INT(FILTER_SANITIZE_NUMBER_FLOAT, k_FILTER_SANITIZE_NUMBER_FLOAT);
174 HHVM_RC_INT(FILTER_SANITIZE_MAGIC_QUOTES, k_FILTER_SANITIZE_MAGIC_QUOTES);
175 HHVM_RC_INT(FILTER_CALLBACK, k_FILTER_CALLBACK);
176 HHVM_RC_INT(FILTER_FLAG_ALLOW_OCTAL, k_FILTER_FLAG_ALLOW_OCTAL);
177 HHVM_RC_INT(FILTER_FLAG_ALLOW_HEX, k_FILTER_FLAG_ALLOW_HEX);
178 HHVM_RC_INT(FILTER_FLAG_STRIP_LOW, k_FILTER_FLAG_STRIP_LOW);
179 HHVM_RC_INT(FILTER_FLAG_STRIP_HIGH, k_FILTER_FLAG_STRIP_HIGH);
180 HHVM_RC_INT(FILTER_FLAG_ENCODE_LOW, k_FILTER_FLAG_ENCODE_LOW);
181 HHVM_RC_INT(FILTER_FLAG_ENCODE_HIGH, k_FILTER_FLAG_ENCODE_HIGH);
182 HHVM_RC_INT(FILTER_FLAG_ENCODE_AMP, k_FILTER_FLAG_ENCODE_AMP);
183 HHVM_RC_INT(FILTER_FLAG_NO_ENCODE_QUOTES, k_FILTER_FLAG_NO_ENCODE_QUOTES);
184 HHVM_RC_INT(FILTER_FLAG_EMPTY_STRING_NULL, k_FILTER_FLAG_EMPTY_STRING_NULL);
185 HHVM_RC_INT(FILTER_FLAG_STRIP_BACKTICK, k_FILTER_FLAG_STRIP_BACKTICK);
186 HHVM_RC_INT(FILTER_FLAG_ALLOW_FRACTION, k_FILTER_FLAG_ALLOW_FRACTION);
187 HHVM_RC_INT(FILTER_FLAG_ALLOW_THOUSAND, k_FILTER_FLAG_ALLOW_THOUSAND);
188 HHVM_RC_INT(FILTER_FLAG_ALLOW_SCIENTIFIC, k_FILTER_FLAG_ALLOW_SCIENTIFIC);
189 HHVM_RC_INT(FILTER_FLAG_SCHEME_REQUIRED, k_FILTER_FLAG_SCHEME_REQUIRED);
190 HHVM_RC_INT(FILTER_FLAG_HOST_REQUIRED, k_FILTER_FLAG_HOST_REQUIRED);
191 HHVM_RC_INT(FILTER_FLAG_PATH_REQUIRED, k_FILTER_FLAG_PATH_REQUIRED);
192 HHVM_RC_INT(FILTER_FLAG_QUERY_REQUIRED, k_FILTER_FLAG_QUERY_REQUIRED);
193 HHVM_RC_INT(FILTER_FLAG_IPV4, k_FILTER_FLAG_IPV4);
194 HHVM_RC_INT(FILTER_FLAG_IPV6, k_FILTER_FLAG_IPV6);
195 HHVM_RC_INT(FILTER_FLAG_NO_RES_RANGE, k_FILTER_FLAG_NO_RES_RANGE);
196 HHVM_RC_INT(FILTER_FLAG_NO_PRIV_RANGE, k_FILTER_FLAG_NO_PRIV_RANGE);
198 HHVM_FE(filter_list);
199 HHVM_FE(filter_id);
200 HHVM_FE(filter_var);
202 loadSystemlib();
205 void threadInit() override {
206 s_filter_request_data.getCheck();
209 void requestShutdown() override {
210 // warm up the s_filter_request_data
211 s_filter_request_data->requestShutdown();
213 } s_filter_extension;
215 namespace {
216 InitFiniNode globalsInit([]() { s_filter_request_data->requestInit(); },
217 InitFiniNode::When::GlobalsInit);
220 ///////////////////////////////////////////////////////////////////////////////
222 typedef struct filter_list_entry {
223 StaticString name;
224 int64_t id;
225 Variant (*function)(PHP_INPUT_FILTER_PARAM_DECL);
226 } filter_list_entry;
228 static const filter_list_entry filter_list[] = {
230 StaticString("int"),
231 k_FILTER_VALIDATE_INT,
232 php_filter_int
233 }, {
234 StaticString("boolean"),
235 k_FILTER_VALIDATE_BOOLEAN,
236 php_filter_boolean
237 }, {
238 StaticString("float"),
239 k_FILTER_VALIDATE_FLOAT,
240 php_filter_float
241 }, {
242 StaticString("validate_regexp"),
243 k_FILTER_VALIDATE_REGEXP,
244 php_filter_validate_regexp
245 }, {
246 StaticString("validate_url"),
247 k_FILTER_VALIDATE_URL,
248 php_filter_validate_url
249 }, {
250 StaticString("validate_email"),
251 k_FILTER_VALIDATE_EMAIL,
252 php_filter_validate_email
253 }, {
254 StaticString("validate_ip"),
255 k_FILTER_VALIDATE_IP,
256 php_filter_validate_ip
257 }, {
258 StaticString("validate_mac"),
259 k_FILTER_VALIDATE_MAC,
260 php_filter_validate_mac
261 }, {
262 StaticString("string"),
263 k_FILTER_SANITIZE_STRING,
264 php_filter_string
265 }, {
266 StaticString("stripped"),
267 k_FILTER_SANITIZE_STRING,
268 php_filter_string
269 }, {
270 StaticString("encoded"),
271 k_FILTER_SANITIZE_ENCODED,
272 php_filter_encoded
273 }, {
274 StaticString("special_chars"),
275 k_FILTER_SANITIZE_SPECIAL_CHARS,
276 php_filter_special_chars
277 }, {
278 StaticString("full_special_chars"),
279 k_FILTER_SANITIZE_FULL_SPECIAL_CHARS,
280 php_filter_full_special_chars
281 }, {
282 StaticString("unsafe_raw"),
283 k_FILTER_UNSAFE_RAW,
284 php_filter_unsafe_raw
285 }, {
286 StaticString("email"),
287 k_FILTER_SANITIZE_EMAIL,
288 php_filter_email
289 }, {
290 StaticString("url"),
291 k_FILTER_SANITIZE_URL,
292 php_filter_url
293 }, {
294 StaticString("number_int"),
295 k_FILTER_SANITIZE_NUMBER_INT,
296 php_filter_number_int
297 }, {
298 StaticString("number_float"),
299 k_FILTER_SANITIZE_NUMBER_FLOAT,
300 php_filter_number_float
301 }, {
302 StaticString("magic_quotes"),
303 k_FILTER_SANITIZE_MAGIC_QUOTES,
304 php_filter_magic_quotes
305 }, {
306 StaticString("callback"),
307 k_FILTER_CALLBACK,
308 php_filter_callback
312 const StaticString
313 s_flags("flags"),
314 s_default("default"),
315 s_options("options");
317 static Variant fail(bool return_null, const Variant& options) {
318 if (options.isArray()) {
319 const Array& arr(options.toArray());
320 if (arr.exists(s_default)) {
321 return arr[s_default];
324 if (return_null) {
325 return init_null();
327 return false;
330 static const filter_list_entry* php_find_filter(uint64_t id) {
331 int i, size = sizeof(filter_list) / sizeof(filter_list_entry);
333 for (i = 0; i < size; ++i) {
334 if (filter_list[i].id == id) {
335 return &filter_list[i];
339 return nullptr;
342 #define FAIL_IF(x) do { if (x) return false; } while (0)
344 static bool filter_var(Variant& ret, const Variant& variable, int64_t filter,
345 const Variant& options) {
346 const filter_list_entry* filter_func = php_find_filter(filter);
347 if (!filter_func) {
348 return false;
351 int64_t flags;
352 Variant option_array;
353 if (options.isArray()) {
354 auto arr = options.toArray();
355 flags = arr[s_flags].toInt64();
356 option_array = arr[s_options];
357 } else {
358 flags = options.toInt64();
361 FAIL_IF(variable.isObject() && !variable.getObjectData()->hasToString());
363 ret = filter_func->function(variable.toString(), flags, option_array);
364 if (option_array.isArray() && option_array.toArray().exists(s_default) &&
365 ((flags & k_FILTER_NULL_ON_FAILURE && ret.isNull()) ||
366 (!(flags & k_FILTER_NULL_ON_FAILURE) && ret.isBoolean() &&
367 ret.asBooleanVal() == 0))) {
368 ret = option_array.toArray()[s_default];
370 return true;
373 static bool filter_recursive(Variant& ret, const Variant& variable, int64_t filter,
374 const Variant& options) {
375 Array arr = Array::Create();
376 for (ArrayIter iter(variable.toArray()); iter; ++iter) {
377 Variant v;
378 if (iter.second().isArray()) {
379 FAIL_IF(!filter_recursive(v, iter.second().toArray(), filter, options));
380 } else {
381 FAIL_IF(!filter_var(v, iter.second(), filter, options));
383 arr.add(iter.first(), v);
385 ret = arr;
386 return true;
389 #undef FAIL_IF
391 ///////////////////////////////////////////////////////////////////////////////
393 Variant HHVM_FUNCTION(filter_list) {
394 size_t size = sizeof(filter_list) / sizeof(filter_list_entry);
395 Array ret;
396 for (size_t i = 0; i < size; ++i) {
397 ret.append(filter_list[i].name);
399 return ret;
402 Variant HHVM_FUNCTION(filter_id,
403 const String& filtername) {
404 size_t size = sizeof(filter_list) / sizeof(filter_list_entry);
405 for (size_t i = 0; i < size; ++i) {
406 if (filter_list[i].name == filtername) {
407 return filter_list[i].id;
411 return false;
414 #define FAIL_IF(x) \
415 do { \
416 if (x) return fail(filter_flags & k_FILTER_NULL_ON_FAILURE, options); \
417 } while(0)
419 Variant HHVM_FUNCTION(filter_var,
420 const Variant& variable,
421 int64_t filter /* = 516 */,
422 const Variant& options /* = empty_array_ref */) {
423 int64_t filter_flags;
424 if (options.isArray()) {
425 filter_flags = options.toCArrRef()[s_flags].toInt64();
426 } else {
427 filter_flags = options.toInt64();
430 if (!(filter_flags & k_FILTER_REQUIRE_ARRAY ||
431 filter_flags & k_FILTER_FORCE_ARRAY)) {
432 filter_flags |= k_FILTER_REQUIRE_SCALAR;
435 // No idea why, but zend does this..
436 if (filter == k_FILTER_CALLBACK) {
437 filter_flags = 0;
440 if (variable.isArray()) {
441 FAIL_IF(filter_flags & k_FILTER_REQUIRE_SCALAR);
442 Variant ret;
443 FAIL_IF(!filter_recursive(ret, variable, filter, options));
444 return ret;
446 FAIL_IF(filter_flags & k_FILTER_REQUIRE_ARRAY);
448 Variant ret;
449 FAIL_IF(!filter_var(ret, variable, filter, options));
450 if (filter_flags & k_FILTER_FORCE_ARRAY && !ret.isArray()) {
451 ret = make_packed_array(ret);
453 return ret;
456 #undef FAIL_IF
458 Array HHVM_FUNCTION(__SystemLib_filter_input_get_var, int64_t variable_name) {
459 return s_filter_request_data->getVar(variable_name);
462 void HHVM_FUNCTION(_filter_snapshot_globals) {
463 s_filter_request_data->requestInit();
466 ///////////////////////////////////////////////////////////////////////////////