1 /*
2    +----------------------------------------------------------------------+
3    | This source file is subject to version 3.01 of the PHP license,      |
4    | that is bundled with this package in the file LICENSE, and is        |
5    | available through the world-wide-web at the following url:           |
6    | https://www.php.net/license/3_01.txt                                 |
7    | If you did not receive a copy of the PHP license and are unable to   |
8    | obtain it through the world-wide-web, please send a note to          |
9    | license@php.net so we can mail you a copy immediately.               |
10    +----------------------------------------------------------------------+
11    | Authors: Scott MacVicar <scottmac@php.net>                           |
12    +----------------------------------------------------------------------+
13  */
14 
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif
18 
19 #include "php_intl.h"
20 #include "spoofchecker_class.h"
21 
22 /* {{{ Checks if a given text contains any suspicious characters */
PHP_METHOD(Spoofchecker,isSuspicious)23 PHP_METHOD(Spoofchecker, isSuspicious)
24 {
25 	int32_t ret, errmask;
26 	char *text;
27 	size_t text_len;
28 	zval *error_code = NULL;
29 	SPOOFCHECKER_METHOD_INIT_VARS;
30 
31 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|z", &text, &text_len, &error_code)) {
32 		RETURN_THROWS();
33 	}
34 
35 	SPOOFCHECKER_METHOD_FETCH_OBJECT;
36 
37 #if U_ICU_VERSION_MAJOR_NUM >= 58
38 	ret = uspoof_check2UTF8(co->uspoof, text, text_len, co->uspoofres, SPOOFCHECKER_ERROR_CODE_P(co));
39 #else
40 	ret = uspoof_checkUTF8(co->uspoof, text, text_len, NULL, SPOOFCHECKER_ERROR_CODE_P(co));
41 #endif
42 
43 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
44 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
45 #if U_ICU_VERSION_MAJOR_NUM >= 58
46 		errmask = uspoof_getCheckResultChecks(co->uspoofres, SPOOFCHECKER_ERROR_CODE_P(co));
47 
48 		if (errmask != ret) {
49 			php_error_docref(NULL, E_WARNING, "unexpected error (%d), does not relate to the flags passed to setChecks (%d)", ret, errmask);
50 		}
51 #endif
52 		RETURN_TRUE;
53 	}
54 
55 	if (error_code) {
56 		zval_ptr_dtor(error_code);
57 		ZVAL_LONG(Z_REFVAL_P(error_code), ret);
58 		Z_TRY_ADDREF_P(error_code);
59 	}
60 	RETVAL_BOOL(ret != 0);
61 }
62 /* }}} */
63 
64 /* {{{ Checks if a given text contains any confusable characters */
PHP_METHOD(Spoofchecker,areConfusable)65 PHP_METHOD(Spoofchecker, areConfusable)
66 {
67 	int ret;
68 	char *s1, *s2;
69 	size_t s1_len, s2_len;
70 	zval *error_code = NULL;
71 	SPOOFCHECKER_METHOD_INIT_VARS;
72 
73 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "ss|z", &s1, &s1_len,
74 										 &s2, &s2_len, &error_code)) {
75 		RETURN_THROWS();
76 	}
77 
78 	SPOOFCHECKER_METHOD_FETCH_OBJECT;
79 	if(s1_len > INT32_MAX || s2_len > INT32_MAX) {
80 		SPOOFCHECKER_ERROR_CODE(co) = U_BUFFER_OVERFLOW_ERROR;
81 	} else {
82 		ret = uspoof_areConfusableUTF8(co->uspoof, s1, (int32_t)s1_len, s2, (int32_t)s2_len, SPOOFCHECKER_ERROR_CODE_P(co));
83 	}
84 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
85 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
86 		RETURN_TRUE;
87 	}
88 
89 	if (error_code) {
90 		zval_ptr_dtor(error_code);
91 		ZVAL_LONG(Z_REFVAL_P(error_code), ret);
92 		Z_TRY_ADDREF_P(error_code);
93 	}
94 	RETVAL_BOOL(ret != 0);
95 }
96 /* }}} */
97 
98 /* {{{ Locales to use when running checks */
PHP_METHOD(Spoofchecker,setAllowedLocales)99 PHP_METHOD(Spoofchecker, setAllowedLocales)
100 {
101 	char *locales;
102 	size_t locales_len;
103 	SPOOFCHECKER_METHOD_INIT_VARS;
104 
105 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &locales, &locales_len)) {
106 		RETURN_THROWS();
107 	}
108 
109 	SPOOFCHECKER_METHOD_FETCH_OBJECT;
110 
111 	uspoof_setAllowedLocales(co->uspoof, locales, SPOOFCHECKER_ERROR_CODE_P(co));
112 
113 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
114 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
115 		return;
116 	}
117 }
118 /* }}} */
119 
120 /* {{{ Set the checks to run */
PHP_METHOD(Spoofchecker,setChecks)121 PHP_METHOD(Spoofchecker, setChecks)
122 {
123 	zend_long checks;
124 	SPOOFCHECKER_METHOD_INIT_VARS;
125 
126 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &checks)) {
127 		RETURN_THROWS();
128 	}
129 
130 	SPOOFCHECKER_METHOD_FETCH_OBJECT;
131 
132 	uspoof_setChecks(co->uspoof, checks, SPOOFCHECKER_ERROR_CODE_P(co));
133 
134 	if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
135 		php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
136 	}
137 }
138 /* }}} */
139 
140 #if U_ICU_VERSION_MAJOR_NUM >= 58
141 /* TODO Document this method on PHP.net */
142 /* {{{ Set the loosest restriction level allowed for strings. */
PHP_METHOD(Spoofchecker,setRestrictionLevel)143 PHP_METHOD(Spoofchecker, setRestrictionLevel)
144 {
145 	zend_long level;
146 	SPOOFCHECKER_METHOD_INIT_VARS;
147 
148 	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &level)) {
149 		RETURN_THROWS();
150 	}
151 
152 	SPOOFCHECKER_METHOD_FETCH_OBJECT;
153 
154 	if (USPOOF_ASCII != level &&
155 			USPOOF_SINGLE_SCRIPT_RESTRICTIVE != level &&
156 			USPOOF_HIGHLY_RESTRICTIVE != level &&
157 			USPOOF_MODERATELY_RESTRICTIVE != level &&
158 			USPOOF_MINIMALLY_RESTRICTIVE != level &&
159 			USPOOF_UNRESTRICTIVE != level) {
160 		zend_argument_value_error(1, "must be one of Spoofchecker::ASCII, Spoofchecker::SINGLE_SCRIPT_RESTRICTIVE, "
161 			"Spoofchecker::SINGLE_HIGHLY_RESTRICTIVE, Spoofchecker::SINGLE_MODERATELY_RESTRICTIVE, "
162 			"Spoofchecker::SINGLE_MINIMALLY_RESTRICTIVE, or Spoofchecker::UNRESTRICTIVE");
163 		RETURN_THROWS();
164 	}
165 
166 	uspoof_setRestrictionLevel(co->uspoof, (URestrictionLevel)level);
167 }
168 /* }}} */
169 #endif
170