diff --git a/.gitmodules b/.gitmodules index 525bff2..7330f9f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "lib/index"] path = lib/index url = https://git.flash.moe/flash/index.git +[submodule "lib/device-detector"] + path = lib/device-detector + url = https://github.com/matomo-org/device-detector.git +[submodule "lib/spyc"] + path = lib/spyc + url = https://github.com/mustangostang/spyc.git diff --git a/composer.json b/composer.json index 9ed0d78..9d1735c 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,6 @@ "twig/twig": "^3.0", "erusev/parsedown": "~1.6", "chillerlan/php-qrcode": "^4.3", - "whichbrowser/parser": "^2.0", "symfony/mailer": "^6.0" }, "autoload": { diff --git a/composer.lock b/composer.lock index 1b1d51f..af75351 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "26152622b2776a8d945647d5c1cfc48c", + "content-hash": "d757fc71803876a28a7434df1fa5edf1", "packages": [ { "name": "chillerlan/php-qrcode", @@ -342,55 +342,6 @@ }, "time": "2019-12-30T22:54:17+00:00" }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, { "name": "psr/container", "version": "2.0.2", @@ -1427,70 +1378,6 @@ } ], "time": "2023-06-08T12:52:13+00:00" - }, - { - "name": "whichbrowser/parser", - "version": "v2.1.7", - "source": { - "type": "git", - "url": "https://github.com/WhichBrowser/Parser-PHP.git", - "reference": "1044880bc792dbce5948fbff22ae731c43c280d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/WhichBrowser/Parser-PHP/zipball/1044880bc792dbce5948fbff22ae731c43c280d9", - "reference": "1044880bc792dbce5948fbff22ae731c43c280d9", - "shasum": "" - }, - "require": { - "php": ">=5.4.0", - "psr/cache": "^1.0 || ^2.0 || ^3.0" - }, - "require-dev": { - "cache/array-adapter": "^1.1", - "icomefromthenet/reverse-regex": "0.0.6.3", - "php-coveralls/php-coveralls": "^2.0", - "phpunit/php-code-coverage": "^5.0 || ^7.0", - "phpunit/phpunit": "^6.0 || ^8.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/yaml": "~3.4 || ~4.0" - }, - "suggest": { - "cache/array-adapter": "Allows testing of the caching functionality" - }, - "type": "library", - "autoload": { - "psr-4": { - "WhichBrowser\\": [ - "src/", - "tests/src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Niels Leenheer", - "email": "niels@leenheer.nl", - "role": "Developer" - } - ], - "description": "Useragent sniffing library for PHP", - "homepage": "http://whichbrowser.net", - "keywords": [ - "browser", - "sniffing", - "ua", - "useragent" - ], - "support": { - "issues": "https://github.com/WhichBrowser/Parser-PHP/issues", - "source": "https://github.com/WhichBrowser/Parser-PHP/tree/v2.1.7" - }, - "time": "2022-04-19T20:14:54+00:00" } ], "packages-dev": [], diff --git a/lib/device-detector b/lib/device-detector new file mode 160000 index 0000000..3e0fac7 --- /dev/null +++ b/lib/device-detector @@ -0,0 +1 @@ +Subproject commit 3e0fac7e77f3faadc3858fea9f5fa7efeb9cf239 diff --git a/lib/spyc b/lib/spyc new file mode 160000 index 0000000..4627c83 --- /dev/null +++ b/lib/spyc @@ -0,0 +1 @@ +Subproject commit 4627c838b16550b666d15aeae1e5289dd5b77da0 diff --git a/misuzu.php b/misuzu.php index 2bf2ab1..f8c7e30 100644 --- a/misuzu.php +++ b/misuzu.php @@ -29,7 +29,12 @@ Environment::setDebug(MSZ_DEBUG); mb_internal_encoding('utf-8'); date_default_timezone_set('utc'); +// Third party libraries require_once MSZ_ROOT . '/vendor/autoload.php'; +require_once MSZ_LIBRARIES . '/spyc/Spyc.php'; // dependency for device-detector +require_once MSZ_LIBRARIES . '/device-detector/autoload.php'; + +// Procedural components require_once MSZ_ROOT . '/utility.php'; require_once MSZ_SOURCE . '/perms.php'; require_once MSZ_SOURCE . '/manage.php'; diff --git a/public-legacy/manage/users/user.php b/public-legacy/manage/users/user.php index bdbb4c1..fd4cd59 100644 --- a/public-legacy/manage/users/user.php +++ b/public-legacy/manage/users/user.php @@ -111,8 +111,6 @@ if(CSRF::validateRequest() && $canEdit) { } if(!empty($_POST['user']) && is_array($_POST['user'])) { - //$setUsername = (string)($_POST['user']['username'] ?? ''); - //$setEMailAddress = (string)($_POST['user']['email'] ?? ''); $setCountry = (string)($_POST['user']['country'] ?? ''); $setTitle = (string)($_POST['user']['title'] ?? ''); @@ -122,21 +120,10 @@ if(CSRF::validateRequest() && $canEdit) { $userInfo->setDisplayRole(UserRole::byId($displayRole)); } catch(UserRoleNotFoundException $ex) {} - //$usernameValidation = User::validateUsername($setUsername); - //$emailValidation = User::validateEMailAddress($setEMailAddress); $countryValidation = strlen($setCountry) === 2 && ctype_alpha($setCountry) && ctype_upper($setCountry); - //if(!empty($usernameValidation)) - // $notices[] = User::usernameValidationErrorString($usernameValidation); - - // if(!empty($emailValidation)) { - // $notices[] = $emailValidation === 'in-use' - // ? 'This e-mail address has already been used!' - // : 'This e-mail address is invalid!'; - // } - if(!$countryValidation) $notices[] = 'Country code was invalid.'; @@ -144,10 +131,7 @@ if(CSRF::validateRequest() && $canEdit) { $notices[] = 'User title was invalid.'; if(empty($notices)) - $userInfo - // ->setUsername((string)($_POST['user']['username'] ?? '')) - // ->setEMailAddress((string)($_POST['user']['email'] ?? '')) - ->setCountry((string)($_POST['user']['country'] ?? '')) + $userInfo->setCountry((string)($_POST['user']['country'] ?? '')) ->setTitle((string)($_POST['user']['title'] ?? '')) ->setDisplayRole(UserRole::byId((int)($_POST['user']['display_role'] ?? 0))); } diff --git a/src/ClientInfo.php b/src/ClientInfo.php new file mode 100644 index 0000000..dca7fcc --- /dev/null +++ b/src/ClientInfo.php @@ -0,0 +1,76 @@ +dd = $dd; + } + + public function __toString(): string { + if($this->dd->isBot()) { + $botInfo = $this->dd->getBot(); + return $botInfo['name'] ?? 'an unknown bot'; + } + + $clientInfo = $this->dd->getClient(); + if(empty($clientInfo['name'])) + return 'an unknown browser'; + + $string = $clientInfo['name']; + if(!empty($clientInfo['version'])) + $string .= ' ' . $clientInfo['version']; + + $osInfo = $this->dd->getOs(); + $hasOsInfo = !empty($osInfo['name']); + + $brandName = $this->dd->getBrandName(); + $modelName = $this->dd->getModel(); + $hasModelName = !empty($modelName); + + if($hasOsInfo || $hasModelName) + $string .= ' on '; + + if($hasModelName) { + $deviceName = trim($brandName . ' ' . $modelName); + // most naive check in the world but it works well enough for this lol + $firstCharIsVowel = in_array(strtolower($deviceName[0]), ['a', 'i', 'u', 'e', 'o']); + $string .= ($firstCharIsVowel ? 'an' : 'a') . ' ' . $deviceName; + } + + if($hasOsInfo) { + if($hasModelName) + $string .= ' running '; + + $string .= $osInfo['name']; + if(!empty($osInfo['version'])) + $string .= ' ' . $osInfo['version']; + if(!empty($osInfo['platform'])) + $string .= ' (' . $osInfo['platform'] . ')'; + } + + return $string; + } + + public static function parse(array|string $serverVarsOrUserAgent): self { + if(is_string($serverVarsOrUserAgent)) { + $userAgent = $serverVarsOrUserAgent; + $clientHints = null; + } else { + $userAgent = array_key_exists('HTTP_USER_AGENT', $serverVarsOrUserAgent) + ? $serverVarsOrUserAgent['HTTP_USER_AGENT'] : ''; + $clientHints = ClientHints::factory($serverVarsOrUserAgent); + } + + $dd = new DeviceDetector($userAgent, $clientHints); + $dd->parse(); + + return new static($dd); + } +} diff --git a/src/Users/User.php b/src/Users/User.php index 133fecb..50faf9a 100644 --- a/src/Users/User.php +++ b/src/Users/User.php @@ -92,18 +92,10 @@ class User implements HasRankInterface { public function getUsername(): string { return $this->username; } - public function setUsername(string $username): self { - $this->username = $username; - return $this; - } public function getEmailAddress(): string { return $this->email; } - public function setEmailAddress(string $address): self { - $this->email = mb_strtolower($address); - return $this; - } public function getRegisterRemoteAddress(): string { return $this->register_ip ?? '::1'; @@ -115,10 +107,6 @@ class User implements HasRankInterface { public function isSuper(): bool { return boolval($this->user_super); } - public function setSuper(bool $super): self { - $this->user_super = $super ? 1 : 0; - return $this; - } public function hasCountry(): bool { return $this->user_country !== 'XX'; @@ -400,40 +388,12 @@ class User implements HasRankInterface { * DELETING * ************/ - private const NUKE_TIMEOUT = 600; - public function getDeletedTime(): int { return $this->user_deleted === null ? -1 : $this->user_deleted; } public function isDeleted(): bool { return $this->getDeletedTime() >= 0; } - public function delete(): void { - if($this->isDeleted()) - return; - $this->user_deleted = time(); - DB::prepare('UPDATE `' . DB::PREFIX . self::TABLE . '` SET `user_deleted` = NOW() WHERE `user_id` = :user') - ->bind('user', $this->user_id) - ->execute(); - } - public function restore(): void { - if(!$this->isDeleted()) - return; - $this->user_deleted = null; - DB::prepare('UPDATE `' . DB::PREFIX . self::TABLE . '` SET `user_deleted` = NULL WHERE `user_id` = :user') - ->bind('user', $this->user_id) - ->execute(); - } - public function canBeNuked(): bool { - return $this->isDeleted() && time() > $this->getDeletedTime() + self::NUKE_TIMEOUT; - } - public function nuke(): void { - if(!$this->canBeNuked()) - return; - DB::prepare('DELETE FROM `' . DB::PREFIX . self::TABLE . '` WHERE `user_id` = :user') - ->bind('user', $this->user_id) - ->execute(); - } /********** * ASSETS * diff --git a/src/Users/UserLoginAttempt.php b/src/Users/UserLoginAttempt.php index 679e1d0..b67666d 100644 --- a/src/Users/UserLoginAttempt.php +++ b/src/Users/UserLoginAttempt.php @@ -1,9 +1,9 @@ attempt_user_agent; } - public function getUserAgentInfo(): UserAgentParser { - if($this->uaInfo === null) - $this->uaInfo = new UserAgentParser($this->getUserAgent()); - return $this->uaInfo; + public function getClientString(): string { + return (string)ClientInfo::parse($this->attempt_user_agent); } public static function remaining(string $remoteAddr): int { diff --git a/src/Users/UserSession.php b/src/Users/UserSession.php index 160dc15..f24c576 100644 --- a/src/Users/UserSession.php +++ b/src/Users/UserSession.php @@ -1,9 +1,9 @@ session_user_agent; } - public function getUserAgentInfo(): UserAgentParser { - if($this->uaInfo === null) - $this->uaInfo = new UserAgentParser($this->getUserAgent()); - return $this->uaInfo; + public function getClientString(): string { + return (string)ClientInfo::parse($this->session_user_agent); } public function getCountry(): string { diff --git a/templates/user/macros.twig b/templates/user/macros.twig index 9d828dd..a338a04 100644 --- a/templates/user/macros.twig +++ b/templates/user/macros.twig @@ -73,7 +73,7 @@
{{ session.country }}
- {{ session.userAgentInfo.toString }} + {{ session.clientString }}
@@ -160,7 +160,7 @@
{{ attempt.country }}
- {{ attempt.userAgentInfo.toString }} + {{ attempt.clientString }}