X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Fnickname.php;h=1ed0abbe78dbfcec46365c97f73684cb2ff34b83;hb=refs%2Fheads%2Fupstream-changes%2Fgoogle-analytics-removal;hp=a0c9378cd3add545ecde82f63425b323652fb99e;hpb=9df856e667a12cd217576263efbc72fff12692d9;p=quix0rs-gnu-social.git diff --git a/lib/nickname.php b/lib/nickname.php index a0c9378cd3..1ed0abbe78 100644 --- a/lib/nickname.php +++ b/lib/nickname.php @@ -20,16 +20,41 @@ class Nickname { /** - * Regex fragment for pulling an arbitrarily-formated nickname. + * Regex fragment for pulling a formated nickname *OR* ID number. + * Suitable for router def of 'id' parameters on API actions. * * Not guaranteed to be valid after normalization; run the string through * Nickname::normalize() to get the canonical form, or Nickname::isValid() * if you just need to check if it's properly formatted. * - * This and CANONICAL_FMT replace the old NICKNAME_FMT, but be aware - * that these should not be enclosed in []s. + * This, DISPLAY_FMT, and CANONICAL_FMT should not be enclosed in []s. + * + * @fixme would prefer to define in reference to the other constants + */ + const INPUT_FMT = '(?:[0-9]+|[0-9a-zA-Z_]{1,64})'; + + /** + * Regex fragment for acceptable user-formatted variant of a nickname. + * + * This includes some chars such as underscore which will be removed + * from the normalized canonical form, but still must fit within + * field length limits. + * + * Not guaranteed to be valid after normalization; run the string through + * Nickname::normalize() to get the canonical form, or Nickname::isValid() + * if you just need to check if it's properly formatted. + * + * This, INPUT_FMT and CANONICAL_FMT should not be enclosed in []s. + */ + const DISPLAY_FMT = '[0-9a-zA-Z_]{1,64}'; + + /** + * Simplified regex fragment for acceptable full WebFinger ID of a user + * + * We could probably use an email regex here, but mainly we are interested + * in matching it in our URLs, like https://social.example/user@example.com */ - const DISPLAY_FMT = '[0-9a-zA-Z_]+'; + const WEBFINGER_FMT = '[0-9a-zA-Z_]{1,64}\@[0-9a-zA-Z_-.]{3,255}'; /** * Regex fragment for checking a canonical nickname. @@ -42,8 +67,7 @@ class Nickname * there are multiple possible denormalized forms for each valid * canonical-form name. * - * This and DISPLAY_FMT replace the old NICKNAME_FMT, but be aware - * that these should not be enclosed in []s. + * This, INPUT_FMT and DISPLAY_FMT should not be enclosed in []s. */ const CANONICAL_FMT = '[0-9a-z]{1,64}'; @@ -58,45 +82,61 @@ class Nickname * * Note that valid nicknames may be in use or reserved. * - * @param string $str - * @return boolean + * @param string $str The nickname string to test + * @param boolean $checkuse Check if it's in use (return false if it is) + * + * @return boolean True if nickname is valid. False if invalid (or taken if checkuse==true). */ - public static function isValid($str) + public static function isValid($str, $checkuse=false) { try { - self::normalize($str); - return true; + self::normalize($str, $checkuse); } catch (NicknameException $e) { return false; } + + return true; } /** * Validate an input nickname string, and normalize it to its canonical form. * The canonical form will be returned, or an exception thrown if invalid. * - * @param string $str + * @param string $str The nickname string to test + * @param boolean $checkuse Check if it's in use (return false if it is) * @return string Normalized canonical form of $str * * @throws NicknameException (base class) - * @throws NicknameInvalidException + * @throws NicknameBlacklistedException * @throws NicknameEmptyException + * @throws NicknameInvalidException + * @throws NicknamePathCollisionException + * @throws NicknameTakenException * @throws NicknameTooLongException */ - public static function normalize($str) + public static function normalize($str, $checkuse=false) { + // We should also have UTF-8 normalization (Ã¥ to a etc.) $str = trim($str); $str = str_replace('_', '', $str); $str = mb_strtolower($str); - $len = mb_strlen($str); - if ($len < 1) { - throw new NicknameEmptyException(); - } else if ($len > self::MAX_LEN) { + if (mb_strlen($str) > self::MAX_LEN) { + // Display forms must also fit! throw new NicknameTooLongException(); - } - if (!self::isCanonical($str)) { + } elseif (mb_strlen($str) < 1) { + throw new NicknameEmptyException(); + } elseif (!self::isCanonical($str)) { throw new NicknameInvalidException(); + } elseif (self::isBlacklisted($str)) { + throw new NicknameBlacklistedException(); + } elseif (self::isSystemPath($str)) { + throw new NicknamePathCollisionException(); + } elseif ($checkuse) { + $profile = self::isTaken($str); + if ($profile instanceof Profile) { + throw new NicknameTakenException($profile); + } } return $str; @@ -112,6 +152,73 @@ class Nickname { return preg_match('/^(?:' . self::CANONICAL_FMT . ')$/', $str); } + + /** + * Is the given string in our nickname blacklist? + * + * @param string $str + * @return boolean + */ + public static function isBlacklisted($str) + { + $blacklist = common_config('nickname', 'blacklist'); + return in_array($str, $blacklist); + } + + /** + * Is the given string identical to a system path or route? + * This could probably be put in some other class, but at + * at the moment, only Nickname requires this functionality. + * + * @param string $str + * @return boolean + */ + public static function isSystemPath($str) + { + $paths = array(); + + // All directory and file names in site root should be blacklisted + $d = dir(INSTALLDIR); + while (false !== ($entry = $d->read())) { + $paths[] = $entry; + } + $d->close(); + + // All top level names in the router should be blacklisted + $router = Router::get(); + foreach (array_keys($router->m->getPaths()) as $path) { + if (preg_match('/^\/(.*?)[\/\?]/',$path,$matches)) { + $paths[] = $matches[1]; + } + } + return in_array($str, $paths); + } + + /** + * Is the nickname already in use locally? Checks the User table. + * + * @param string $str + * @return Profile|null Returns Profile if nickname found, otherwise null + */ + public static function isTaken($str) + { + $found = User::getKV('nickname', $str); + if ($found instanceof User) { + return $found->getProfile(); + } + + $found = Local_group::getKV('nickname', $str); + if ($found instanceof Local_group) { + return $found->getProfile(); + } + + $found = Group_alias::getKV('alias', $str); + if ($found instanceof Group_alias) { + return $found->getProfile(); + } + + return null; + } } class NicknameException extends ClientException @@ -146,7 +253,7 @@ class NicknameInvalidException extends NicknameException { } } -class NicknameEmptyException extends NicknameException +class NicknameEmptyException extends NicknameInvalidException { /** * Default localized message for this type of exception. @@ -174,3 +281,43 @@ class NicknameTooLongException extends NicknameInvalidException Nickname::MAX_LEN); } } + +class NicknameBlacklistedException extends NicknameException +{ + protected function defaultMessage() + { + // TRANS: Validation error in form for registration, profile and group settings, etc. + return _('Nickname is disallowed through blacklist.'); + } +} + +class NicknamePathCollisionException extends NicknameException +{ + protected function defaultMessage() + { + // TRANS: Validation error in form for registration, profile and group settings, etc. + return _('Nickname is identical to system path names.'); + } +} + +class NicknameTakenException extends NicknameException +{ + public $profile = null; // the Profile which occupies the nickname + + public function __construct(Profile $profile, $msg=null, $code=400) + { + $this->profile = $profile; + + if ($msg === null) { + $msg = $this->defaultMessage(); + } + + parent::__construct($msg, $code); + } + + protected function defaultMessage() + { + // TRANS: Validation error in form for registration, profile and group settings, etc. + return _('Nickname is already in use on this server.'); + } +}