get_value("user_info_$UserID"); // the !isset($UserInfo['Paranoia']) can be removed after a transition period if (empty($UserInfo) || empty($UserInfo['ID']) || !isset($UserInfo['Paranoia']) || empty($UserInfo['Class'])) { $OldQueryID = G::$DB->get_query_id(); G::$DB->prepared_query(" SELECT m.ID, m.Username, m.PermissionID, m.Paranoia, (donor.UserID IS NOT NULL) AS Donor, i.Warned, i.Avatar, m.Enabled, m.Title, m.Visible, la.Type AS LockedAccount, GROUP_CONCAT(ul.PermissionID SEPARATOR ',') AS Levels FROM users_main AS m INNER JOIN users_info AS i ON (i.UserID = m.ID) LEFT JOIN locked_accounts AS la ON (la.UserID = m.ID) LEFT JOIN users_levels AS ul ON (ul.UserID = m.ID) LEFT JOIN users_levels AS donor ON (donor.UserID = m.ID AND donor.PermissionID = (SELECT ID FROM permissions WHERE Name = 'Donor' LIMIT 1) ) WHERE m.ID = ? GROUP BY m.ID ", $UserID ); $Classes = (new \Gazelle\Manager\User)->classList(); if (!G::$DB->has_results()) { // Deleted user, maybe? $UserInfo = [ 'ID' => $UserID, 'Username' => '', 'PermissionID' => 0, 'Paranoia' => [], 'Donor' => false, 'Warned' => null, 'Avatar' => '', 'Enabled' => 0, 'Title' => '', 'Visible' => '1', 'Levels' => '', 'Class' => 0]; } else { $UserInfo = G::$DB->next_record(MYSQLI_ASSOC, ['Paranoia', 'Title']); $UserInfo['Paranoia'] = unserialize_array($UserInfo['Paranoia']); if ($UserInfo['Paranoia'] === false) { $UserInfo['Paranoia'] = []; } $UserInfo['Class'] = $Classes[$UserInfo['PermissionID']]['Level']; } if (isset($UserInfo['LockedAccount'])) { unset($UserInfo['LockedAccount']); } if (!empty($UserInfo['Levels'])) { $UserInfo['ExtraClasses'] = array_fill_keys(explode(',', $UserInfo['Levels']), 1); } else { $UserInfo['ExtraClasses'] = []; } unset($UserInfo['Levels']); $EffectiveClass = (int)$UserInfo['Class']; foreach ($UserInfo['ExtraClasses'] as $Class => $Val) { $EffectiveClass = max($EffectiveClass, (int)$Classes[$Class]['Level']); } $UserInfo['EffectiveClass'] = $EffectiveClass; G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); G::$DB->set_query_id($OldQueryID); } if (strtotime($UserInfo['Warned']) < time()) { $UserInfo['Warned'] = null; G::$Cache->cache_value("user_info_$UserID", $UserInfo, 2592000); } return $UserInfo; } /** * Gets the heavy user info * Only used for current user * * @param string $UserID The userid to get the information for * @return array fetched heavy info. * Just read the goddamn code, I don't have time to comment this shit. */ public static function user_heavy_info($UserID) { $HeavyInfo = G::$Cache->get_value("user_info_heavy_$UserID"); if (empty($HeavyInfo)) { $QueryID = G::$DB->get_query_id(); G::$DB->prepared_query(' SELECT m.Invites, m.torrent_pass, m.IP, m.CustomPermissions, m.can_leech AS CanLeech, m.IRCKey, i.Info, i.AuthKey, i.RatioWatchEnds, i.RatioWatchDownload, i.StyleID, i.StyleURL, i.DisableInvites, i.DisablePosting, i.DisableUpload, i.DisablePoints, i.DisableWiki, i.DisableAvatar, i.DisablePM, i.DisableRequests, i.DisableForums, i.DisableIRC, i.DisableTagging, i.SiteOptions, i.DownloadAlt, i.RestrictedForums, i.PermittedForums, i.NavItems, i.collages AS Collages, uf.tokens AS FLTokens, m.PermissionID, CASE WHEN uha.UserID IS NULL THEN 1 ELSE 0 END AS AcceptFL FROM users_main AS m INNER JOIN users_info AS i ON (i.UserID = m.ID) INNER JOIN user_flt AS uf ON (uf.user_id = m.ID) LEFT JOIN user_has_attr AS uha ON (uha.UserID = m.ID) LEFT JOIN user_attr as ua ON (ua.ID = uha.UserAttrID AND ua.Name = ?) WHERE m.ID = ? ', 'no-fl-gifts', $UserID ); $HeavyInfo = G::$DB->next_record(MYSQLI_ASSOC, ['CustomPermissions', 'SiteOptions']); if ($HeavyInfo['RatioWatchEnds'] == '') { $HeavyInfo['RatioWatchEnds'] = null; } $HeavyInfo['CustomPermissions'] = unserialize_array($HeavyInfo['CustomPermissions']); if (!empty($HeavyInfo['RestrictedForums'])) { $RestrictedForums = array_map('trim', explode(',', $HeavyInfo['RestrictedForums'])); } else { $RestrictedForums = []; } unset($HeavyInfo['RestrictedForums']); if (!empty($HeavyInfo['PermittedForums'])) { $PermittedForums = array_map('trim', explode(',', $HeavyInfo['PermittedForums'])); } else { $PermittedForums = []; } unset($HeavyInfo['PermittedForums']); if (!empty($HeavyInfo['NavItems'])) { $NavItems = array_map('trim', explode(',', $HeavyInfo['NavItems'])); } else { $NavItems = []; } $HeavyInfo['NavItems'] = $NavItems; G::$DB->query(" SELECT PermissionID FROM users_levels WHERE UserID = $UserID"); $PermIDs = G::$DB->collect('PermissionID'); foreach ($PermIDs AS $PermID) { $Perms = Permissions::get_permissions($PermID); if (!empty($Perms['PermittedForums'])) { $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); } } $Perms = Permissions::get_permissions($HeavyInfo['PermissionID']); unset($HeavyInfo['PermissionID']); if (!empty($Perms['PermittedForums'])) { $PermittedForums = array_merge($PermittedForums, array_map('trim', explode(',', $Perms['PermittedForums']))); } if (!empty($PermittedForums) || !empty($RestrictedForums)) { $HeavyInfo['CustomForums'] = []; foreach ($RestrictedForums as $ForumID) { $HeavyInfo['CustomForums'][$ForumID] = 0; } foreach ($PermittedForums as $ForumID) { $HeavyInfo['CustomForums'][$ForumID] = 1; } } else { $HeavyInfo['CustomForums'] = null; } if (isset($HeavyInfo['CustomForums'][''])) { unset($HeavyInfo['CustomForums']['']); } $HeavyInfo['SiteOptions'] = unserialize_array($HeavyInfo['SiteOptions']); $HeavyInfo['SiteOptions'] = array_merge(static::default_site_options(), $HeavyInfo['SiteOptions']); $HeavyInfo = array_merge($HeavyInfo, $HeavyInfo['SiteOptions']); unset($HeavyInfo['SiteOptions']); G::$DB->set_query_id($QueryID); G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); } return $HeavyInfo; } public static function get_user_nav_items($userId) { $Info = self::user_heavy_info($userId); $UserIds = !empty($Info['NavItems']) ? $Info['NavItems'] : []; $NavItems = self::get_nav_items(); $UserItems = []; foreach ($NavItems as $n) { if (($n['mandatory'] || in_array($n['id'], $UserIds)) || (!count($UserIds) && $n['initial'])) { $UserItems[] = $n; } } return $UserItems; } public static function get_nav_items() { $Items = G::$Cache->get_value("nav_items"); if (!$Items) { $QueryID = G::$DB->get_query_id(); G::$DB->prepared_query(" SELECT id, tag, title, target, tests, test_user, mandatory, initial FROM nav_items"); $Items = G::$DB->to_array("id", MYSQLI_ASSOC); G::$Cache->cache_value("nav_items", $Items, 0); G::$DB->set_query_id($QueryID); } return $Items; } /** * Return the ID of a Username * @param string Username * @return userID if exists, null otherwise */ public static function ID_from_username($name) { $digest = base64_encode(md5($name, true)); $key = "username_id_$digest"; $ID = G::$Cache->get_value($key); if ($ID == -1) { return null; } elseif ($ID === false) { G::$DB->prepared_query("SELECT ID FROM users_main WHERE Username=?", $name); if (!G::$DB->has_results()) { // cache negative hits for a while G::$Cache->cache_value($key, -1, 300); return null; } list($ID) = G::$DB->next_record(); G::$Cache->cache_value($key, $ID, 300); } return $ID; } /** * Does this ID point to an existing user? * @param integer ID * @return boolean */ public static function exists($ID) { G::$DB->prepared_query("SELECT 1 FROM users_main WHERE ID = ?", $ID); return G::$DB->has_results(); } /** * Default settings to use for SiteOptions * @return array */ public static function default_site_options() { return [ 'HttpsTracker' => true ]; } /** * Updates the site options in the database * * @param int $UserID the UserID to set the options for * @param array $NewOptions the new options to set * @return false if $NewOptions is empty, true otherwise */ public static function update_site_options($UserID, $NewOptions) { if (!is_number($UserID)) { error(0); } if (empty($NewOptions)) { return false; } $QueryID = G::$DB->get_query_id(); // Get SiteOptions G::$DB->query(" SELECT SiteOptions FROM users_info WHERE UserID = $UserID"); list($SiteOptions) = G::$DB->next_record(MYSQLI_NUM, false); $SiteOptions = unserialize_array($SiteOptions); $SiteOptions = array_merge(static::default_site_options(), $SiteOptions); // Get HeavyInfo $HeavyInfo = Users::user_heavy_info($UserID); // Insert new/replace old options $SiteOptions = array_merge($SiteOptions, $NewOptions); $HeavyInfo = array_merge($HeavyInfo, $NewOptions); // Update DB G::$DB->prepared_query(' UPDATE users_info SET SiteOptions = ? WHERE UserID = ? ', $UserID, serialize($SiteOptions) ); G::$DB->set_query_id($QueryID); // Update cache G::$Cache->cache_value("user_info_heavy_$UserID", $HeavyInfo, 0); // Update G::$LoggedUser if the options are changed for the current if (G::$LoggedUser['ID'] == $UserID) { G::$LoggedUser = array_merge(G::$LoggedUser, $NewOptions); G::$LoggedUser['ID'] = $UserID; // We don't want to allow userid switching } return true; } /** * Verify a password against a password hash * * @param string $Password password * @param string $Hash password hash * @return bool true on correct password */ public static function check_password($Password, $Hash) { if (empty($Password) || empty($Hash)) { return false; } return password_verify(hash('sha256', $Password), $Hash) || password_verify($Password, $Hash); } public static function check_password_old($Password, $Hash) { if (empty($Password) || empty($Hash)) { return false; } return password_verify($Password, $Hash); } /** * Returns a username string for display * * @param int|string $UserID * @param boolean $Badges whether or not badges (donor, warned, enabled) should be shown * @param boolean $IsWarned -- TODO: Why the fuck do we need this? * @param boolean $IsEnabled -- TODO: Why the fuck do we need this? * @param boolean $Class whether or not to show the class * @param boolean $Title whether or not to show the title * @param boolean $IsDonorForum for displaying donor forum honorific prefixes and suffixes * @return string HTML formatted username */ public static function format_username($UserID, $Badges = false, $IsWarned = true, $IsEnabled = true, $Class = false, $Title = false, $IsDonorForum = false) { if ($UserID == 0) { return 'System'; } $UserInfo = self::user_info($UserID); if ($UserInfo['Username'] == '') { return "Unknown [$UserID]"; } $Str = ''; $Username = $UserInfo['Username']; $Paranoia = $UserInfo['Paranoia']; $Classes = (new \Gazelle\Manager\User)->classList(); if ($UserInfo['Class'] < $Classes[MOD]['Level']) { $OverrideParanoia = check_perms('users_override_paranoia', $UserInfo['Class']); } else { // Don't override paranoia for mods who don't want to show their donor heart $OverrideParanoia = false; } $ShowDonorIcon = (!in_array('hide_donor_heart', $Paranoia) || $OverrideParanoia); $donorMan = new \Gazelle\Manager\Donation; if ($IsDonorForum) { list($Prefix, $Suffix, $HasComma) = $donorMan->titles($UserID); $Username = "$Prefix $Username" . ($HasComma ? ', ' : ' ') . "$Suffix "; } if ($Title) { $Str .= "$Username"; } else { $Str .= "$Username"; } if ($Badges) { $DonorRank = $donorMan->rank($UserID); if ($DonorRank == 0 && $UserInfo['Donor'] == 1) { $DonorRank = 1; } if ($ShowDonorIcon && $DonorRank > 0) { $IconLink = 'donate.php'; $IconImage = 'donor.png'; $IconText = 'Donor'; $DonorHeart = $DonorRank; $SpecialRank = $donorMan->specialRank($UserID); $EnabledRewards = $donorMan->enabledRewards($UserID); $DonorRewards = $donorMan->rewards($UserID); if ($EnabledRewards['HasDonorIconMouseOverText'] && !empty($DonorRewards['IconMouseOverText'])) { $IconText = display_str($DonorRewards['IconMouseOverText']); } if ($EnabledRewards['HasDonorIconLink'] && !empty($DonorRewards['CustomIconLink'])) { $IconLink = display_str($DonorRewards['CustomIconLink']); } if ($EnabledRewards['HasCustomDonorIcon'] && !empty($DonorRewards['CustomIcon'])) { $IconImage = ImageTools::process($DonorRewards['CustomIcon'], false, 'donoricon', $UserID); } else { if ($SpecialRank === MAX_SPECIAL_RANK) { $DonorHeart = 6; } elseif ($DonorRank === 5) { $DonorHeart = 4; // Two points between rank 4 and 5 } elseif ($DonorRank >= MAX_RANK) { $DonorHeart = 5; } if ($DonorHeart === 1) { $IconImage = STATIC_SERVER . '/common/symbols/donor.png'; } else { $IconImage = STATIC_SERVER . "/common/symbols/donor_{$DonorHeart}.png"; } } $Str .= "\"$IconText\""; } } $Str .= ($IsWarned && $UserInfo['Warned']) ? 'Warned' : ''; $Str .= ($IsEnabled && $UserInfo['Enabled'] == 2) ? 'Banned' : ''; if ($Badges) { $ClassesDisplay = []; foreach (array_keys($UserInfo['ExtraClasses']) as $PermID) { if ($Classes[$PermID]['badge'] !== '') { $ClassesDisplay[] = ''.$Classes[$PermID]['badge'].''; } } if (!empty($ClassesDisplay)) { $Str .= ' '.implode(' ', $ClassesDisplay); } } if ($Class) { if ($Title) { $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; } else { $Str .= ' ('.Users::make_class_string($UserInfo['PermissionID']).')'; } } if ($Title) { // Image proxy CTs if (check_perms('site_proxy_images') && !empty($UserInfo['Title'])) { $UserInfo['Title'] = preg_replace_callback('~src=("?)(http.+?)(["\s>])~', function($Matches) { return 'src=' . $Matches[1] . ImageTools::process($Matches[2]) . $Matches[3]; }, $UserInfo['Title']); } if ($UserInfo['Title']) { $Str .= ' ('.$UserInfo['Title'].')'; } } return $Str; } /** * Given a class ID, return its name. * * @param int $ClassID * @return string name */ public static function make_class_string($ClassID) { $Classes = (new Gazelle\Manager\User)->classList(); return $Classes[$ClassID]['Name']; } /** * Returns an array with User Bookmark data: group IDs, collage data, torrent data * @param string|int $UserID * @return array Group IDs, Bookmark Data, Torrent List */ public static function get_bookmarks($UserID) { $UserID = (int)$UserID; if (($Data = G::$Cache->get_value("bookmarks_group_ids_$UserID"))) { list($GroupIDs, $BookmarkData) = $Data; } else { $QueryID = G::$DB->get_query_id(); G::$DB->query(" SELECT GroupID, Sort, `Time` FROM bookmarks_torrents WHERE UserID = $UserID ORDER BY Sort, `Time` ASC"); $GroupIDs = G::$DB->collect('GroupID'); $BookmarkData = G::$DB->to_array('GroupID', MYSQLI_ASSOC); G::$DB->set_query_id($QueryID); G::$Cache->cache_value("bookmarks_group_ids_$UserID", [$GroupIDs, $BookmarkData], 3600); } $TorrentList = Torrents::get_groups($GroupIDs); return [$GroupIDs, $BookmarkData, $TorrentList]; } /** * Generate HTML for a user's avatar or just return the avatar URL * @param unknown $Avatar * @param unknown $UserID * @param unknown $Username * @param unknown $Setting * @param number $Size * @param string $ReturnHTML * @return string */ public static function show_avatar($Avatar, $UserID, $Username, $Setting, $Size = 150, $ReturnHTML = True) { $Avatar = ImageTools::process($Avatar, false, 'avatar', $UserID); $AvatarMouseOverText = ''; $FirstAvatar = ''; $SecondAvatar = ''; $ShowAvatar = false; $donorMan = new \Gazelle\Manager\Donation; $EnabledRewards = $donorMan->enabledRewards($UserID); if ($EnabledRewards['HasAvatarMouseOverText']) { $Rewards = $donorMan->rewards($UserID); $AvatarMouseOverText = $Rewards['AvatarMouseOverText']; } if (!empty($AvatarMouseOverText)) { $AvatarMouseOverText = "title=\"$AvatarMouseOverText\" alt=\"$AvatarMouseOverText\""; } else { $AvatarMouseOverText = "alt=\"$Username's avatar\""; } if ($EnabledRewards['HasSecondAvatar'] && !empty($Rewards['SecondAvatar'])) { $SecondAvatar = ImageTools::process($Rewards['SecondAvatar'], false, 'avatar2', $UserID); } $Attrs = "width=\"$Size\" $AvatarMouseOverText"; // purpose of the switch is to set $FirstAvatar (URL) // case 1 is avatars disabled switch ($Setting) { case 0: if (!empty($Avatar)) { $FirstAvatar = $Avatar; } else { $FirstAvatar = STATIC_SERVER.'/common/avatars/default.png'; } break; case 2: $ShowAvatar = true; // Fallthrough case 3: if ($ShowAvatar && !empty($Avatar)) { $FirstAvatar = $Avatar; break; } $Robot = false; switch (G::$LoggedUser['Identicons']) { case 0: $Type = 'identicon'; break; case 1: $Type = 'monsterid'; break; case 2: $Type = 'wavatar'; break; case 3: $Type = 'retro'; break; case 4: $Type = '1'; $Robot = true; break; case 5: $Type = '2'; $Robot = true; break; case 6: $Type = '3'; $Robot = true; break; default: $Type = 'identicon'; } $Rating = 'pg'; if (!$Robot) { $FirstAvatar = 'https://secure.gravatar.com/avatar/'.md5(strtolower(trim($Username)))."?s=$Size&d=$Type&r=$Rating"; } else { $FirstAvatar = 'https://robohash.org/'.md5($Username)."?set=set$Type&size={$Size}x$Size"; } break; default: $FirstAvatar = STATIC_SERVER.'/common/avatars/default.png'; } // in this case, $Attrs is actually just a URL if (!$ReturnHTML) { return $FirstAvatar; } $ToReturn = '
'; foreach ([$FirstAvatar, $SecondAvatar] as $AvatarNum => $CurAvatar) { if ($CurAvatar) { $ToReturn .= "
"; } } $ToReturn .= '
'; return $ToReturn; } public static function has_avatars_enabled() { global $HeavyInfo; return isset($HeavyInfo['DisableAvatars']) && $HeavyInfo['DisableAvatars'] != 1; } }