]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - lib/nickname.php
Removed plugin Google-Analytics as this is free/libre and decentralized
[quix0rs-gnu-social.git] / lib / nickname.php
index a0c9378cd3add545ecde82f63425b323652fb99e..1ed0abbe78dbfcec46365c97f73684cb2ff34b83 100644 (file)
 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.');
+    }
+}