diff --git a/src/application/controller/api/user/avatar.php b/src/application/controller/api/user/avatar.php index 04ef82f..759aaeb 100644 --- a/src/application/controller/api/user/avatar.php +++ b/src/application/controller/api/user/avatar.php @@ -1,88 +1,113 @@ false, 'message' => _('Internal server error'), 'avatar' => false ]; +// Access allowed for authorized users only if (isset($_SESSION['userName'])) { + // Prepare user request, authorized user by default $userName = isset($_GET['userName']) ? Filter::userName($_GET['userName']) : $_SESSION['userName']; - if ($avatar = $_memcache->get('api.user.avatar.' . $userName)) { + // Check user exists in the database + if ($userId = $_modelUser->getUserId($userName)) { - $response = [ - 'success' => true, - 'message' => _('Avatar successfully received from Cache'), - 'avatar' => $avatar - ]; + /* + * Step 1: try to obtain avatar from cache + * + * */ + if ($mcAvatar = $_memcache->get('api.user.avatar.' . $userName)) { - } else if ($avatarVersions = $_twister->getDHT($userName, 'avatar', 's')) { + $response = [ + 'success' => true, + 'message' => _('Avatar successfully received from Cache'), + 'avatar' => $mcAvatar + ]; - // Check avatar exists - if ($userId = $_modelUser->getUserId($userName)) { + /* + * Step 2: try to obtain profile from DHT + * + * */ + } else if ($dhtAvatarRevisions = $_twister->getDHTAvatarRevisions($userName)) { // Add DHT version if not exists - foreach ($avatarVersions as $avatarVersion) { + foreach ((array) $dhtAvatarRevisions as $dhtAvatarRevision) { if (!$_modelAvatar->versionExists($userId, - Filter::int($avatarVersion['p']['height']), - Filter::int($avatarVersion['p']['seq']))) { - - $_modelAvatar->add( $userId, - Filter::int($avatarVersion['p']['height']), - Filter::int($avatarVersion['p']['seq']), - Filter::int($avatarVersion['p']['time']), - Filter::string($avatarVersion['p']['v'])); + $dhtAvatarRevision['height'], + $dhtAvatarRevision['seq'])) { + + $_modelAvatar->add($userId, + $dhtAvatarRevision['height'], + $dhtAvatarRevision['seq'], + $dhtAvatarRevision['time'], + $dhtAvatarRevision['data']); } } } - // Get latest version available - if ($avatarInfo = $_modelAvatar->get($userId)) { + /* + * Step 3: Select latest version available from DB revisions + * + * */ + $dbAvatarRevision = $_modelAvatar->get($userId); + if ($dbAvatarRevision && Valid::base64image($dbAvatarRevision['data'])) { + + // Response $response = [ 'success' => true, - 'message' => _('Avatar successfully received from DHT'), - 'avatar' => $avatarInfo['data'] + 'message' => _('Avatar successfully received from DHT/DB'), + 'avatar' => $dbAvatarRevision['data'] // format ]; - $_memcache->set('api.user.avatar.' . $userName, $avatarInfo['data'], MEMCACHE_COMPRESS, MEMCACHE_DHT_AVATAR_TIMEOUT); + // Save request into the cache pool + $_memcache->set('api.user.avatar.' . $userName, $dbAvatarRevision['data'], MEMCACHE_COMPRESS, MEMCACHE_DHT_AVATAR_TIMEOUT); + // Cache, DHT, DB not contain any the avatar details about user requested, + // Generate and return identity icon } else { - $response = [ - 'success' => false, - 'message' => _('Avatar data not available'), - 'avatar' => false - ]; - } + // Generate identity icon + $fileName = md5($userName); + $filePath = PROJECT_DIR . '/cache/image/' . $fileName . '.jpeg'; - // Generate identity icon - } else { + // Identity icons supports file cache + if (!file_exists($filePath)) { + + $icon = new Icon(); + $image = $icon->generateImageResource($fileName, 42, 42, false); - $fileName = md5($userName); - $filePath = PROJECT_DIR . '/cache/image/' . $fileName . '.jpeg'; + file_put_contents($filePath, $image); + } - if (!file_exists($filePath)) { + $identityIcon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents($filePath)); - $icon = new Icon(); - $image = $icon->generateImageResource($fileName, 42, 42, false); + $response = [ + 'success' => true, + 'message' => _('Could not receive any avatar details, generated identity icon'), + 'avatar' => $identityIcon + ]; - file_put_contents($filePath, $image); + // Save identity icon into the cache pool + $_memcache->set('api.user.avatar.' . $userName, $identityIcon, MEMCACHE_COMPRESS, MEMCACHE_DHT_AVATAR_TIMEOUT); } - $image = file_get_contents($filePath); + // User not found in the local database registry + } else { $response = [ - 'success' => true, - 'message' => _('Avatar successfully received from Identity'), - 'avatar' => 'data:image/jpeg;base64,' . base64_encode($image) + 'success' => false, + 'message' => _('Requested user not found'), + 'profile' => [] ]; - } +// Session expired response } else { $response = [ diff --git a/src/application/controller/api/user/profile.php b/src/application/controller/api/user/profile.php index 483b1e9..20901c6 100644 --- a/src/application/controller/api/user/profile.php +++ b/src/application/controller/api/user/profile.php @@ -1,92 +1,110 @@ false, 'message' => _('Internal server error'), 'profile' => [] ]; +// Access allowed for authorized users only if (isset($_SESSION['userName'])) { + // Prepare user request, authorized user by default $userName = isset($_GET['userName']) ? Filter::userName($_GET['userName']) : $_SESSION['userName']; - if ($profile = $_memcache->get('api.user.profile.' . $userName)) { + // Check user exists in the database + if ($userId = $_modelUser->getUserId($userName)) { - $response = [ - 'success' => true, - 'message' => _('Profile successfully received from Cache'), - 'profile' => $profile - ]; + /* + * Step 1: try to obtain profile from cache + * + * */ + if ($mcProfile = $_memcache->get('api.user.profile.' . $userName)) { - } else if ($userProfileVersions = $_twister->getDHT($userName, 'profile', 's')) { + $response = [ + 'success' => true, + 'message' => _('Profile successfully received from Cache'), + 'profile' => $mcProfile + ]; - // Check user exists - if ($userId = $_modelUser->getUserId($userName)) { + /* + * Step 2: try to obtain profile from DHT + * + * */ + } else if ($dhtProfileRevisions = $_twister->getDHTProfileRevisions($userName)) { // Add DHT version if not exists - foreach ($userProfileVersions as $userProfileVersion) { + foreach ((array) $dhtProfileRevisions as $dhtProfileRevision) { + // Save revision into the database if not exists if (!$_modelProfile->versionExists($userId, - Filter::int($userProfileVersion['p']['height']), - Filter::int($userProfileVersion['p']['seq']))) { - - $profile = $userProfileVersion['p']['v']; + $dhtProfileRevision['height'], + $dhtProfileRevision['seq'])) { $_modelProfile->add($userId, - Filter::int($userProfileVersion['p']['height']), - Filter::int($userProfileVersion['p']['seq']), - Filter::int($userProfileVersion['p']['time']), - - isset($profile['fullname']) ? Filter::string($profile['fullname']) : '', - isset($profile['bio']) ? Filter::string($profile['bio']) : '', - isset($profile['location']) ? Filter::string($profile['location']) : '', - isset($profile['url']) ? Filter::string($profile['url']) : '', - isset($profile['bitmessage']) ? Filter::string($profile['bitmessage']) : '', - isset($profile['tox']) ? Filter::string($profile['tox']) : ''); + $dhtProfileRevision['height'], + $dhtProfileRevision['seq'], + $dhtProfileRevision['time'], + + $dhtProfileRevision['fullName'], + $dhtProfileRevision['bio'], + $dhtProfileRevision['location'], + $dhtProfileRevision['url'], + $dhtProfileRevision['bitMessage'], + $dhtProfileRevision['tox']); } } } - // Get latest version available - if ($profileInfo = $_modelProfile->get($userId)) { + /* + * Step 3: Select latest version available from DB revisions + * + * */ + if ($dbProfileRevision = $_modelProfile->get($userId)) { + // Format output $profile = [ 'userName' => $userName, - 'fullName' => Filter::string($profileInfo['fullName']), - 'location' => Filter::string($profileInfo['location']), - 'url' => Filter::string($profileInfo['url']), - 'bitMessage' => Filter::string($profileInfo['bitMessage']), - 'tox' => Filter::string($profileInfo['tox']), - 'bio' => nl2br(Filter::string($profileInfo['bio'])), + 'fullName' => Format::text($dbProfileRevision['fullName']), + 'location' => Format::text($dbProfileRevision['location']), + 'url' => Format::text($dbProfileRevision['url']), + 'bitMessage' => Format::text($dbProfileRevision['bitMessage']), + 'tox' => Format::text($dbProfileRevision['tox']), + 'bio' => Format::text($dbProfileRevision['bio']), ]; + // Save request into the cache pool + $_memcache->set('api.user.profile.' . $userName, $profile, MEMCACHE_COMPRESS, MEMCACHE_DHT_PROFILE_TIMEOUT); + + // Response $response = [ 'success' => true, - 'message' => _('Profile successfully received from DHT'), + 'message' => _('Profile successfully received from DHT/DB'), 'profile' => $profile ]; - $_memcache->set('api.user.profile.' . $userName, $profile, MEMCACHE_COMPRESS, MEMCACHE_DHT_PROFILE_TIMEOUT); - + // Cache, DHT, DB not contain any the details about user requested } else { $response = [ 'success' => false, - 'message' => _('Profile data not available'), + 'message' => _('Could not receive any profile details'), 'profile' => [] ]; } + // User not found in the local database registry } else { $response = [ 'success' => false, - 'message' => _('Could not receive profile details'), + 'message' => _('Requested user not found'), 'profile' => [] ]; - } +// Session expired response } else { $response = [ diff --git a/src/system/helper/filter.php b/src/system/helper/filter.php index a2fedaf..32345c5 100644 --- a/src/system/helper/filter.php +++ b/src/system/helper/filter.php @@ -19,7 +19,7 @@ class Filter { public static function string(mixed $string) { - return htmlentities($string, ENT_QUOTES, 'UTF-8'); + return (string) $string; } public static function int(mixed $int) { diff --git a/src/system/helper/format.php b/src/system/helper/format.php index f3c0230..57c372e 100644 --- a/src/system/helper/format.php +++ b/src/system/helper/format.php @@ -40,24 +40,26 @@ class Format { } else { return sprintf(_('%s %s later'), $r, self::plural($r, $v)); } - - } } } - public static function post(string $text) { + public static function text(string $string) { + + $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8'); + $string = htmlentities($string, ENT_QUOTES, 'UTF-8'); + + $string = preg_replace("|\*([\S]+)\*|i", "$1", $string); + $string = preg_replace("|\~([\S]+)\~|i", "$1", $string); + $string = preg_replace("|\_([\S]+)\_|i", "$1", $string); + $string = preg_replace("|\-([\S]+)\-|i", "$1", $string); + $string = preg_replace("|\`([\S]+)\`|i", "$1", $string); - $text = preg_replace("|\*([\S]+)\*|i", "$1", $text); - $text = preg_replace("|\~([\S]+)\~|i", "$1", $text); - $text = preg_replace("|\_([\S]+)\_|i", "$1", $text); - $text = preg_replace("|\-([\S]+)\-|i", "$1", $text); - $text = preg_replace("|\`([\S]+)\`|i", "$1", $text); + $string = preg_replace("|@([a-zA-Z0-9_]+)|i", "@$1", $string); + $string = preg_replace("|((https?://)+([\d\w\.-]+\.[\w\.]{2,6})[^\s\]\[\<\>]*/?)|i", "$3", $string); - $text = preg_replace("|@([a-zA-Z0-9_]+)|i", "@$1", $text); - $text = preg_replace("|((https?://)+([\d\w\.-]+\.[\w\.]{2,6})[^\s\]\[\<\>]*/?)|i", "$3", $text); - $text = nl2br($text); + $string = nl2br($string); - return $text; + return $string; } } \ No newline at end of file diff --git a/src/system/helper/valid.php b/src/system/helper/valid.php index 7686808..e7ad6d7 100644 --- a/src/system/helper/valid.php +++ b/src/system/helper/valid.php @@ -17,6 +17,38 @@ class Valid { } } + public static function base64(string $string) { + + if (base64_encode(base64_decode($string, true)) === $string) { + + return true; + + } else { + + return false; + } + } + + public static function base64image(string $string) { + + $string = str_replace([ + 'data:image/jpeg;base64,', + 'data:image/jpg;base64,', + 'data:image/gif;base64,', + 'data:image/png;base64,', + 'data:image/webp;base64,', + ], '', $string); + + if (self::base64($string) && imagecreatefromstring(base64_decode($string))) { + + return true; + + } else { + + return false; + } + } + public static function userName(string $userName) { if (preg_match('/[^a-zA-Z0-9_]+/u', $userName)) { diff --git a/src/system/twister.php b/src/system/twister.php index cd3844e..8286458 100644 --- a/src/system/twister.php +++ b/src/system/twister.php @@ -243,6 +243,115 @@ class Twister { return false; } + public function getDHTProfileRevisions(string $userName) { + + $this->_curl->prepare( + '/', + 'POST', + 30, + [ + 'jsonrpc' => '2.0', + 'method' => 'dhtget', + 'params' => [ + $userName, + 'profile', + 's' + ], + 'id' => time() + rand() + ] + ); + + if ($response = $this->_curl->execute()) { + + if ($response['error']) { + + $this->_error = _($response['error']['message']); + + } else { + + $dhtProfileVersions = []; + foreach ((array) $response['result'] as $dhtProfileVersion) { + + // Required fields validation needed to make the DB revision compare + if (isset($dhtProfileVersion['p']['height']) && + isset($dhtProfileVersion['p']['seq']) && + isset($dhtProfileVersion['p']['time'])) { + + // Format revision response + $dhtProfileVersions[] = [ + 'height' => (int) $dhtProfileVersion['p']['height'], + 'seq' => (int) $dhtProfileVersion['p']['seq'], + 'time' => (int) $dhtProfileVersion['p']['time'], + + 'fullName' => isset($dhtProfileVersion['p']['v']['fullname']) ? (string) $dhtProfileVersion['p']['v']['fullname'] : '', + 'bio' => isset($dhtProfileVersion['p']['v']['bio']) ? (string) $dhtProfileVersion['p']['v']['bio'] : '', + 'location' => isset($dhtProfileVersion['p']['v']['location']) ? (string) $dhtProfileVersion['p']['v']['location'] : '', + 'url' => isset($dhtProfileVersion['p']['v']['url']) ? (string) $dhtProfileVersion['p']['v']['url'] : '', + 'bitMessage' => isset($dhtProfileVersion['p']['v']['bitmessage']) ? (string) $dhtProfileVersion['p']['v']['bitmessage'] : '', + 'tox' => isset($dhtProfileVersion['p']['v']['tox']) ? (string) $dhtProfileVersion['p']['v']['tox'] : '', + ]; + } + } + + return $dhtProfileVersions; // Formatted array + } + } + + return []; + } + + public function getDHTAvatarRevisions(string $userName) { + + $this->_curl->prepare( + '/', + 'POST', + 30, + [ + 'jsonrpc' => '2.0', + 'method' => 'dhtget', + 'params' => [ + $userName, + 'avatar', + 's' + ], + 'id' => time() + rand() + ] + ); + + if ($response = $this->_curl->execute()) { + + if ($response['error']) { + + $this->_error = _($response['error']['message']); + + } else { + + $dhtAvatarVersions = []; + foreach ((array) $response['result'] as $dhtAvatarVersion) { + + // Required fields validation needed to make the DB revision compare + if (isset($dhtAvatarVersion['p']['height']) && + isset($dhtAvatarVersion['p']['seq']) && + isset($dhtAvatarVersion['p']['time'])) { + + // Format revision response + $dhtAvatarVersions[] = [ + 'height' => (int) $dhtAvatarVersion['p']['height'], + 'seq' => (int) $dhtAvatarVersion['p']['seq'], + 'time' => (int) $dhtAvatarVersion['p']['time'], + + 'data' => isset($dhtAvatarVersion['p']['v']) ? (string) $dhtAvatarVersion['p']['v'] : '', + ]; + } + } + + return $dhtAvatarVersions; // Formatted array + } + } + + return []; + } + public function getDHT(string $userName, string $command, string $flag) { $this->_curl->prepare(