Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / util / UserUtil.class.php
CommitLineData
158bd3ca
TD
1<?php
2namespace wcf\util;
3use 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 13final 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}