| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- <?php
- namespace App\Utils;
- /**
- * Socket communication class.
- *
- * Originally designed for use with DirectAdmin's API, this class will fill any HTTP socket need.
- *
- * Very, very basic usage:
- * $Socket = new HTTPSocket;
- * echo $Socket->get('http://user:[email protected]/somedir/some.file?query=string&this=that');
- *
- * @author Phi1 'l0rdphi1' Stier <[email protected]>
- * @package HTTPSocket
- * @version 2.7.2
- * 2.7.2
- * added x-use-https header check
- * added max number of location redirects
- * added custom settable message if x-use-https is found, so users can be told where to set their scripts
- * if a redirect host is https, add ssl:// to remote_host
- * 2.7.1
- * added isset to headers['location'], line 306
- */
- class HTTPSocket
- {
- public $version = '2.7.2';
-
- /* all vars are private except $error, $query_cache, and $doFollowLocationHeader */
- public $method = 'GET';
- public $remote_host;
- public $remote_port;
- public $remote_uname;
- public $remote_passwd;
- public $result;
- public $result_header;
- public $result_body;
- public $result_status_code;
- public $lastTransferSpeed;
- public $bind_host;
- public $error = array();
- public $warn = array();
- public $query_cache = array();
- public $doFollowLocationHeader = true;
- public $redirectURL;
- public $max_redirects = 5;
- public $ssl_setting_message = 'DirectAdmin appears to be using SSL. Change your script to connect to ssl://';
- public $extra_headers = array();
- /**
- * Create server "connection".
- *
- */
- public function connect($host, $port = '')
- {
- if (!is_numeric($port)) {
- $port = 80;
- }
- $this->remote_host = $host;
- $this->remote_port = $port;
- }
- public function bind($ip = '')
- {
- if ($ip == '') {
- $ip = $_SERVER['SERVER_ADDR'];
- }
- $this->bind_host = $ip;
- }
- /**
- * Change the method being used to communicate.
- *
- * @param string|null request method. supports GET, POST, and HEAD. default is GET
- */
- public function set_method($method = 'GET')
- {
- $this->method = strtoupper($method);
- }
- /**
- * Specify a username and password.
- *
- * @param string|null username. defualt is null
- * @param string|null password. defualt is null
- */
- public function set_login($uname = '', $passwd = '')
- {
- if (strlen($uname) > 0) {
- $this->remote_uname = $uname;
- }
- if (strlen($passwd) > 0) {
- $this->remote_passwd = $passwd;
- }
- }
- /**
- * Query the server
- *
- * @param string containing properly formatted server API. See DA API docs and examples. Http:// URLs O.K. too.
- * @param string|array query to pass to url
- * @param int if connection KB/s drops below value here, will drop connection
- */
- public function query($request, $content = '', $doSpeedCheck = 0)
- {
- $this->error = $this->warn = array();
- $this->result_status_code = null;
- // is our request a http:// ... ?
- if (preg_match('!^http://!i', $request) || preg_match('!^https://!i', $request)) {
- $location = parse_url($request);
- if (preg_match('!^https://!i', $request)) {
- $this->connect('ssl://'.$location['host'], $location['port']);
- } else {
- $this->connect($location['host'], $location['port']);
- }
- $this->set_login($location['user'], $location['pass']);
- $request = $location['path'];
- $content = $location['query'];
- if (strlen($request) < 1) {
- $request = '/';
- }
- }
- $array_headers = array(
- 'User-Agent' => "HTTPSocket/$this->version",
- 'Host' => ($this->remote_port == 80 ? $this->remote_host : "$this->remote_host:$this->remote_port"),
- 'Accept' => '*/*',
- 'Connection' => 'Close' );
- foreach ($this->extra_headers as $key => $value) {
- $array_headers[$key] = $value;
- }
- $this->result = $this->result_header = $this->result_body = '';
- // was content sent as an array? if so, turn it into a string
- if (is_array($content)) {
- $pairs = array();
- foreach ($content as $key => $value) {
- $pairs[] = "$key=".urlencode($value);
- }
- $content = join('&', $pairs);
- unset($pairs);
- }
- $OK = true;
- // instance connection
- if ($this->bind_host) {
- $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
- socket_bind($socket, $this->bind_host);
- if (!@socket_connect($socket, $this->remote_host, $this->remote_port)) {
- $OK = false;
- }
- } else {
- $socket = @fsockopen($this->remote_host, $this->remote_port, $sock_errno, $sock_errstr, 10);
- }
- if (!$socket || !$OK) {
- $this->error[] = "Can't create socket connection to $this->remote_host:$this->remote_port.";
- return 0;
- }
- // if we have a username and password, add the header
- if (isset($this->remote_uname) && isset($this->remote_passwd)) {
- $array_headers['Authorization'] = 'Basic '.base64_encode("$this->remote_uname:$this->remote_passwd");
- }
- // for DA skins: if $this->remote_passwd is NULL, try to use the login key system
- if (isset($this->remote_uname) && $this->remote_passwd == null) {
- $array_headers['Cookie'] = "session={$_SERVER['SESSION_ID']}; key={$_SERVER['SESSION_KEY']}";
- }
- // if method is POST, add content length & type headers
- if ($this->method == 'POST') {
- $array_headers['Content-type'] = 'application/x-www-form-urlencoded';
- $array_headers['Content-length'] = strlen($content);
- }
- // else method is GET or HEAD. we don't support anything else right now.
- else {
- if ($content) {
- $request .= "?$content";
- }
- }
- // prepare query
- $query = "$this->method $request HTTP/1.0\r\n";
- foreach ($array_headers as $key => $value) {
- $query .= "$key: $value\r\n";
- }
- $query .= "\r\n";
- // if POST we need to append our content
- if ($this->method == 'POST' && $content) {
- $query .= "$content\r\n\r\n";
- }
- // query connection
- if ($this->bind_host) {
- socket_write($socket, $query);
- // now load results
- while ($out = socket_read($socket, 2048)) {
- $this->result .= $out;
- }
- } else {
- fwrite($socket, $query, strlen($query));
- // now load results
- $this->lastTransferSpeed = 0;
- $status = socket_get_status($socket);
- $startTime = time();
- $length = 0;
- $prevSecond = 0;
- while (!feof($socket) && !$status['timed_out']) {
- $chunk = fgets($socket, 1024);
- $length += strlen($chunk);
- $this->result .= $chunk;
- $elapsedTime = time() - $startTime;
- if ($elapsedTime > 0) {
- $this->lastTransferSpeed = ($length/1024)/$elapsedTime;
- }
- if ($doSpeedCheck > 0 && $elapsedTime > 5 && $this->lastTransferSpeed < $doSpeedCheck) {
- $this->warn[] = "kB/s for last 5 seconds is below 50 kB/s (~".(($length/1024)/$elapsedTime)."), dropping connection...";
- $this->result_status_code = 503;
- break;
- }
- }
- if ($this->lastTransferSpeed == 0) {
- $this->lastTransferSpeed = $length/1024;
- }
- }
-
- list($this->result_header, $this->result_body) = preg_split("/\r\n\r\n/", $this->result, 2);
- if ($this->bind_host) {
- socket_close($socket);
- } else {
- fclose($socket);
- }
- $this->query_cache[] = $query;
- $headers = $this->fetch_header();
- // what return status did we get?
- if (!$this->result_status_code) {
- preg_match("#HTTP/1\.. (\d+)#", $headers[0], $matches);
- $this->result_status_code = $matches[1];
- }
- // did we get the full file?
- if (!empty($headers['content-length']) && $headers['content-length'] != strlen($this->result_body)) {
- $this->result_status_code = 206;
- }
- // now, if we're being passed a location header, should we follow it?
- if ($this->doFollowLocationHeader) {
- //dont bother if we didn't even setup the script correctly
- if (isset($headers['x-use-https']) && $headers['x-use-https']=='yes') {
- die($this->ssl_setting_message);
- }
- if (isset($headers['location'])) {
- if ($this->max_redirects <= 0) {
- die("Too many redirects on: ".$headers['location']);
- }
- $this->max_redirects--;
- $this->redirectURL = $headers['location'];
- $this->query($headers['location']);
- }
- }
- }
- public function getTransferSpeed()
- {
- return $this->lastTransferSpeed;
- }
- /**
- * The quick way to get a URL's content :)
- *
- * @param string URL
- * @param boolean return as array? (like PHP's file() command)
- * @return string result body
- */
- public function get($location, $asArray = false)
- {
- $this->query($location);
- if ($this->get_status_code() == 200) {
- if ($asArray) {
- return preg_split("/\n/", $this->fetch_body());
- }
- return $this->fetch_body();
- }
- return false;
- }
- /**
- * Returns the last status code.
- * 200 = OK;
- * 403 = FORBIDDEN;
- * etc.
- *
- * @return int status code
- */
- public function get_status_code()
- {
- return $this->result_status_code;
- }
- /**
- * Adds a header, sent with the next query.
- *
- * @param string header name
- * @param string header value
- */
- public function add_header($key, $value)
- {
- $this->extra_headers[$key] = $value;
- }
- /**
- * Clears any extra headers.
- *
- */
- public function clear_headers()
- {
- $this->extra_headers = array();
- }
- /**
- * Return the result of a query.
- *
- * @return string result
- */
- public function fetch_result()
- {
- return $this->result;
- }
- /**
- * Return the header of result (stuff before body).
- *
- * @param string (optional) header to return
- * @return array result header
- */
- public function fetch_header($header = '')
- {
- $array_headers = preg_split("/\r\n/", $this->result_header);
-
- $array_return = array( 0 => $array_headers[0] );
- unset($array_headers[0]);
- foreach ($array_headers as $pair) {
- list($key, $value) = preg_split("/: /", $pair, 2);
- $array_return[strtolower($key)] = $value;
- }
- if ($header != '') {
- return $array_return[strtolower($header)];
- }
- return $array_return;
- }
- /**
- * Return the body of result (stuff after header).
- *
- * @return string result body
- */
- public function fetch_body()
- {
- return $this->result_body;
- }
- /**
- * Return parsed body in array format.
- *
- * @return array result parsed
- */
- public function fetch_parsed_body()
- {
- parse_str($this->result_body, $x);
- return $x;
- }
- /**
- * Set a specifc message on how to change the SSL setting, in the event that it's not set correctly.
- */
- public function set_ssl_setting_message($str)
- {
- $this->ssl_setting_message = $str;
- }
- }
|