From 6da8eb931dc783b47fe8e68cc193d6bf8f677131 Mon Sep 17 00:00:00 2001 From: flashwave Date: Sun, 27 Mar 2016 23:18:57 +0200 Subject: [PATCH] premium stuff --- cron.php | 20 +- libraries/BBcode.php | 35 ++- libraries/Controllers/AuthController.php | 70 ++++- libraries/Controllers/FileController.php | 67 +++-- libraries/Controllers/ForumController.php | 56 +++- libraries/Controllers/HelperController.php | 7 +- libraries/Controllers/MetaController.php | 47 +++- libraries/Controllers/PremiumController.php | 284 +++++++++++--------- libraries/Controllers/UserController.php | 59 ++-- libraries/Forum/Post.php | 4 +- libraries/Net.php | 8 +- libraries/Session.php | 4 +- libraries/User.php | 107 ++++++-- libraries/Users.php | 135 ---------- libraries/Utils.php | 92 +------ routes.php | 6 +- sakura.php | 2 +- templates/yuuno/forum/index.twig | 2 +- templates/yuuno/forum/viewtopic.twig | 4 +- templates/yuuno/global/information.twig | 4 +- templates/yuuno/global/master.twig | 12 +- templates/yuuno/main/premiumcomplete.twig | 2 +- templates/yuuno/main/profile.twig | 2 +- templates/yuuno/main/support.twig | 17 +- 24 files changed, 541 insertions(+), 505 deletions(-) diff --git a/cron.php b/cron.php index a91ee12..494b3dd 100644 --- a/cron.php +++ b/cron.php @@ -6,14 +6,6 @@ // Declare Namespace namespace Sakura; -// Check if the script isn't executed by root -if (function_exists('posix_getuid')) { - if (posix_getuid() === 0) { - trigger_error('Running cron as root is disallowed for security reasons.', E_USER_ERROR); - exit; - } -} - // Define that this page won't require templating define('SAKURA_NO_TPL', true); @@ -35,9 +27,13 @@ DB::table('notifications') // Get expired premium accounts $expiredPremium = DB::table('premium') ->where('premium_expire', '<', time()) - ->get(); + ->get(['user_id']); -// Process expired premium accounts, make this not stupid in the future -foreach ($expiredPremium as $expired) { - Users::updatePremiumMeta($expired->user_id); +foreach ($expiredPremium as $premium) { + DB::table('premium') + ->where('user_id', $premium->user_id) + ->delete(); + + User::construct($premium->user_id) + ->isPremium(); } diff --git a/libraries/BBcode.php b/libraries/BBcode.php index 82c1c61..7d032a4 100644 --- a/libraries/BBcode.php +++ b/libraries/BBcode.php @@ -139,7 +139,7 @@ class BBcode $parsed = nl2br(self::$bbcode->getAsHtml()); - $parsed = Utils::fixCodeTags($parsed); + $parsed = self::fixCodeTags($parsed); $parsed = self::parseEmoticons($parsed); return $parsed; @@ -178,4 +178,37 @@ class BBcode return self::$bbcode->getAsText(); } + + /** + * Clean up the contents of tags. + * + * @param string $text Dirty + * + * @return string Clean + */ + public static function fixCodeTags($text) + { + $parts = explode('', $text); + $newStr = ''; + + if (count($parts) > 1) { + foreach ($parts as $p) { + $parts2 = explode('', $p); + if (count($parts2) > 1) { + $code = str_replace('
', '', $parts2[0]); + $code = str_replace('
', '', $code); + $code = str_replace('
', '', $code); + $code = str_replace('<', '<', $code); + $newStr .= '' . $code . ''; + $newStr .= $parts2[1]; + } else { + $newStr .= $p; + } + } + } else { + $newStr = $text; + } + + return $newStr; + } } diff --git a/libraries/Controllers/AuthController.php b/libraries/Controllers/AuthController.php index 745a9a1..3bea855 100644 --- a/libraries/Controllers/AuthController.php +++ b/libraries/Controllers/AuthController.php @@ -28,17 +28,28 @@ use Sakura\Utils; */ class AuthController extends Controller { - protected function touchRateLimit($user, $mode = 0) + /** + * Touch the login rate limit. + * + * @param $user int The ID of the user that attempted to log in. + * @param $sucess bool Whether the login attempt was successful. + */ + protected function touchRateLimit($user, $success = false) { DB::table('login_attempts') ->insert([ - 'attempt_success' => $mode, + 'attempt_success' => $success ? 1 : 0, 'attempt_timestamp' => time(), - 'attempt_ip' => Net::pton(Net::IP()), + 'attempt_ip' => Net::pton(Net::ip()), 'user_id' => $user, ]); } + /** + * End the current session. + * + * @return string + */ public function logout() { // Check if user is logged in @@ -65,11 +76,21 @@ class AuthController extends Controller return Template::render('global/information'); } + /** + * Get the login page. + * + * @return string + */ public function loginGet() { return Template::render('auth/login'); } + /** + * Do a login attempt. + * + * @return string + */ public function loginPost() { // Preliminarily set login to failed @@ -91,7 +112,7 @@ class AuthController extends Controller // Check if we haven't hit the rate limit $rates = DB::table('login_attempts') - ->where('attempt_ip', Net::pton(Net::IP())) + ->where('attempt_ip', Net::pton(Net::ip())) ->where('attempt_timestamp', '>', time() - 1800) ->where('attempt_success', '0') ->count(); @@ -172,7 +193,7 @@ class AuthController extends Controller Config::get('cookie_path') ); - $this->touchRateLimit($user->id, 1); + $this->touchRateLimit($user->id, true); $success = 1; @@ -189,12 +210,17 @@ class AuthController extends Controller return Template::render('global/information'); } + /** + * Get the registration page. + * + * @return string + */ public function registerGet() { // Attempt to check if a user has already registered from the current IP $getUserIP = DB::table('users') - ->where('register_ip', Net::pton(Net::IP())) - ->orWhere('last_ip', Net::pton(Net::IP())) + ->where('register_ip', Net::pton(Net::ip())) + ->orWhere('last_ip', Net::pton(Net::ip())) ->get(); if ($getUserIP) { @@ -207,6 +233,11 @@ class AuthController extends Controller return Template::render('auth/register'); } + /** + * Do a registration attempt. + * + * @return string + */ public function registerPost() { // Preliminarily set registration to failed @@ -366,6 +397,11 @@ class AuthController extends Controller return Template::render('global/information'); } + /** + * Do a activation attempt. + * + * @return string + */ public function activate() { // Preliminarily set activation to failed @@ -426,11 +462,21 @@ class AuthController extends Controller return Template::render('global/information'); } + /** + * Get the reactivation request form. + * + * @return string + */ public function reactivateGet() { return Template::render('auth/reactivate'); } + /** + * Do a reactivation preparation attempt. + * + * @return string + */ public function reactivatePost() { // Preliminarily set registration to failed @@ -498,11 +544,21 @@ class AuthController extends Controller return Template::render('global/information'); } + /** + * Get the password reset forum. + * + * @return string + */ public function resetPasswordGet() { return Template::render('auth/resetpassword'); } + /** + * Do a password reset attempt. + * + * @return string + */ public function resetPasswordPost() { // Preliminarily set action to failed diff --git a/libraries/Controllers/FileController.php b/libraries/Controllers/FileController.php index f5bb5be..c4617b3 100644 --- a/libraries/Controllers/FileController.php +++ b/libraries/Controllers/FileController.php @@ -8,9 +8,9 @@ namespace Sakura\Controllers; use Sakura\Config; -use Sakura\User; use Sakura\File; use Sakura\Perms\Site; +use Sakura\User; /** * File controller, handles user uploads like avatars. @@ -20,18 +20,28 @@ use Sakura\Perms\Site; */ class FileController extends Controller { - private function serveImage($data, $mime, $name) + /** + * The base for serving a file. + * + * @return string + */ + private function serve($data, $mime, $name) { // Add original filename - header('Content-Disposition: inline; filename="' . $name . '"'); + header("Content-Disposition: inline; filename={$name}"); // Set content type - header('Content-Type: ' . $mime); + header("Content-Type: {$mime}"); // Return image data return $data; } + /** + * Attempt to get an avatar. + * + * @return string + */ public function avatar($id = 0) { global $templateName; @@ -72,26 +82,32 @@ class FileController extends Controller $user = User::construct($id); if ($user->permission(Site::DEACTIVATED)) { - return $this->serveImage($deactive['data'], $deactive['mime'], $deactive['name']); + return $this->serve($deactive['data'], $deactive['mime'], $deactive['name']); } - if ($user->checkBan() || $user->permission(Site::RESTRICTED)) { - return $this->serveImage($banned['data'], $banned['mime'], $banned['name']); + if ($user->checkBan() + || $user->permission(Site::RESTRICTED)) { + return $this->serve($banned['data'], $banned['mime'], $banned['name']); } if (!$user->avatar) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + return $this->serve($none['data'], $none['mime'], $none['name']); } $serve = new File($user->avatar); if (!$serve->id) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + return $this->serve($none['data'], $none['mime'], $none['name']); } - return $this->serveImage($serve->data, $serve->mime, $serve->name); + return $this->serve($serve->data, $serve->mime, $serve->name); } + /** + * Attempt to get a background. + * + * @return string + */ public function background($id = 0) { global $templateName; @@ -104,24 +120,32 @@ class FileController extends Controller ]; if (!$id) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + return $this->serve($none['data'], $none['mime'], $none['name']); } $user = User::construct($id); - if ($user->permission(Site::DEACTIVATED) || $user->checkBan() || $user->permission(Site::RESTRICTED) || !$user->background) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + if ($user->permission(Site::DEACTIVATED) + || $user->checkBan() + || $user->permission(Site::RESTRICTED) + || !$user->background) { + return $this->serve($none['data'], $none['mime'], $none['name']); } $serve = new File($user->background); if (!$serve->id) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + return $this->serve($none['data'], $none['mime'], $none['name']); } - return $this->serveImage($serve->data, $serve->mime, $serve->name); + return $this->serve($serve->data, $serve->mime, $serve->name); } + /** + * Attempt to get a profile header. + * + * @return string + */ public function header($id = 0) { global $templateName; @@ -134,21 +158,24 @@ class FileController extends Controller ]; if (!$id) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + return $this->serve($none['data'], $none['mime'], $none['name']); } $user = User::construct($id); - if ($user->permission(Site::DEACTIVATED) || $user->checkBan() || $user->permission(Site::RESTRICTED) || !$user->header) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + if ($user->permission(Site::DEACTIVATED) + || $user->checkBan() + || $user->permission(Site::RESTRICTED) + || !$user->header) { + return $this->serve($none['data'], $none['mime'], $none['name']); } $serve = new File($user->header); if (!$serve->id) { - return $this->serveImage($none['data'], $none['mime'], $none['name']); + return $this->serve($none['data'], $none['mime'], $none['name']); } - return $this->serveImage($serve->data, $serve->mime, $serve->name); + return $this->serve($serve->data, $serve->mime, $serve->name); } } diff --git a/libraries/Controllers/ForumController.php b/libraries/Controllers/ForumController.php index a4d342c..63d59e9 100644 --- a/libraries/Controllers/ForumController.php +++ b/libraries/Controllers/ForumController.php @@ -29,7 +29,7 @@ class ForumController extends Controller /** * Serves the forum index. * - * @return mixed HTML for the forum index. + * @return string HTML for the forum index. */ public function index() { @@ -119,6 +119,11 @@ class ForumController extends Controller return Template::render('forum/index'); } + /** + * Get a forum page. + * + * @return string + */ public function forum($id = 0) { global $currentUser; @@ -182,6 +187,11 @@ class ForumController extends Controller return Template::render('forum/viewforum'); } + /** + * Mark a forum as read. + * + * @return string + */ public function markForumRead($id = 0) { global $currentUser; @@ -246,6 +256,11 @@ class ForumController extends Controller return Template::render('global/information'); } + /** + * View a thread. + * + * @return string + */ public function thread($id = 0) { global $currentUser; @@ -283,6 +298,11 @@ class ForumController extends Controller return Template::render('forum/viewtopic'); } + /** + * Moderate a thread. + * + * @return string + */ public function threadModerate($id = 0) { global $currentUser; @@ -409,14 +429,17 @@ class ForumController extends Controller } // Set the variables - Template::vars([ - 'page' => compact('message', 'redirect'), - ]); + Template::vars(compact('message', 'redirect')); // Print page contents return Template::render('global/information'); } + /** + * Redirect to the position of a post in a thread. + * + * @return mixed + */ public function post($id = 0) { global $currentUser; @@ -462,6 +485,11 @@ class ForumController extends Controller return header("Location: {$threadLink}#p{$post->id}"); } + /** + * Get the raw text of a post. + * + * @return string + */ public function postRaw($id = 0) { global $currentUser; @@ -485,6 +513,11 @@ class ForumController extends Controller return $post->text; } + /** + * Reply to a thread. + * + * @return string + */ public function threadReply($id = 0) { global $currentUser; @@ -571,6 +604,11 @@ class ForumController extends Controller return header("Location: {$postLink}"); } + /** + * Create a thread. + * + * @return string + */ public function createThread($id = 0) { global $currentUser; @@ -664,6 +702,11 @@ class ForumController extends Controller return Template::render('forum/viewtopic'); } + /** + * Edit a post. + * + * @return string + */ public function editPost($id = 0) { global $currentUser; @@ -781,6 +824,11 @@ class ForumController extends Controller return header("Location: {$postLink}"); } + /** + * Delete a post. + * + * @return string + */ public function deletePost($id = 0) { global $currentUser; diff --git a/libraries/Controllers/HelperController.php b/libraries/Controllers/HelperController.php index 1297f2f..fd01ea9 100644 --- a/libraries/Controllers/HelperController.php +++ b/libraries/Controllers/HelperController.php @@ -17,9 +17,14 @@ use Sakura\BBcode; */ class HelperController extends Controller { + /** + * Parsed BBcode from a post request + * + * @return string The parsed BBcode + */ public function bbcodeParse() { - $text = isset($_POST['text']) ? $_POST['text'] : null; + $text = isset($_POST['text']) ? $_POST['text'] : ''; $text = BBcode::toHTML($text); diff --git a/libraries/Controllers/MetaController.php b/libraries/Controllers/MetaController.php index 66ba153..98f7487 100644 --- a/libraries/Controllers/MetaController.php +++ b/libraries/Controllers/MetaController.php @@ -12,7 +12,6 @@ use Sakura\DB; use Sakura\News; use Sakura\Template; use Sakura\User; -use Sakura\Users; /** * Meta page controllers (sections that aren't big enough to warrant a dedicated controller). @@ -29,6 +28,38 @@ class MetaController extends Controller */ public function index() { + // Get the newest user + $newestUserId = DB::table('users') + ->where('rank_main', '!=', Config::get('restricted_rank_id')) + ->where('rank_main', '!=', Config::get('deactive_rank_id')) + ->orderBy('user_id', 'desc') + ->limit(1) + ->get(['user_id']); + $newestUser = User::construct($newestUserId ? $newestUserId[0]->user_id : 0); + + // Get all the currently online users + $timeRange = time() - Config::get('max_online_time'); + + // Create a storage variable + $onlineUsers = []; + + // Get all online users + $getOnline = DB::table('users') + ->where('user_last_online', '>', $timeRange) + ->get(['user_id']); + $getOnline = array_column($getOnline, 'user_id'); + + foreach ($getOnline as $user) { + $user = User::construct($user); + + // Do a second check + if (!$user->isOnline()) { + continue; + } + + $onlineUsers[$user->id] = $user; + } + // Merge index specific stuff with the global render data Template::vars([ 'news' => new News(Config::get('site_news_category')), @@ -38,14 +69,14 @@ class MetaController extends Controller ->where('password_algo', '!=', 'disabled') ->whereNotIn('rank_main', [1, 10]) ->count(), - 'newestUser' => User::construct(Users::getNewestUserId()), + 'newestUser' => $newestUser, 'lastRegDate' => date_diff( - date_create(date('Y-m-d', User::construct(Users::getNewestUserId())->registered)), + date_create(date('Y-m-d', $newestUser->registered)), date_create(date('Y-m-d')) )->format('%a'), 'topicCount' => DB::table('topics')->count(), 'postCount' => DB::table('posts')->count(), - 'onlineUsers' => Users::checkAllOnline(), + 'onlineUsers' => $onlineUsers, ], ]); @@ -153,14 +184,6 @@ class MetaController extends Controller */ public function search() { - // Set parse variables - Template::vars([ - 'page' => [ - 'title' => 'Search', - ], - ]); - - // Print page contents return Template::render('main/search'); } } diff --git a/libraries/Controllers/PremiumController.php b/libraries/Controllers/PremiumController.php index 5e15c00..ba886df 100644 --- a/libraries/Controllers/PremiumController.php +++ b/libraries/Controllers/PremiumController.php @@ -9,12 +9,10 @@ namespace Sakura\Controllers; use Exception; use Sakura\Config; -use Sakura\Template; -use Sakura\User; -use Sakura\Users; -use Sakura\Utils; use Sakura\Payments; use Sakura\Perms\Site; +use Sakura\Router; +use Sakura\Template; /** * Premium pages controller. @@ -24,144 +22,164 @@ use Sakura\Perms\Site; */ class PremiumController extends Controller { + /** + * The amount of premium a user received per period. + */ + const PERIOD_PER_PAYMENT = 2628000; + + /** + * Constructor. + */ + public function __construct() + { + Payments::init(); + } + + /** + * Returns the premium purchase index. + * + * @return mixed + */ public function index() { - global $currentUser, $urls; + global $currentUser; - // Switch between modes (we only allow this to be used by logged in user) - if (isset($_REQUEST['mode']) - && Users::checkLogin() - && $currentUser->permission(Site::OBTAIN_PREMIUM)) { - // Initialise Payments class - if (!Payments::init()) { - header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true'); - } else { - switch ($_REQUEST['mode']) { - // Create the purchase - case 'purchase': - // Compare time and session so we know the link isn't forged - if (!isset($_REQUEST['time']) - || $_REQUEST['time'] < time() - 1000) { - return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true'); - } + $price = Config::get('premium_price_per_month'); + $amountLimit = Config::get('premium_amount_max'); - // Match session ids for the same reason - if (!isset($_REQUEST['session']) - || $_REQUEST['session'] != session_id()) { - return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true'); - } + Template::vars(compact('price', 'amountLimit')); - // Half if shit isn't gucci - if (!isset($_POST['months']) - || !is_numeric($_POST['months']) - || (int) $_POST['months'] < 1 - || (int) $_POST['months'] > Config::get('premium_amount_max')) { - return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true'); - } else { - // Calculate the total - $total = (float) Config::get('premium_price_per_month') * (int) $_POST['months']; - $total = number_format($total, 2, '.', ''); - - // Generate item name - $itemName = Config::get('sitename') - . ' Premium - ' - . (string) $_POST['months'] - . ' month' - . ((int) $_POST['months'] == 1 ? '' : 's'); - - // Attempt to create a transaction - if ($transaction = Payments::createTransaction( - $total, - $itemName, - Config::get('sitename') . ' Premium Purchase', - 'http' . (isset($_SERVER['HTTPS']) ? 's' : '') . '://' . Config::get('url_main') . $urls->format('SITE_PREMIUM') - )) { - // Store the amount of months in the global session array - $_SESSION['premiumMonths'] = (int) $_POST['months']; - - return header('Location: ' . $transaction); - } else { - return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true'); - } - } - - // Finalising the purchase - case 'finish': - // Check if the success GET request is set and is true - if (isset($_GET['success']) - && isset($_GET['paymentId']) - && isset($_GET['PayerID']) - && isset($_SESSION['premiumMonths'])) { - // Attempt to complete the transaction - try { - $finalise = Payments::completeTransaction($_GET['paymentId'], $_GET['PayerID']); - } catch (Exception $e) { - return trigger_error('Something went horribly wrong.', E_USER_ERROR); - } - - // Attempt to complete the transaction - if ($finalise) { - // Make the user premium - Users::addUserPremium($currentUser->id, (2628000 * $_SESSION['premiumMonths'])); - Users::updatePremiumMeta($currentUser->id); - Utils::updatePremiumTracker( - $currentUser->id, - ((float) Config::get('premium_price_per_month') * $_SESSION['premiumMonths']), - $currentUser->username - . ' bought premium for ' - . $_SESSION['premiumMonths'] - . ' month' - . ($_SESSION['premiumMonths'] == 1 ? '' : 's') - . '.' - ); - - // Redirect to the complete - return header('Location: ' . $urls->format('SITE_PREMIUM') . '?mode=complete'); - } - } - - return header('Location: ' . $urls->format('SITE_PREMIUM') . '?fail=true'); - - case 'complete': - // Set parse variables - Template::vars([ - 'page' => [ - 'expiration' => ($prem = $currentUser->isPremium()[2]) !== null ? $prem : 0, - ], - ]); - - // Print page contents - return Template::render('main/premiumcomplete'); - - default: - return header('Location: ' . $urls->format('SITE_PREMIUM')); - - } - } - } - - // Set parse variables - Template::vars([ - 'page' => [ - 'fail' => isset($_GET['fail']), - 'price' => Config::get('premium_price_per_month'), - 'current' => $currentUser->isPremium(), - 'amount_max' => Config::get('premium_amount_max'), - ], - ]); - - // Print page contents return Template::render('main/support'); } - public function tracker() + /** + * Handles a purchase request. + * + * @return mixed + */ + public function purchase() { - // Set parse variables - Template::vars([ - 'tracker' => Utils::getPremiumTrackerData(), - ]); + global $currentUser; - // Print page contents - return Template::render('main/supporttracker'); + // Get values from post + $session = isset($_POST['session']) ? $_POST['session'] : ''; + $months = isset($_POST['months']) ? $_POST['months'] : 0; + + // Check if the session is valid + if ($session !== session_id() + || $currentUser->permission(Site::DEACTIVATED) + || !$currentUser->permission(Site::OBTAIN_PREMIUM)) { + $message = "You are not allowed to get premium!"; + $redirect = Router::route('premium.index'); + + Template::vars(compact('message', 'redirect')); + + return Template::render('global/information'); + } + + // Fetch the limit + $amountLimit = Config::get('premium_amount_max'); + + // Check months + if ($months < 1 + || $months > $amountLimit) { + $message = "An incorrect amount of months was specified, stop messing with the source."; + $redirect = Router::route('premium.index'); + + Template::vars(compact('message', 'redirect')); + + return Template::render('global/information'); + } + + $pricePerMonth = Config::get('premium_price_per_month'); + $total = number_format($pricePerMonth * $months, 2, '.', ''); + + $siteName = Config::get('sitename'); + $multiMonths = $months !== 1 ? 's' : ''; + + $siteUrl = 'http' + . (isset($_SERVER['HTTPS']) ? 's' : '') + . "://{$_SERVER['SERVER_NAME']}" + . ($_SERVER['SERVER_PORT'] != 80 ? ":{$_SERVER['SERVER_PORT']}" : ''); + $handlerRoute = Router::route('premium.handle'); + + $itemName = "{$siteName} Premium - {$months} month{$multiMonths}"; + $transactionName = "{$siteName} premium purchase"; + $handlerUrl = "{$siteUrl}{$handlerRoute}"; + + // Create the transaction + $transaction = Payments::createTransaction( + $total, + $itemName, + $transactionName, + $handlerUrl + ); + + // Attempt to create a transaction + if (!$transaction) { + $message = "Something went wrong while preparing the transaction."; + $redirect = Router::route('premium.index'); + + Template::vars(compact('message', 'redirect')); + + return Template::render('global/information'); + } + + // Store the amount of months in the global session array + $_SESSION['premiumMonths'] = (int) $months; + + return header("Location: {$transaction}"); + } + + /** + * Handles the data returned by PayPal. + * + * @return mixed + */ + public function handle() + { + global $currentUser; + + $success = isset($_GET['success']); + $payment = isset($_GET['paymentId']) ? $_GET['paymentId'] : null; + $payer = isset($_GET['PayerID']) ? $_GET['PayerID'] : null; + $months = isset($_SESSION['premiumMonths']) ? $_SESSION['premiumMonths'] : null; + + $successRoute = Router::route('premium.complete'); + $failRoute = Router::route('premium.index') . "?fail=true"; + + if (!$success + || !$payment + || !$payer + || !$months) { + return header("Location: {$failRoute}"); + } + + // Attempt to complete the transaction + try { + $finalise = Payments::completeTransaction($_GET['paymentId'], $_GET['PayerID']); + } catch (Exception $e) { + $finalise = false; + } + + if (!$finalise) { + return header("Location: {$failRoute}"); + } + + $pricePerMonth = Config::get('premium_price_per_month'); + + $currentUser->addPremium(self::PERIOD_PER_PAYMENT * $months); + + return header("Location: {$successRoute}"); + } + + /** + * Presents the user with a thank you <3. + * + * @return mixed + */ + public function complete() + { + return Template::render('main/premiumcomplete'); } } diff --git a/libraries/Controllers/UserController.php b/libraries/Controllers/UserController.php index a169aff..57bc04f 100644 --- a/libraries/Controllers/UserController.php +++ b/libraries/Controllers/UserController.php @@ -49,46 +49,18 @@ class UserController extends Controller // Redirect if so if ($check) { - Template::vars([ - 'page' => [ - 'message' => 'The user this profile belongs to changed their username, you are being redirected.', - 'redirect' => Router::route('user.profile', $check[0]->user_id), - ], - ]); + $message = "This user changed their username! Redirecting you to their new profile."; + $redirect = Router::route('user.profile', $check[0]->user_id); + + Template::vars(compact('message', 'redirect')); // Print page contents return Template::render('global/information'); } } - // Check if we're trying to restrict - if (isset($_GET['restrict']) && $_GET['restrict'] == session_id() && $currentUser->permission(\Sakura\Perms\Manage::CAN_RESTRICT_USERS, \Sakura\Perms::MANAGE)) { - // Check restricted status - $restricted = $profile->permission(\Sakura\Perms\Site::RESTRICTED); - - if ($restricted) { - $profile->removeRanks([Config::get('restricted_rank_id')]); - $profile->addRanks([2]); - } else { - $profile->addRanks([Config::get('restricted_rank_id')]); - $profile->removeRanks(array_keys($profile->ranks)); - } - - Template::vars([ - 'page' => [ - 'message' => 'Toggled the restricted status of the user.', - 'redirect' => Router::route('user.profile', $profile->id), - ], - ]); - - // Print page contents - return Template::render('global/information'); - } - // Set parse variables - Template::vars([ - 'profile' => $profile, - ]); + Template::vars(compact('profile')); // Print page contents return Template::render('main/profile'); @@ -128,17 +100,21 @@ class UserController extends Controller // Get the active rank $rank = array_key_exists($rank, $ranks) ? $rank : ($rank ? 0 : 2); + // Get members per page + $membersPerPage = Config::get('members_per_page'); + // Set parse variables - Template::vars([ - 'ranks' => $ranks, - 'rank' => $rank, - 'membersPerPage' => Config::get('members_per_page'), - ]); + Template::vars(compact('ranks', 'rank', 'membersPerPage')); // Render the template return Template::render('main/memberlist'); } + /** + * Get the notification JSON object for the currently authenticated user. + * + * @return string The JSON object. + */ public function notifications() { // TODO: add friend on/offline messages @@ -153,6 +129,13 @@ class UserController extends Controller ); } + /** + * Mark a notification as read. + * + * @param int The ID of the notification. + * + * @return string Not entirely set on this one yet but 1 for success and 0 for fail. + */ public function markNotification($id = 0) { global $currentUser; diff --git a/libraries/Forum/Post.php b/libraries/Forum/Post.php index 831e21a..7d7e252 100644 --- a/libraries/Forum/Post.php +++ b/libraries/Forum/Post.php @@ -168,7 +168,7 @@ class Post 'topic_id' => $thread->id, 'forum_id' => $thread->forum, 'poster_id' => $poster->id, - 'poster_ip' => Net::IP(), + 'poster_ip' => Net::ip(), 'post_time' => time(), 'post_subject' => $subject, 'post_text' => $text, @@ -206,7 +206,7 @@ class Post 'topic_id' => $thread->id, 'forum_id' => $thread->forum, 'poster_id' => $this->poster->id, - 'poster_ip' => Net::pton(Net::IP()), + 'poster_ip' => Net::pton(Net::ip()), 'post_time' => $this->time, 'post_subject' => $this->subject, 'post_text' => $this->text, diff --git a/libraries/Net.php b/libraries/Net.php index ae73554..3545ab4 100644 --- a/libraries/Net.php +++ b/libraries/Net.php @@ -20,7 +20,7 @@ class Net * * @return string The IP. */ - public static function IP() + public static function ip() { return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '::1'; } @@ -163,7 +163,7 @@ class Net { // Generate an address from the mask $addr = str_repeat("f", $mask / 4); - + // Append uneven bit switch ($mask % 4) { case 1: @@ -178,10 +178,10 @@ class Net $addr .= 'e'; break; } - + // Pad the address with zeroes $addr = str_pad($addr, 32, '0'); - + // Pack the address $addr = pack('H*', $addr); diff --git a/libraries/Session.php b/libraries/Session.php index 0dcb1d3..adce85f 100644 --- a/libraries/Session.php +++ b/libraries/Session.php @@ -98,7 +98,7 @@ class Session DB::table('sessions') ->insert([ 'user_id' => $this->userId, - 'user_ip' => Net::pton(Net::IP()), + 'user_ip' => Net::pton(Net::ip()), 'user_agent' => Utils::cleanString(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'No user agent header.'), 'session_key' => $session, 'session_start' => time(), @@ -141,7 +141,7 @@ class Session if ($ipCheck) { // Split both IPs up $sessionIP = explode('.', $session[0]->user_ip); - $userIP = explode('.', Net::IP()); + $userIP = explode('.', Net::ip()); // Take 1 off the ipCheck variable so it's equal to the array keys $ipCheck = $ipCheck - 1; diff --git a/libraries/User.php b/libraries/User.php index 1a6fc09..47c2fd5 100644 --- a/libraries/User.php +++ b/libraries/User.php @@ -9,6 +9,7 @@ namespace Sakura; use Sakura\Perms; use Sakura\Perms\Site; +use stdClass; /** * Everything you'd ever need from a specific user. @@ -268,8 +269,8 @@ class User 'password_iter' => $password[1], 'email' => $emailClean, 'rank_main' => 0, - 'register_ip' => Net::pton(Net::IP()), - 'last_ip' => Net::pton(Net::IP()), + 'register_ip' => Net::pton(Net::ip()), + 'last_ip' => Net::pton(Net::ip()), 'user_registered' => time(), 'user_last_online' => 0, 'user_country' => Utils::getCountryCode(), @@ -913,37 +914,99 @@ class User } /** - * Does this user have premium? + * Add premium in seconds. * - * @return array Premium status information. + * @param int $seconds The amount of seconds. + * + * @return int The new expiry date. */ - public function isPremium() + public function addPremium($seconds) { - - // Check if the user has static premium - if ($this->permission(Site::STATIC_PREMIUM)) { - return [2, 0, time() + 1]; - } - - // Attempt to retrieve the premium record from the database - $getRecord = DB::table('premium') + // Check if there's already a record of premium for this user in the database + $getUser = DB::table('premium') ->where('user_id', $this->id) ->get(); - // If nothing was returned just return false - if (empty($getRecord)) { - return [0]; + // Calculate the (new) start and expiration timestamp + $start = $getUser ? $getUser[0]->premium_start : time(); + $expire = $getUser ? $getUser[0]->premium_expire + $seconds : time() + $seconds; + + // If the user already exists do an update call, otherwise an insert call + if ($getUser) { + DB::table('premium') + ->where('user_id', $this->id) + ->update([ + 'premium_expire' => $expire, + ]); + } else { + DB::table('premium') + ->insert([ + 'user_id' => $this->id, + 'premium_start' => $start, + 'premium_expire' => $expire, + ]); } - $getRecord = $getRecord[0]; + // Return the expiration timestamp + return $expire; + } - // Check if the Tenshi hasn't expired - if ($getRecord->premium_expire < time()) { - return [0, $getRecord->premium_start, $getRecord->premium_expire]; + /** + * Does this user have premium? + * + * @return int Returns the premium expiration date. + */ + public function isPremium() + { + // Get rank IDs from the db + $premiumRank = (int) Config::get('premium_rank_id'); + $defaultRank = (int) Config::get('default_rank_id'); + + // Fetch expiration date + $expire = $this->premiumInfo()->expire; + + // Check if the user has static premium + if (!$expire + && $this->permission(Site::STATIC_PREMIUM)) { + $expire = time() + 1; } - // Else return the start and expiration date - return [1, $getRecord->premium_start, $getRecord->premium_expire]; + // Check if the user has premium and isn't in the premium rank + if ($expire + && !$this->hasRanks([$premiumRank])) { + // Add the premium rank + $this->addRanks([$premiumRank]); + + // Set it as default + if ($this->mainRankId == $defaultRank) { + $this->setMainRank($premiumRank); + } + } elseif (!$expire + && $this->hasRanks([$premiumRank])) { + $this->removeRanks([$premiumRank]); + + if ($this->mainRankId == $premiumRank) { + $this->setMainRank($defaultRank); + } + } + + return $expire; + } + + public function premiumInfo() + { + // Attempt to retrieve the premium record from the database + $check = DB::table('premium') + ->where('user_id', $this->id) + ->where('premium_expire', '>', time()) + ->get(); + + $return = new stdClass; + + $return->start = $check ? $check[0]->premium_start : 0; + $return->expire = $check ? $check[0]->premium_expire : 0; + + return $return; } /** diff --git a/libraries/Users.php b/libraries/Users.php index 730b4d6..c1d1d16 100644 --- a/libraries/Users.php +++ b/libraries/Users.php @@ -47,22 +47,6 @@ class Users // Check if the session exists and check if the user is activated if ($sessionValid == 0 || $user->permission(Site::DEACTIVATED)) { - // Unset User ID - setcookie( - Config::get('cookie_prefix') . 'id', - 0, - time() - 60, - Config::get('cookie_path') - ); - - // Unset Session ID - setcookie( - Config::get('cookie_prefix') . 'session', - '', - time() - 60, - Config::get('cookie_path') - ); - return false; } @@ -92,9 +76,6 @@ class Users 'user_last_online' => time(), ]); - // Update the premium meta - self::updatePremiumMeta($uid); - // If everything went through return true return [$uid, $sid]; } @@ -250,120 +231,4 @@ class Users // Return the yeahs return $fields; } - - /** - * Get all online users. - * - * @return array Array containing User instances. - */ - public static function checkAllOnline() - { - // Assign time - 500 to a variable - $time = time() - Config::get('max_online_time'); - - $return = []; - - // Get all online users in the past 5 minutes - $getAll = DB::table('users') - ->where('user_last_online', '>', $time) - ->get(); - - foreach ($getAll as $user) { - $return[] = User::construct($user->user_id); - } - - // Return all the online users - return $return; - } - - /** - * Add premium time to a user. - * - * @param int $id The user ID. - * @param int $seconds The amount of extra seconds. - * - * @return array|double|int The new expiry date. - */ - public static function addUserPremium($id, $seconds) - { - // Check if there's already a record of premium for this user in the database - $getUser = DB::table('premium') - ->where('user_id', $id) - ->count(); - - // Calculate the (new) start and expiration timestamp - $start = isset($getUser['premium_start']) ? $getUser['premium_start'] : time(); - $expire = isset($getUser['premium_expire']) ? $getUser['premium_expire'] + $seconds : time() + $seconds; - - // If the user already exists do an update call, otherwise an insert call - if (empty($getUser)) { - DB::table('premium') - ->insert([ - 'user_id' => $id, - 'premium_start' => $start, - 'premium_expire' => $expire, - ]); - } else { - DB::table('premium') - ->where('user_id', $id) - ->update('premium_expire', $expire); - } - - // Return the expiration timestamp - return $expire; - } - - /** - * Process premium meta data. - * - * @param int $id The user ID. - */ - public static function updatePremiumMeta($id) - { - // Get the ID for the premium user rank from the database - $premiumRank = Config::get('premium_rank_id'); - $excepted = Config::get('restricted_rank_id'); - - // Create user object - $user = User::construct($id); - - // Run the check - $check = $user->isPremium(); - - // Check if the user has premium - if ($check[0] && !array_key_exists($excepted, $user->ranks)) { - // If so add the rank to them - $user->addRanks([$premiumRank]); - - // Check if the user's default rank is standard user and update it to premium - if ($user->mainRankId == 2) { - $user->setMainRank($premiumRank); - } - } elseif (!$check[0]) { - // Remove the expired entry - DB::table('premium') - ->where('user_id', $user->id) - ->delete(); - - // Else remove the rank from them - $user->removeRanks([$premiumRank]); - } - } - - /** - * Get the newest member's ID. - * - * @return int The user ID. - */ - public static function getNewestUserId() - { - $get = DB::table('users') - ->where('rank_main', '!=', Config::get('restricted_rank_id')) - ->where('rank_main', '!=', Config::get('deactive_rank_id')) - ->orderBy('user_id', 'desc') - ->limit(1) - ->get(['user_id']); - - return $get ? $get[0]->user_id : 0; - } } diff --git a/libraries/Utils.php b/libraries/Utils.php index 2c0864c..3a01dd2 100644 --- a/libraries/Utils.php +++ b/libraries/Utils.php @@ -263,7 +263,7 @@ class Utils // Attempt to get country code using PHP's built in geo thing if (function_exists("geoip_country_code_by_name")) { try { - $code = geoip_country_code_by_name(Net::IP()); + $code = geoip_country_code_by_name(Net::ip()); // Check if $code is anything if ($code) { @@ -346,94 +346,4 @@ class Utils // Return the formatted string return $bytes; } - - /** - * Get the premium tracker data. - * - * @return array The premium tracker data. - */ - public static function getPremiumTrackerData() - { - // Create data array - $data = []; - - // Get database stuff - $table = DB::table('premium_log') - ->orderBy('transaction_id', 'desc') - ->get(); - - // Add raw table data to data array - $data['table'] = $table; - - // Create balance entry - $data['balance'] = 0.0; - - // users - $data['users'] = []; - - // Calculate the thing - foreach ($table as $row) { - // Calculate balance - $data['balance'] = $data['balance'] + $row->transaction_amount; - - // Add userdata to table - if (!array_key_exists($row->user_id, $data['users'])) { - $data['users'][$row->user_id] = User::construct($row->user_id); - } - } - - // Return the data - return $data; - } - - /** - * Add a new entry to the tracker. - * - * @param int $id The user ID. - * @param float $amount The amount of money. - * @param string $comment A little information. - */ - public static function updatePremiumTracker($id, $amount, $comment) - { - DB::table('premium_log') - ->insert([ - 'user_id' => $id, - 'transaction_amount' => $amount, - 'transaction_date' => time(), - 'transaction_comment' => $comment, - ]); - } - - /** - * Clean up the contents of tags. - * - * @param string $text Dirty - * - * @return string Clean - */ - public static function fixCodeTags($text) - { - $parts = explode('', $text); - $newStr = ''; - - if (count($parts) > 1) { - foreach ($parts as $p) { - $parts2 = explode('', $p); - if (count($parts2) > 1) { - $code = str_replace('
', '', $parts2[0]); - $code = str_replace('
', '', $code); - $code = str_replace('
', '', $code); - $code = str_replace('<', '<', $code); - $newStr .= '' . $code . ''; - $newStr .= $parts2[1]; - } else { - $newStr .= $p; - } - } - } else { - $newStr = $text; - } - - return $newStr; - } } diff --git a/routes.php b/routes.php index 0ee16b2..42a184a 100644 --- a/routes.php +++ b/routes.php @@ -106,9 +106,11 @@ Router::get('/a/{id}', 'FileController@avatar', 'file.avatar'); Router::get('/bg/{id}', 'FileController@background', 'file.background'); // Premium -Router::group(['prefix' => 'support'], function () { +Router::group(['prefix' => 'support', 'before' => 'loginCheck'], function () { Router::get('/', 'PremiumController@index', 'premium.index'); - Router::get('/tracker', 'PremiumController@tracker', 'premium.tracker'); + Router::get('/handle', 'PremiumController@handle', 'premium.handle'); + Router::get('/complete', 'PremiumController@complete', 'premium.complete'); + Router::post('/purchase', 'PremiumController@purchase', 'premium.purchase'); }); // Helpers diff --git a/sakura.php b/sakura.php index 8ef0d95..f27a147 100644 --- a/sakura.php +++ b/sakura.php @@ -8,7 +8,7 @@ namespace Sakura; // Define Sakura version -define('SAKURA_VERSION', 20160326); +define('SAKURA_VERSION', 20160327); // Define Sakura Path define('ROOT', __DIR__ . '/'); diff --git a/templates/yuuno/forum/index.twig b/templates/yuuno/forum/index.twig index 0020fa1..9d47259 100644 --- a/templates/yuuno/forum/index.twig +++ b/templates/yuuno/forum/index.twig @@ -51,7 +51,7 @@

{{ activePoster.username }}

- {% if activePoster.isPremium[0] %}Tenshi {% endif %}{{ activePoster.country }} {{ activePoster.title }} + {% if activePoster.isPremium %}Tenshi {% endif %}{{ activePoster.country }} {{ activePoster.title }}
diff --git a/templates/yuuno/forum/viewtopic.twig b/templates/yuuno/forum/viewtopic.twig index 6b0bc86..0bdb960 100644 --- a/templates/yuuno/forum/viewtopic.twig +++ b/templates/yuuno/forum/viewtopic.twig @@ -86,7 +86,7 @@ {% endif %}
{{ post.poster.title }}
- Tenshi {{ post.poster.country(true) }}{% if post.poster.id == (thread.posts|first).poster.id %} OP{% endif %} + Tenshi {{ post.poster.country(true) }}{% if post.poster.id == (thread.posts|first).poster.id %} OP{% endif %} {% if session.checkLogin %}
{% if (user.id == post.poster.id and forum.permission(constant('Sakura\\Perms\\Forum::EDIT_OWN'), user.id)) or forum.permission(constant('Sakura\\Perms\\Forum::EDIT_ANY'), user.id) %} @@ -141,7 +141,7 @@ {{ user.username }}
{{ user.title }}
- Tenshi {{ user.country(true) }}{% if user.id == (thread.posts|first).poster.id %} OP{% endif %} + Tenshi {{ user.country(true) }}{% if user.id == (thread.posts|first).poster.id %} OP{% endif %}
diff --git a/templates/yuuno/global/information.twig b/templates/yuuno/global/information.twig index ac7ae1a..3fd41b3 100644 --- a/templates/yuuno/global/information.twig +++ b/templates/yuuno/global/information.twig @@ -7,8 +7,8 @@

Information


- {{ page.message }} - {% if page.redirect %}
Click here if you aren't being redirected.{% endif %} + {{ message }} + {% if redirect %}
Click here if you aren't being redirected.{% endif %}
{% endblock %} diff --git a/templates/yuuno/global/master.twig b/templates/yuuno/global/master.twig index a2b3dab..baff4e0 100644 --- a/templates/yuuno/global/master.twig +++ b/templates/yuuno/global/master.twig @@ -10,8 +10,14 @@ -{% if page.redirect %} - + +{# want to start moving away from page.etc but older files are a thing #} +{% if message is not defined %}{% set message = page.message %}{% endif %} +{% if redirect is not defined %}{% set redirect = page.redirect %}{% endif %} +{% if redirectTimeout is not defined %}{% set redirectTimeout = page.redirectTimeout %}{% endif %} + +{% if redirect %} + {% endif %} @@ -63,7 +69,7 @@ notifyUI({ "title": "An error has occurred!", "text": "There was a problem while executing the JavaScript code for this page: " + msg + ", URL: " + url + ", Line: " + line + ", Column: " + col + ". Please report this to a developer.", - "img": "FONT:fa-warning" + "image": "FONT:fa-warning" }); } diff --git a/templates/yuuno/main/premiumcomplete.twig b/templates/yuuno/main/premiumcomplete.twig index e4eebf7..24639cc 100644 --- a/templates/yuuno/main/premiumcomplete.twig +++ b/templates/yuuno/main/premiumcomplete.twig @@ -6,6 +6,6 @@

Thank you for your contribution!

-

Your Tenshi will expire on {{ page.expiration|date(sakura.dateFormat) }}.

+

Your Tenshi will expire on {{ user.isPremium|date(sakura.dateFormat) }}.

{% endblock %} diff --git a/templates/yuuno/main/profile.twig b/templates/yuuno/main/profile.twig index 15fd1e0..351b788 100644 --- a/templates/yuuno/main/profile.twig +++ b/templates/yuuno/main/profile.twig @@ -81,7 +81,7 @@

{{ profile.username }}

- {% if profile.isPremium[0] %}Tenshi {% endif %}{{ profile.country }} {{ profile.title }} + {% if profile.isPremium %}Tenshi {% endif %}{{ profile.country }} {{ profile.title }}
Joined diff --git a/templates/yuuno/main/support.twig b/templates/yuuno/main/support.twig index 3cc55a6..42228cf 100644 --- a/templates/yuuno/main/support.twig +++ b/templates/yuuno/main/support.twig @@ -2,8 +2,10 @@ {% block title %}Support {{ sakura.siteName }}{% endblock %} +{% set persistentPremium = user.permission(constant('Sakura\\Perms\\Site::STATIC_PREMIUM')) %} + {% block content %} - {% if page.fail %} + {% if get.fail %}

The payment failed or was cancelled!

Something went wrong while processing the transaction, your PayPal account wasn't charged.

@@ -13,15 +15,14 @@
Support {{ sakura.siteName }}

In order to keep the site, its services and improvements on it going I need money but I'm not that big of a fan of asking for money without giving anything special in return thus Tenshi exists. Tenshi is the name for our supporter rank which gives you access to an extra set of features (which are listed further down on this page). With your help we can keep adding new stuff, get new hardware and keep the site awesome!

-

Ever wonder what happens on the financial side of things? View the donation tracker!

- {% if page.current[0] %} + {% if user.isPremium %}
Your current Tenshi tag
-

{% if page.current[0] == 2 %}Your rank has persistent Tenshi.{% else %}Your Tenshi tag is valid till {{ page.current[2]|date(sakura.dateFormat) }}.{% endif %}

- +

{% if persistentPremium %}Your rank has persistent Tenshi.{% else %}Your Tenshi tag is valid till {{ user.premiumInfo.expire|date(sakura.dateFormat) }}.{% endif %}

+
{% endif %}
@@ -91,7 +92,7 @@
{% if session.checkLogin and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %}
- +
@@ -109,14 +110,14 @@ {% endif %}
{% if session.checkLogin and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %} - {% endif %} {% endblock %}