diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 2e1a217152..7d7a69c8a8 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -99,6 +99,7 @@ use pocketmine\timings\TimingsHandler; use pocketmine\updater\AutoUpdater; use pocketmine\utils\Binary; use pocketmine\utils\Config; +use pocketmine\utils\Internet; use pocketmine\utils\MainLogger; use pocketmine\utils\Terminal; use pocketmine\utils\TextFormat; @@ -2203,7 +2204,7 @@ class Server{ if($report){ $url = ($this->getProperty("auto-report.use-https", true) ? "https" : "http") . "://" . $this->getProperty("auto-report.host", "crash.pmmp.io") . "/submit/api"; - $reply = Utils::postURL($url, [ + $reply = Internet::postURL($url, [ "report" => "yes", "name" => $this->getName() . " " . $this->getPocketMineVersion(), "email" => "crash@pocketmine.net", diff --git a/src/pocketmine/network/upnp/UPnP.php b/src/pocketmine/network/upnp/UPnP.php index 780a2d5cfe..ff894c4b47 100644 --- a/src/pocketmine/network/upnp/UPnP.php +++ b/src/pocketmine/network/upnp/UPnP.php @@ -26,12 +26,13 @@ declare(strict_types=1); */ namespace pocketmine\network\upnp; +use pocketmine\utils\Internet; use pocketmine\utils\Utils; abstract class UPnP{ public static function PortForward(int $port) : void{ - if(!Utils::$online){ + if(!Internet::$online){ throw new \RuntimeException("Server is offline"); } if(Utils::getOS() !== "win"){ @@ -55,7 +56,7 @@ abstract class UPnP{ } public static function RemovePortForward(int $port) : bool{ - if(!Utils::$online){ + if(!Internet::$online){ return false; } if(Utils::getOS() != "win" or !class_exists("COM")){ diff --git a/src/pocketmine/scheduler/BulkCurlTask.php b/src/pocketmine/scheduler/BulkCurlTask.php index bf48ab0a52..d7a0e4b39a 100644 --- a/src/pocketmine/scheduler/BulkCurlTask.php +++ b/src/pocketmine/scheduler/BulkCurlTask.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\scheduler; -use pocketmine\utils\Utils; +use pocketmine\utils\Internet; /** * Executes a consecutive list of cURL operations. @@ -55,7 +55,7 @@ class BulkCurlTask extends AsyncTask{ $results = []; foreach($operations as $op){ try{ - $results[] = Utils::simpleCurl($op["page"], $op["timeout"] ?? 10, $op["extraHeaders"] ?? [], $op["extraOpts"] ?? []); + $results[] = Internet::simpleCurl($op["page"], $op["timeout"] ?? 10, $op["extraHeaders"] ?? [], $op["extraOpts"] ?? []); }catch(\RuntimeException $e){ $results[] = $e; } diff --git a/src/pocketmine/scheduler/SendUsageTask.php b/src/pocketmine/scheduler/SendUsageTask.php index 900d7ac7a3..b618f33dda 100644 --- a/src/pocketmine/scheduler/SendUsageTask.php +++ b/src/pocketmine/scheduler/SendUsageTask.php @@ -25,6 +25,7 @@ namespace pocketmine\scheduler; use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\Server; +use pocketmine\utils\Internet; use pocketmine\utils\Utils; use pocketmine\utils\UUID; use pocketmine\utils\VersionString; @@ -147,7 +148,7 @@ class SendUsageTask extends AsyncTask{ public function onRun(){ try{ - Utils::postURL($this->endpoint, $this->data, 5, [ + Internet::postURL($this->endpoint, $this->data, 5, [ "Content-Type: application/json", "Content-Length: " . strlen($this->data) ]); diff --git a/src/pocketmine/updater/UpdateCheckTask.php b/src/pocketmine/updater/UpdateCheckTask.php index a4fc4100ec..e71b8f2c83 100644 --- a/src/pocketmine/updater/UpdateCheckTask.php +++ b/src/pocketmine/updater/UpdateCheckTask.php @@ -27,7 +27,7 @@ namespace pocketmine\updater; use pocketmine\scheduler\AsyncTask; use pocketmine\Server; -use pocketmine\utils\Utils; +use pocketmine\utils\Internet; class UpdateCheckTask extends AsyncTask{ @@ -45,7 +45,7 @@ class UpdateCheckTask extends AsyncTask{ public function onRun(){ $error = ""; - $response = Utils::getURL($this->endpoint . "?channel=" . $this->channel, 4, [], $error); + $response = Internet::getURL($this->endpoint . "?channel=" . $this->channel, 4, [], $error); $this->error = $error; if($response !== false){ diff --git a/src/pocketmine/utils/Internet.php b/src/pocketmine/utils/Internet.php new file mode 100644 index 0000000000..292f269ca8 --- /dev/null +++ b/src/pocketmine/utils/Internet.php @@ -0,0 +1,185 @@ + 0){ + return self::$ip = $matches[1]; + } + + $ip = self::getURL("http://www.checkip.org/"); + if($ip !== false and preg_match('#">([0-9a-fA-F\:\.]*)#', $ip, $matches) > 0){ + return self::$ip = $matches[1]; + } + + $ip = self::getURL("http://checkmyip.org/"); + if($ip !== false and preg_match('#Your IP address is ([0-9a-fA-F\:\.]*)#', $ip, $matches) > 0){ + return self::$ip = $matches[1]; + } + + $ip = self::getURL("http://ifconfig.me/ip"); + if($ip !== false and trim($ip) != ""){ + return self::$ip = trim($ip); + } + + return false; + } + + /** + * GETs an URL using cURL + * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. + * + * @param string $page + * @param int $timeout default 10 + * @param array $extraHeaders + * @param string &$err Will be set to the output of curl_error(). Use this to retrieve errors that occured during the operation. + * @param array[] &$headers + * @param int &$httpCode + * + * @return bool|mixed false if an error occurred, mixed data if successful. + */ + public static function getURL(string $page, int $timeout = 10, array $extraHeaders = [], &$err = null, &$headers = null, &$httpCode = null){ + try{ + list($ret, $headers, $httpCode) = self::simpleCurl($page, $timeout, $extraHeaders); + return $ret; + }catch(\RuntimeException $ex){ + $err = $ex->getMessage(); + return false; + } + } + + /** + * POSTs data to an URL + * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. + * + * @param string $page + * @param array|string $args + * @param int $timeout + * @param array $extraHeaders + * @param string &$err Will be set to the output of curl_error(). Use this to retrieve errors that occured during the operation. + * @param array[] &$headers + * @param int &$httpCode + * + * @return bool|mixed false if an error occurred, mixed data if successful. + */ + public static function postURL(string $page, $args, int $timeout = 10, array $extraHeaders = [], &$err = null, &$headers = null, &$httpCode = null){ + try{ + list($ret, $headers, $httpCode) = self::simpleCurl($page, $timeout, $extraHeaders, [ + CURLOPT_POST => 1, + CURLOPT_POSTFIELDS => $args + ]); + return $ret; + }catch(\RuntimeException $ex){ + $err = $ex->getMessage(); + return false; + } + } + + /** + * General cURL shorthand function. + * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. + * + * @param string $page + * @param float|int $timeout The maximum connect timeout and timeout in seconds, correct to ms. + * @param string[] $extraHeaders extra headers to send as a plain string array + * @param array $extraOpts extra CURLOPT_* to set as an [opt => value] map + * @param callable|null $onSuccess function to be called if there is no error. Accepts a resource argument as the cURL handle. + * + * @return array a plain array of three [result body : string, headers : array[], HTTP response code : int]. Headers are grouped by requests with strtolower(header name) as keys and header value as values + * + * @throws \RuntimeException if a cURL error occurs + */ + public static function simpleCurl(string $page, $timeout = 10, array $extraHeaders = [], array $extraOpts = [], callable $onSuccess = null){ + if(!self::$online){ + throw new \RuntimeException("Cannot execute web request while offline"); + } + + $ch = curl_init($page); + + curl_setopt_array($ch, $extraOpts + [ + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => 2, + CURLOPT_FORBID_REUSE => 1, + CURLOPT_FRESH_CONNECT => 1, + CURLOPT_AUTOREFERER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CONNECTTIMEOUT_MS => (int) ($timeout * 1000), + CURLOPT_TIMEOUT_MS => (int) ($timeout * 1000), + CURLOPT_HTTPHEADER => array_merge(["User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 " . \pocketmine\NAME], $extraHeaders), + CURLOPT_HEADER => true + ]); + try{ + $raw = curl_exec($ch); + $error = curl_error($ch); + if($error !== ""){ + throw new \RuntimeException($error); + } + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $rawHeaders = substr($raw, 0, $headerSize); + $body = substr($raw, $headerSize); + $headers = []; + foreach(explode("\r\n\r\n", $rawHeaders) as $rawHeaderGroup){ + $headerGroup = []; + foreach(explode("\r\n", $rawHeaderGroup) as $line){ + $nameValue = explode(":", $line, 2); + if(isset($nameValue[1])){ + $headerGroup[trim(strtolower($nameValue[0]))] = trim($nameValue[1]); + } + } + $headers[] = $headerGroup; + } + if($onSuccess !== null){ + $onSuccess($ch); + } + return [$body, $headers, $httpCode]; + }finally{ + curl_close($ch); + } + } +} diff --git a/src/pocketmine/utils/Timezone.php b/src/pocketmine/utils/Timezone.php index 292cf260a0..1138f66a7a 100644 --- a/src/pocketmine/utils/Timezone.php +++ b/src/pocketmine/utils/Timezone.php @@ -61,7 +61,7 @@ abstract class Timezone{ break; } - if($response = Utils::getURL("http://ip-api.com/json") //If system timezone detection fails or timezone is an invalid value. + if($response = Internet::getURL("http://ip-api.com/json") //If system timezone detection fails or timezone is an invalid value. and $ip_geolocation_data = json_decode($response, true) and $ip_geolocation_data['status'] !== 'fail' and date_default_timezone_set($ip_geolocation_data['timezone']) diff --git a/src/pocketmine/utils/Utils.php b/src/pocketmine/utils/Utils.php index d60be3b68b..fa80a13ac3 100644 --- a/src/pocketmine/utils/Utils.php +++ b/src/pocketmine/utils/Utils.php @@ -33,8 +33,6 @@ use pocketmine\ThreadManager; * Big collection of functions */ class Utils{ - public static $online = true; - public static $ip = false; public static $os; /** @var UUID|null */ private static $serverUniqueId = null; @@ -123,45 +121,15 @@ class Utils{ } /** - * Gets the External IP using an external service, it is cached + * @deprecated + * @see Internet::getIP() * * @param bool $force default false, force IP check even when cached * * @return string|bool */ public static function getIP(bool $force = false){ - if(!Utils::$online){ - return false; - }elseif(Utils::$ip !== false and !$force){ - return Utils::$ip; - } - - $ip = Utils::getURL("http://api.ipify.org/"); - if($ip !== false){ - return Utils::$ip = $ip; - } - - $ip = Utils::getURL("http://checkip.dyndns.org/"); - if($ip !== false and preg_match('#Current IP Address\: ([0-9a-fA-F\:\.]*)#', trim(strip_tags($ip)), $matches) > 0){ - return Utils::$ip = $matches[1]; - } - - $ip = Utils::getURL("http://www.checkip.org/"); - if($ip !== false and preg_match('#">([0-9a-fA-F\:\.]*)#', $ip, $matches) > 0){ - return Utils::$ip = $matches[1]; - } - - $ip = Utils::getURL("http://checkmyip.org/"); - if($ip !== false and preg_match('#Your IP address is ([0-9a-fA-F\:\.]*)#', $ip, $matches) > 0){ - return Utils::$ip = $matches[1]; - } - - $ip = Utils::getURL("http://ifconfig.me/ip"); - if($ip !== false and trim($ip) != ""){ - return Utils::$ip = trim($ip); - } - - return false; + return Internet::getIP($force); } /** @@ -363,8 +331,8 @@ class Utils{ }*/ /** - * GETs an URL using cURL - * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. + * @deprecated + * @see Internet::getURL() * * @param string $page * @param int $timeout default 10 @@ -376,18 +344,12 @@ class Utils{ * @return bool|mixed false if an error occurred, mixed data if successful. */ public static function getURL(string $page, int $timeout = 10, array $extraHeaders = [], &$err = null, &$headers = null, &$httpCode = null){ - try{ - list($ret, $headers, $httpCode) = self::simpleCurl($page, $timeout, $extraHeaders); - return $ret; - }catch(\RuntimeException $ex){ - $err = $ex->getMessage(); - return false; - } + return Internet::getURL($page, $timeout, $extraHeaders, $err, $headers, $httpCode); } /** - * POSTs data to an URL - * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. + * @deprecated + * @see Internet::postURL() * * @param string $page * @param array|string $args @@ -400,22 +362,12 @@ class Utils{ * @return bool|mixed false if an error occurred, mixed data if successful. */ public static function postURL(string $page, $args, int $timeout = 10, array $extraHeaders = [], &$err = null, &$headers = null, &$httpCode = null){ - try{ - list($ret, $headers, $httpCode) = self::simpleCurl($page, $timeout, $extraHeaders, [ - CURLOPT_POST => 1, - CURLOPT_POSTFIELDS => $args - ]); - return $ret; - }catch(\RuntimeException $ex){ - $err = $ex->getMessage(); - return false; - } - + return Internet::postURL($page, $args, $timeout, $extraHeaders, $err, $headers, $httpCode); } /** - * General cURL shorthand function. - * NOTE: This is a blocking operation and can take a significant amount of time. It is inadvisable to use this method on the main thread. + * @deprecated + * @see Internet::simpleCurl() * * @param string $page * @param float|int $timeout The maximum connect timeout and timeout in seconds, correct to ms. @@ -428,53 +380,7 @@ class Utils{ * @throws \RuntimeException if a cURL error occurs */ public static function simpleCurl(string $page, $timeout = 10, array $extraHeaders = [], array $extraOpts = [], callable $onSuccess = null){ - if(!Utils::$online){ - throw new \RuntimeException("Server is offline"); - } - - $ch = curl_init($page); - - curl_setopt_array($ch, $extraOpts + [ - CURLOPT_SSL_VERIFYPEER => false, - CURLOPT_SSL_VERIFYHOST => 2, - CURLOPT_FORBID_REUSE => 1, - CURLOPT_FRESH_CONNECT => 1, - CURLOPT_AUTOREFERER => true, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CONNECTTIMEOUT_MS => (int) ($timeout * 1000), - CURLOPT_TIMEOUT_MS => (int) ($timeout * 1000), - CURLOPT_HTTPHEADER => array_merge(["User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 " . \pocketmine\NAME], $extraHeaders), - CURLOPT_HEADER => true - ]); - try{ - $raw = curl_exec($ch); - $error = curl_error($ch); - if($error !== ""){ - throw new \RuntimeException($error); - } - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); - $rawHeaders = substr($raw, 0, $headerSize); - $body = substr($raw, $headerSize); - $headers = []; - foreach(explode("\r\n\r\n", $rawHeaders) as $rawHeaderGroup){ - $headerGroup = []; - foreach(explode("\r\n", $rawHeaderGroup) as $line){ - $nameValue = explode(":", $line, 2); - if(isset($nameValue[1])){ - $headerGroup[trim(strtolower($nameValue[0]))] = trim($nameValue[1]); - } - } - $headers[] = $headerGroup; - } - if($onSuccess !== null){ - $onSuccess($ch); - } - return [$body, $headers, $httpCode]; - }finally{ - curl_close($ch); - } + return Internet::simpleCurl($page, $timeout, $extraHeaders, $extraOpts, $onSuccess); } public static function javaStringHash(string $string) : int{ diff --git a/src/pocketmine/wizard/SetupWizard.php b/src/pocketmine/wizard/SetupWizard.php index fb4ff72b25..d4de8fce09 100644 --- a/src/pocketmine/wizard/SetupWizard.php +++ b/src/pocketmine/wizard/SetupWizard.php @@ -29,7 +29,7 @@ namespace pocketmine\wizard; use pocketmine\lang\BaseLang; use pocketmine\utils\Config; -use pocketmine\utils\Utils; +use pocketmine\utils\Internet; class SetupWizard{ public const DEFAULT_NAME = \pocketmine\NAME . " Server"; @@ -205,7 +205,7 @@ LICENSE; $this->message($this->lang->get("ip_get")); - $externalIP = Utils::getIP(); + $externalIP = Internet::getIP(); if($externalIP === false){ $externalIP = "unknown (server offline)"; }