[ "/directory_login/2fa.php", "/logout.php", "/rbxclient/asset/fetch.php", "/rbxclient/asset/bodycolors.php", "/rbxclient/asset/characterfetch.php", "/rbxclient/friend/arefriends.php", "/rbxclient/game/studio.php", "/rbxclient/game/join.php", "/rbxclient/game/visit.php", "/rbxclient/game/gameserver.php", "/rbxclient/game/machineconfiguration.php", "/rbxclient/game/luawebservice/handlesocialrequest.php", "/rbxclient/game/tools/insertasset.php", "/game/clientpresence.php", "/game/join.php", "/game/server.php", "/game/serverpresence.php", "/game/verifyplayer.php", "/asset/index.php", "/farewell.php" ], "Moderation" => [ "/moderation.php", "/info/terms-of-service.php", "/info/privacy.php", "/info/selfhosting.php", "/directory_login/2fa.php", "/logout.php", "/rbxclient/game/machineconfiguration.php", "/my/account.php", "/api/account/update-settings.php", "/api/account/update-password.php", "/api/account/destroy-sessions.php", "/farewell.php" ], "HTTPS" => [ "/error.php" ], "Negotiate" => [ "/rbxclient/game/join.php", "/rbxclient/game/visit.php", "/rbxclient/game/edit.php" ] ]; if($_SERVER["HTTP_HOST"] == "chef.pizzaboxer.xyz") { header('HTTP/1.1 301 Moved Permanently'); header('Location: http://polygon.pizzaboxer.xyz'.$_SERVER['REQUEST_URI']); exit; } if(!Polygon::CanBypass("HTTPS") && isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == "http") { header('HTTP/1.1 301 Moved Permanently'); header('Location: https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); exit; } // functions that arent strictly specifically for polygon and moreof to just // extend basic php functionality like string manipulation or some small // utilities are typically just put here classless // we're still using php7 soooo if we move to php8 dont forget to nuke these // however i also added array support for str_ends_with as it's pretty handy // in retrospect i shoulda named str_ends_with something else but that was // before i added array support function str_starts_with($haystack, $needle) { return substr($haystack, 0, strlen($needle)) === $needle; } function str_ends_with($haystack, $needle) { if(gettype($needle) == "array") { foreach ($needle as $ending) if(substr($haystack, -strlen($ending)) === $ending) return true; return false; } return substr($haystack, -strlen($needle)) === $needle; } function vowel($string) { if(in_array(strtolower(substr($string, 0, 1)), ["a", "e", "i", "o", "u"])) return "an $string"; return "a $string"; } function plural($string) { if(str_ends_with($string, "s")) return $string; return $string."s"; } function encode_asset_name($string) { $string = preg_replace("![^a-z0-9]+!i", "-", $string); if (str_ends_with($string, "-")) $string = substr($string, 0, -1); return $string; } function generateUUID() { return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); } function rgbtohex($rgb) { $rgb_parsed = sscanf($rgb, "rgb(%i, %i, %i)"); return sprintf('%02X%02X%02X', $rgb_parsed[0], $rgb_parsed[1], $rgb_parsed[2]); } function redirect($url) { die(header("Location: $url")); } function Pagination($Page, $Count, $Limit) { $Page = intval($Page); $Pages = ceil($Count/$Limit); if ($Page < 1) $Page = 1; else if ($Page > $Pages) $Page = $Pages; $Offset = ($Page - 1)*$Limit; if ($Offset < 0) $Offset = 0; return (object)["Page" => (int)$Page, "Pages" => (int)$Pages, "Offset" => (int)$Offset]; } function GetIPAddress() { return $_SERVER["REMOTE_ADDR"]; } function GetUserAgent() { return $_SERVER["HTTP_USER_AGENT"] ?? "Unknown"; } function VerifyReCAPTCHA() { $fields = http_build_query([ 'secret' => SITE_CONFIG["keys"]["captcha"]["secret"], 'response' => $_POST['g-recaptcha-response'] ?? "", 'remoteip' => GetIPAddress() ]); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://www.google.com/recaptcha/api/siteverify"); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $result = json_decode($response); if ($result === false || !$result->success) return false; return true; } function GetIPInfo($IPAddress) { if (!filter_var($IPAddress, FILTER_VALIDATE_IP)) throw new Exception("Invalid IP Address {$IPAddress}"); $response = @file_get_contents("https://proxycheck.io/v2/{$IPAddress}?key=03l192-455797-m9n0w8-0wz258"); if($response === false) throw new Exception(error_get_last()["message"]); $result = json_decode($response); if($result->status != "ok") throw new Exception("CheckIPAddress failed: response didnt return ok: \"".var_export($result, true)."\""); if(!isset($result->{$IPAddress})) throw new Exception("CheckIPAddress failed: bad response \"".var_export($result, true)."\""); return $result->{$IPAddress}; } function GetASNumber($IPAddress) { if (!filter_var($IPAddress, FILTER_VALIDATE_IP)) throw new Exception("Invalid IP Address {$IPAddress}"); $whois = shell_exec("whois -h whois.cymru.com \" -f {$IPAddress}\""); $asn = trim(explode('|', $whois)[0]); if ($asn == "NA") return false; // no asn record available if (!is_numeric($asn)) throw new Exception("GetASNumber failed: invalid ASN: \"{$whois}\""); return intval($asn); } // DEPRECATED: use GetReadableTime() instead function timeSince($datetime, $full = false, $ending = true, $truncate = false, $abbreviate = false) { if(strpos($datetime, '@') === false) $datetime = "@$datetime"; if($datetime == "@") return "-"; if($truncate && ltrim($datetime, "@") < strtotime("1 year ago", time())) return date("n/j/Y", ltrim($datetime, "@")); $now = new DateTime; $ago = new DateTime($datetime); $diff = $now->diff($ago); $diff->w = floor($diff->d / 7); $diff->d -= $diff->w * 7; $string = array( 'y' => 'year', 'm' => 'month', 'w' => 'week', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second', ); if($abbreviate) { $string = ['y' => 'y', 'm' => 'm', 'w' => 'w', 'd' => 'd', 'h' => 'h', 'i' => 'm', 's' => 's']; foreach ($string as $k => &$v) { if ($diff->$k) $v = $diff->$k.$v; else unset($string[$k]); } return implode(' ', $string); } foreach ($string as $k => &$v) { if ($diff->$k) $v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : ''); else unset($string[$k]); } if (!$full) $string = array_slice($string, 0, 1); if($ending){ return $string ? implode(', ', $string) . ' ago' : 'Just now'; } return implode(', ', $string); } // https://stackoverflow.com/questions/1416697/converting-timestamp-to-time-ago-in-php-e-g-1-day-ago-2-days-ago // btw when you use this be sure to put the regular date format as a title or tooltip attribute function GetReadableTime($Timestamp, $Options = []) { $Timestamp += 1; $RelativeTime = $Options["RelativeTime"] ?? false; $Full = $Options["Full"] ?? false; $Ending = $Options["Ending"] ?? true; $Abbreviate = $Options["Abbreviate"] ?? false; $Threshold = $Options["Threshold"] ?? false; if($RelativeTime !== false) { $Full = true; $Ending = false; $Timestamp = ($Timestamp+strtotime($RelativeTime, 0)); } if($Threshold !== false && $Timestamp < strtotime($Threshold, time())) { return date("j/n/Y g:i:s A", $Timestamp); } $TimeNow = new DateTime; $TimeAgo = new DateTime("@$Timestamp"); $TimeDifference = $TimeNow->diff($TimeAgo); $TimeDifference->w = floor($TimeDifference->d / 7); $TimeDifference->d -= $TimeDifference->w * 7; if($Abbreviate) { $Components = [ 'y' => 'y', 'm' => 'm', 'w' => 'w', 'd' => 'd', 'h' => 'h', 'i' => 'm', 's' => 's' ]; foreach ($Components as $Character => &$String) { if ($TimeDifference->$Character) $String = $TimeDifference->$Character . $String; else unset($Components[$Character]); } } else { $Components = [ 'y' => 'year', 'm' => 'month', 'w' => 'week', 'd' => 'day', 'h' => 'hour', 'i' => 'minute', 's' => 'second', ]; foreach ($Components as $Character => &$String) { if ($TimeDifference->$Character) $String = $TimeDifference->$Character . ' ' . $String . ($TimeDifference->$Character > 1 ? 's' : ''); else unset($Components[$Character]); } } if (!$Full) $Components = array_slice($Components, 0, 1); $FirstComponent = [join(', ', array_slice($Components, 0, -1))]; $LastComponent = array_slice($Components, -1); $ReadableTime = join(' and ', array_filter(array_merge($FirstComponent, $LastComponent), "strlen")); if ($Ending) return $Components ? "$ReadableTime ago" : "Just now"; return $ReadableTime; } require Polygon::GetSharedResource("config.php"); $errorHandler = new ErrorHandler(); $errorHandler->register(); if ($_SERVER['REQUEST_METHOD'] == 'POST') { foreach($_POST as $key => $val) { if (gettype($val) == "array") continue; $_POST[$key] = trim($val); } } foreach ($_GET as $key => $val) { if (gettype($val) == "array") continue; $_GET[$key] = trim($val); } $markdown = new Parsedown(); $markdown->setMarkupEscaped(true); $markdown->setBreaksEnabled(true); $markdown->setSafeMode(true); $markdown->setUrlsLinked(true); Polygon::GetAnnouncements(); // TEMPORARY HACK for negotiate.ashx on 2010 and 2011 // later on this should just be moved to /rbxclient/login/negotiate.php if (isset($_GET["suggest"]) && !isset($_COOKIE['polygon_session']) && Polygon::CanBypass("Negotiate")) { // the ticket is formatted as [{username}:{id}:{timestamp}]:{signature} // the part in square brackets is what the signature represents $ticket = explode(":", $_GET["suggest"]); if (count($ticket) == 4) { $username = $ticket[0]; $userid = $ticket[1]; $timestamp = (int)$ticket[2]; $signature = $ticket[3]; // reconstruct the signed message $ticketRecon = sprintf("%s:%s:%d", $username, $userid, $timestamp); // check if signature matches and if ticket is 3 minutes old max if (RBXClient::CryptVerifySignature($ticketRecon, $signature) && $timestamp + 180 > time()) { // before we create the session, let's just quickly check to make sure we don't create any duplicate sessions $lastSession = Database::singleton()->run( "SELECT created FROM sessions WHERE userId = :UserID AND IsGameClient ORDER BY created DESC LIMIT 1", [":UserID" => $userid] )->fetchColumn(); if ($lastSession + 180 < $timestamp) { $session = Session::Create($userid, true); // this might be a war crime $_COOKIE["polygon_session"] = $session; } } } } if (isset($_COOKIE['polygon_session'])) { $Session = Session::Get($_COOKIE['polygon_session']); if ($Session) { $userInfo = Users::GetInfoFromID($Session["userId"]); define("SESSION", [ "2faVerified" => $Session["twofaVerified"], "sessionKey" => $Session["sessionKey"], "csrfToken" => $Session["csrf"], "user" => (array)$userInfo ]); if (SESSION["user"]["twofa"] && !SESSION["2faVerified"] && !Polygon::CanBypass("2FA")) { die(header("Location: /login/2fa")); } else if (SESSION["user"]["Banned"] && !Polygon::CanBypass("Moderation")) { die(header("Location: /moderation")); } else { Users::UpdatePing(); } } else { // do not reload the page if we're doing a negotiate ticket Session::Clear($_COOKIE['polygon_session'], Polygon::CanBypass("Negotiate")); define('SESSION', false); } } else { define('SESSION', false); }