Commit | Line | Data |
---|---|---|
158bd3ca TD |
1 | <?php |
2 | namespace wcf\util; | |
3 | use wcf\system\WCF; | |
4 | ||
5 | /** | |
6 | * Contains user-related functions. | |
7 | * | |
9f959ced | 8 | * @author Marcel Werk |
c839bd49 | 9 | * @copyright 2001-2018 WoltLab GmbH |
158bd3ca | 10 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 | 11 | * @package WoltLabSuite\Core\Util |
158bd3ca | 12 | */ |
18284789 | 13 | final class UserUtil { |
158bd3ca | 14 | /** |
28410a97 | 15 | * Returns true if the given name is a valid username. |
158bd3ca | 16 | * |
9f959ced MS |
17 | * @param string $name |
18 | * @return boolean | |
158bd3ca TD |
19 | */ |
20 | public static function isValidUsername($name) { | |
f5ddfc28 S |
21 | // minimum length is 3 characters, maximum length is 100 characters |
22 | if (mb_strlen($name) < 3 || mb_strlen($name) > 100) { | |
0d0de342 TD |
23 | return false; |
24 | } | |
25 | ||
158bd3ca TD |
26 | // check illegal characters |
27 | if (!preg_match('!^[^,\n]+$!', $name)) { | |
28 | return false; | |
29 | } | |
30 | // check long words | |
31 | $words = preg_split('!\s+!', $name, -1, PREG_SPLIT_NO_EMPTY); | |
32 | foreach ($words as $word) { | |
838e315b | 33 | if (mb_strlen($word) > 20) { |
158bd3ca TD |
34 | return false; |
35 | } | |
36 | } | |
d3d127e4 MW |
37 | // username must not be a valid e-mail |
38 | if (self::isValidEmail($name)) { | |
39 | return false; | |
40 | } | |
41 | ||
158bd3ca TD |
42 | return true; |
43 | } | |
44 | ||
45 | /** | |
28410a97 | 46 | * Returns true if the given username is available. |
158bd3ca | 47 | * |
9f959ced MS |
48 | * @param string $name |
49 | * @return boolean | |
158bd3ca TD |
50 | */ |
51 | public static function isAvailableUsername($name) { | |
5c6ddd85 | 52 | $sql = "SELECT COUNT(username) |
39bea7dd MS |
53 | FROM wcf".WCF_N."_user |
54 | WHERE username = ?"; | |
158bd3ca | 55 | $statement = WCF::getDB()->prepareStatement($sql); |
058cbd6a | 56 | $statement->execute([$name]); |
5c6ddd85 MS |
57 | |
58 | return $statement->fetchSingleColumn() == 0; | |
158bd3ca | 59 | } |
9f959ced | 60 | |
158bd3ca | 61 | /** |
28410a97 | 62 | * Returns true if the given e-mail is a valid address. |
0c166126 | 63 | * @see http://www.faqs.org/rfcs/rfc821.html |
158bd3ca TD |
64 | * |
65 | * @param string $email | |
9f959ced | 66 | * @return boolean |
158bd3ca TD |
67 | */ |
68 | public static function isValidEmail($email) { | |
ae2dd1d2 S |
69 | if (mb_strlen($email) > 191) { |
70 | return false; | |
71 | } | |
72 | ||
158bd3ca TD |
73 | // local-part |
74 | $c = '!#\$%&\'\*\+\-\/0-9=\?a-z\^_`\{\}\|~'; | |
75 | $string = '['.$c.']*(?:\\\\[\x00-\x7F]['.$c.']*)*'; | |
76 | $localPart = $string.'(?:\.'.$string.')*'; | |
77 | ||
78 | // domain | |
79 | $name = '[a-z0-9](?:[a-z0-9-]*[a-z0-9])?'; | |
80 | $domain = $name.'(?:\.'.$name.')*\.[a-z]{2,}'; | |
81 | ||
82 | // mailbox | |
83 | $mailbox = $localPart.'@'.$domain; | |
84 | ||
85 | return preg_match('/^'.$mailbox.'$/i', $email); | |
86 | } | |
87 | ||
88 | /** | |
28410a97 | 89 | * Returns true if the given email address is available. |
158bd3ca | 90 | * |
9f959ced MS |
91 | * @param string $email |
92 | * @return boolean | |
158bd3ca TD |
93 | */ |
94 | public static function isAvailableEmail($email) { | |
5c6ddd85 | 95 | $sql = "SELECT COUNT(email) |
39bea7dd MS |
96 | FROM wcf".WCF_N."_user |
97 | WHERE email = ?"; | |
158bd3ca | 98 | $statement = WCF::getDB()->prepareStatement($sql); |
058cbd6a | 99 | $statement->execute([$email]); |
5c6ddd85 MS |
100 | |
101 | return $statement->fetchSingleColumn() == 0; | |
158bd3ca TD |
102 | } |
103 | ||
104 | /** | |
105 | * Returns the user agent of the client. | |
106 | * | |
107 | * @return string | |
108 | */ | |
109 | public static function getUserAgent() { | |
bc56f619 MW |
110 | if (isset($_SERVER['HTTP_USER_AGENT'])) { |
111 | $userAgent = $_SERVER['HTTP_USER_AGENT']; | |
a92ec792 | 112 | if (!StringUtil::isUTF8($userAgent)) { |
bc56f619 MW |
113 | $userAgent = StringUtil::convertEncoding('ISO-8859-1', 'UTF-8', $userAgent); |
114 | } | |
115 | ||
a2bdc5f8 | 116 | return mb_substr($userAgent, 0, 191); |
bc56f619 | 117 | } |
158bd3ca TD |
118 | return ''; |
119 | } | |
120 | ||
3e17a8ab MS |
121 | /** |
122 | * Returns true if the active user uses a mobile browser. | |
123 | * @see http://detectmobilebrowser.com | |
124 | * | |
125 | * @return boolean | |
126 | */ | |
127 | public static function usesMobileBrowser() { | |
128 | $userAgent = self::getUserAgent(); | |
129 | if (!$userAgent) false; | |
130 | ||
131 | if (preg_match('/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i', $userAgent)) { | |
132 | return true; | |
133 | } | |
134 | ||
3d802a67 | 135 | if (preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i', substr($userAgent, 0, 4))) { |
3e17a8ab MS |
136 | return true; |
137 | } | |
138 | ||
139 | return false; | |
140 | } | |
141 | ||
158bd3ca | 142 | /** |
1e5f194f | 143 | * Returns the ipv6 address of the client. |
a17de04e | 144 | * |
9f959ced | 145 | * @return string |
158bd3ca TD |
146 | */ |
147 | public static function getIpAddress() { | |
148 | $REMOTE_ADDR = ''; | |
149 | if (isset($_SERVER['REMOTE_ADDR'])) $REMOTE_ADDR = $_SERVER['REMOTE_ADDR']; | |
1e5f194f | 150 | |
158bd3ca TD |
151 | // darwin fix |
152 | if ($REMOTE_ADDR == '::1' || $REMOTE_ADDR == 'fe80::1') { | |
92fd47d9 | 153 | $REMOTE_ADDR = '127.0.0.1'; |
158bd3ca TD |
154 | } |
155 | ||
1e5f194f MW |
156 | $REMOTE_ADDR = self::convertIPv4To6($REMOTE_ADDR); |
157 | ||
158bd3ca TD |
158 | return $REMOTE_ADDR; |
159 | } | |
160 | ||
1e5f194f MW |
161 | /** |
162 | * Converts given ipv4 to ipv6. | |
163 | * | |
164 | * @param string $ip | |
165 | * @return string | |
166 | */ | |
167 | public static function convertIPv4To6($ip) { | |
827e7aea AE |
168 | // drop Window's scope id (confused PHP) |
169 | $ip = preg_replace('~%[^%]+$~', '', $ip); | |
170 | ||
1e5f194f MW |
171 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) { |
172 | // given ip is already ipv6 | |
173 | return $ip; | |
174 | } | |
175 | ||
176 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { | |
177 | // invalid ip given | |
178 | return ''; | |
179 | } | |
180 | ||
181 | $ipArray = array_pad(explode('.', $ip), 4, 0); | |
182 | $part7 = base_convert(($ipArray[0] * 256) + $ipArray[1], 10, 16); | |
183 | $part8 = base_convert(($ipArray[2] * 256) + $ipArray[3], 10, 16); | |
6e1b461c | 184 | |
1e5f194f MW |
185 | return '::ffff:'.$part7.':'.$part8; |
186 | } | |
187 | ||
9e7766a4 AE |
188 | /** |
189 | * Converts IPv6 embedded IPv4 address into IPv4 or returns input if true IPv6. | |
190 | * | |
191 | * @param string $ip | |
192 | * @return string | |
193 | */ | |
194 | public static function convertIPv6To4($ip) { | |
195 | // validate if given IP is a proper IPv6 address | |
196 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { | |
197 | // validate if given IP is a proper IPv4 address | |
198 | if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { | |
9e7766a4 AE |
199 | // ip address is invalid |
200 | return ''; | |
201 | } | |
202 | ||
203 | return $ip; | |
204 | } | |
205 | ||
206 | // check if ip is a masked IPv4 address | |
207 | if (substr($ip, 0, 7) == '::ffff:') { | |
dd2dc943 | 208 | $ip = substr($ip, 7); |
6e1b461c | 209 | if (preg_match('~^([a-f0-9]{1,4}):([a-f0-9]{1,4})$~', $ip, $matches)) { |
058cbd6a | 210 | $ip = [ |
dd2dc943 AE |
211 | base_convert($matches[1], 16, 10), |
212 | base_convert($matches[2], 16, 10) | |
058cbd6a | 213 | ]; |
dd2dc943 | 214 | |
058cbd6a | 215 | $ipParts = []; |
dd2dc943 AE |
216 | $tmp = $ip[0] % 256; |
217 | $ipParts[] = ($ip[0] - $tmp) / 256; | |
218 | $ipParts[] = $tmp; | |
219 | $tmp = $ip[1] % 256; | |
220 | $ipParts[] = ($ip[1] - $tmp) / 256; | |
221 | $ipParts[] = $tmp; | |
222 | ||
223 | return implode('.', $ipParts); | |
224 | } | |
225 | else { | |
226 | return $ip; | |
227 | } | |
9e7766a4 AE |
228 | } |
229 | else { | |
230 | // given ip is an IPv6 address and cannot be converted | |
231 | return $ip; | |
232 | } | |
233 | } | |
234 | ||
158bd3ca TD |
235 | /** |
236 | * Returns the request uri of the active request. | |
237 | * | |
238 | * @return string | |
239 | */ | |
240 | public static function getRequestURI() { | |
241 | $REQUEST_URI = ''; | |
55a685d2 | 242 | |
bf0436f9 | 243 | $appendQueryString = true; |
55a685d2 MW |
244 | if (!empty($_SERVER['ORIG_PATH_INFO']) && strpos($_SERVER['ORIG_PATH_INFO'], '.php') !== false) { |
245 | $REQUEST_URI = $_SERVER['ORIG_PATH_INFO']; | |
246 | } | |
247 | else if (!empty($_SERVER['ORIG_SCRIPT_NAME'])) { | |
248 | $REQUEST_URI = $_SERVER['ORIG_SCRIPT_NAME']; | |
249 | } | |
bf0436f9 AE |
250 | else if (!empty($_SERVER['SCRIPT_NAME']) && (isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO']))) { |
251 | $REQUEST_URI = $_SERVER['SCRIPT_NAME'] . $_SERVER['PATH_INFO']; | |
252 | } | |
253 | else if (isset($_SERVER['REQUEST_URI']) && !empty($_SERVER['REQUEST_URI'])) { | |
254 | $REQUEST_URI = $_SERVER['REQUEST_URI']; | |
255 | $appendQueryString = false; | |
55a685d2 MW |
256 | } |
257 | else if (!empty($_SERVER['PHP_SELF'])) { | |
258 | $REQUEST_URI = $_SERVER['PHP_SELF']; | |
259 | } | |
260 | else if (!empty($_SERVER['PATH_INFO'])) { | |
261 | $REQUEST_URI = $_SERVER['PATH_INFO']; | |
262 | } | |
bf0436f9 | 263 | if ($appendQueryString && !empty($_SERVER['QUERY_STRING'])) { |
55a685d2 | 264 | $REQUEST_URI .= '?'.$_SERVER['QUERY_STRING']; |
158bd3ca | 265 | } |
158bd3ca | 266 | |
55a685d2 | 267 | // fix encoding |
a92ec792 | 268 | if (!StringUtil::isUTF8($REQUEST_URI)) { |
55a685d2 MW |
269 | $REQUEST_URI = StringUtil::convertEncoding('ISO-8859-1', 'UTF-8', $REQUEST_URI); |
270 | } | |
158bd3ca | 271 | |
838e315b | 272 | return mb_substr(FileUtil::unifyDirSeparator($REQUEST_URI), 0, 255); |
158bd3ca | 273 | } |
18284789 | 274 | |
1d5f9363 MS |
275 | /** |
276 | * Forbid creation of UserUtil objects. | |
277 | */ | |
278 | private function __construct() { | |
279 | // does nothing | |
280 | } | |
dcb3a44c | 281 | } |