Browse Source

improve security, formatting, validators, add code comments

main
ghost 3 years ago
parent
commit
e1b2366ace
  1. 85
      src/application/controller/api/user/avatar.php
  2. 88
      src/application/controller/api/user/profile.php
  3. 2
      src/system/helper/filter.php
  4. 26
      src/system/helper/format.php
  5. 32
      src/system/helper/valid.php
  6. 109
      src/system/twister.php

85
src/application/controller/api/user/avatar.php

@ -1,70 +1,82 @@
<?php <?php
// Default response
$response = [ $response = [
'success' => false, 'success' => false,
'message' => _('Internal server error'), 'message' => _('Internal server error'),
'avatar' => false 'avatar' => false
]; ];
// Access allowed for authorized users only
if (isset($_SESSION['userName'])) { if (isset($_SESSION['userName'])) {
// Prepare user request, authorized user by default
$userName = isset($_GET['userName']) ? Filter::userName($_GET['userName']) : $_SESSION['userName']; $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)) {
/*
* Step 1: try to obtain avatar from cache
*
* */
if ($mcAvatar = $_memcache->get('api.user.avatar.' . $userName)) {
$response = [ $response = [
'success' => true, 'success' => true,
'message' => _('Avatar successfully received from Cache'), 'message' => _('Avatar successfully received from Cache'),
'avatar' => $avatar 'avatar' => $mcAvatar
]; ];
} else if ($avatarVersions = $_twister->getDHT($userName, 'avatar', 's')) { /*
* Step 2: try to obtain profile from DHT
// Check avatar exists *
if ($userId = $_modelUser->getUserId($userName)) { * */
} else if ($dhtAvatarRevisions = $_twister->getDHTAvatarRevisions($userName)) {
// Add DHT version if not exists // Add DHT version if not exists
foreach ($avatarVersions as $avatarVersion) { foreach ((array) $dhtAvatarRevisions as $dhtAvatarRevision) {
if (!$_modelAvatar->versionExists($userId, if (!$_modelAvatar->versionExists($userId,
Filter::int($avatarVersion['p']['height']), $dhtAvatarRevision['height'],
Filter::int($avatarVersion['p']['seq']))) { $dhtAvatarRevision['seq'])) {
$_modelAvatar->add($userId, $_modelAvatar->add($userId,
Filter::int($avatarVersion['p']['height']), $dhtAvatarRevision['height'],
Filter::int($avatarVersion['p']['seq']), $dhtAvatarRevision['seq'],
Filter::int($avatarVersion['p']['time']), $dhtAvatarRevision['time'],
Filter::string($avatarVersion['p']['v'])); $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 = [ $response = [
'success' => true, 'success' => true,
'message' => _('Avatar successfully received from DHT'), 'message' => _('Avatar successfully received from DHT/DB'),
'avatar' => $avatarInfo['data'] '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 { } else {
$response = [
'success' => false,
'message' => _('Avatar data not available'),
'avatar' => false
];
}
// Generate identity icon // Generate identity icon
} else {
$fileName = md5($userName); $fileName = md5($userName);
$filePath = PROJECT_DIR . '/cache/image/' . $fileName . '.jpeg'; $filePath = PROJECT_DIR . '/cache/image/' . $fileName . '.jpeg';
// Identity icons supports file cache
if (!file_exists($filePath)) { if (!file_exists($filePath)) {
$icon = new Icon(); $icon = new Icon();
@ -73,16 +85,29 @@ if (isset($_SESSION['userName'])) {
file_put_contents($filePath, $image); file_put_contents($filePath, $image);
} }
$image = file_get_contents($filePath); $identityIcon = 'data:image/jpeg;base64,' . base64_encode(file_get_contents($filePath));
$response = [ $response = [
'success' => true, 'success' => true,
'message' => _('Avatar successfully received from Identity'), 'message' => _('Could not receive any avatar details, generated identity icon'),
'avatar' => 'data:image/jpeg;base64,' . base64_encode($image) 'avatar' => $identityIcon
]; ];
// Save identity icon into the cache pool
$_memcache->set('api.user.avatar.' . $userName, $identityIcon, MEMCACHE_COMPRESS, MEMCACHE_DHT_AVATAR_TIMEOUT);
}
// User not found in the local database registry
} else {
$response = [
'success' => false,
'message' => _('Requested user not found'),
'profile' => []
];
} }
// Session expired response
} else { } else {
$response = [ $response = [

88
src/application/controller/api/user/profile.php

@ -1,92 +1,110 @@
<?php <?php
// Default response
$response = [ $response = [
'success' => false, 'success' => false,
'message' => _('Internal server error'), 'message' => _('Internal server error'),
'profile' => [] 'profile' => []
]; ];
// Access allowed for authorized users only
if (isset($_SESSION['userName'])) { if (isset($_SESSION['userName'])) {
// Prepare user request, authorized user by default
$userName = isset($_GET['userName']) ? Filter::userName($_GET['userName']) : $_SESSION['userName']; $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)) {
/*
* Step 1: try to obtain profile from cache
*
* */
if ($mcProfile = $_memcache->get('api.user.profile.' . $userName)) {
$response = [ $response = [
'success' => true, 'success' => true,
'message' => _('Profile successfully received from Cache'), 'message' => _('Profile successfully received from Cache'),
'profile' => $profile 'profile' => $mcProfile
]; ];
} else if ($userProfileVersions = $_twister->getDHT($userName, 'profile', 's')) { /*
* Step 2: try to obtain profile from DHT
// Check user exists *
if ($userId = $_modelUser->getUserId($userName)) { * */
} else if ($dhtProfileRevisions = $_twister->getDHTProfileRevisions($userName)) {
// Add DHT version if not exists // 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, if (!$_modelProfile->versionExists($userId,
Filter::int($userProfileVersion['p']['height']), $dhtProfileRevision['height'],
Filter::int($userProfileVersion['p']['seq']))) { $dhtProfileRevision['seq'])) {
$profile = $userProfileVersion['p']['v'];
$_modelProfile->add($userId, $_modelProfile->add($userId,
Filter::int($userProfileVersion['p']['height']), $dhtProfileRevision['height'],
Filter::int($userProfileVersion['p']['seq']), $dhtProfileRevision['seq'],
Filter::int($userProfileVersion['p']['time']), $dhtProfileRevision['time'],
isset($profile['fullname']) ? Filter::string($profile['fullname']) : '', $dhtProfileRevision['fullName'],
isset($profile['bio']) ? Filter::string($profile['bio']) : '', $dhtProfileRevision['bio'],
isset($profile['location']) ? Filter::string($profile['location']) : '', $dhtProfileRevision['location'],
isset($profile['url']) ? Filter::string($profile['url']) : '', $dhtProfileRevision['url'],
isset($profile['bitmessage']) ? Filter::string($profile['bitmessage']) : '', $dhtProfileRevision['bitMessage'],
isset($profile['tox']) ? Filter::string($profile['tox']) : ''); $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 = [ $profile = [
'userName' => $userName, 'userName' => $userName,
'fullName' => Filter::string($profileInfo['fullName']), 'fullName' => Format::text($dbProfileRevision['fullName']),
'location' => Filter::string($profileInfo['location']), 'location' => Format::text($dbProfileRevision['location']),
'url' => Filter::string($profileInfo['url']), 'url' => Format::text($dbProfileRevision['url']),
'bitMessage' => Filter::string($profileInfo['bitMessage']), 'bitMessage' => Format::text($dbProfileRevision['bitMessage']),
'tox' => Filter::string($profileInfo['tox']), 'tox' => Format::text($dbProfileRevision['tox']),
'bio' => nl2br(Filter::string($profileInfo['bio'])), '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 = [ $response = [
'success' => true, 'success' => true,
'message' => _('Profile successfully received from DHT'), 'message' => _('Profile successfully received from DHT/DB'),
'profile' => $profile '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 { } else {
$response = [ $response = [
'success' => false, 'success' => false,
'message' => _('Profile data not available'), 'message' => _('Could not receive any profile details'),
'profile' => [] 'profile' => []
]; ];
} }
// User not found in the local database registry
} else { } else {
$response = [ $response = [
'success' => false, 'success' => false,
'message' => _('Could not receive profile details'), 'message' => _('Requested user not found'),
'profile' => [] 'profile' => []
]; ];
} }
// Session expired response
} else { } else {
$response = [ $response = [

2
src/system/helper/filter.php

@ -19,7 +19,7 @@ class Filter {
public static function string(mixed $string) { public static function string(mixed $string) {
return htmlentities($string, ENT_QUOTES, 'UTF-8'); return (string) $string;
} }
public static function int(mixed $int) { public static function int(mixed $int) {

26
src/system/helper/format.php

@ -40,24 +40,26 @@ class Format {
} else { } else {
return sprintf(_('%s %s later'), $r, self::plural($r, $v)); 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", "<b>$1</b>", $string);
$string = preg_replace("|\~([\S]+)\~|i", "<i>$1</i>", $string);
$string = preg_replace("|\_([\S]+)\_|i", "<u>$1</u>", $string);
$string = preg_replace("|\-([\S]+)\-|i", "<s>$1</s>", $string);
$string = preg_replace("|\`([\S]+)\`|i", "<samp>$1</samp>", $string);
$text = preg_replace("|\*([\S]+)\*|i", "<b>$1</b>", $text); $string = preg_replace("|@([a-zA-Z0-9_]+)|i", "<a href=\"people/$1\">@$1</a>", $string);
$text = preg_replace("|\~([\S]+)\~|i", "<i>$1</i>", $text); $string = preg_replace("|((https?://)+([\d\w\.-]+\.[\w\.]{2,6})[^\s\]\[\<\>]*/?)|i", "<a href=\"$1\" target=\"_blank\">$3</a>", $string);
$text = preg_replace("|\_([\S]+)\_|i", "<u>$1</u>", $text);
$text = preg_replace("|\-([\S]+)\-|i", "<s>$1</s>", $text);
$text = preg_replace("|\`([\S]+)\`|i", "<samp>$1</samp>", $text);
$text = preg_replace("|@([a-zA-Z0-9_]+)|i", "<a href=\"people/$1\">@$1</a>", $text); $string = nl2br($string);
$text = preg_replace("|((https?://)+([\d\w\.-]+\.[\w\.]{2,6})[^\s\]\[\<\>]*/?)|i", "<a href=\"$1\" target=\"_blank\">$3</a>", $text);
$text = nl2br($text);
return $text; return $string;
} }
} }

32
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) { public static function userName(string $userName) {
if (preg_match('/[^a-zA-Z0-9_]+/u', $userName)) { if (preg_match('/[^a-zA-Z0-9_]+/u', $userName)) {

109
src/system/twister.php

@ -243,6 +243,115 @@ class Twister {
return false; 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) { public function getDHT(string $userName, string $command, string $flag) {
$this->_curl->prepare( $this->_curl->prepare(

Loading…
Cancel
Save