diff --git a/composer.json b/composer.json index ab0413723..f96a1fe3f 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,8 @@ "src/Volatile.php", "src/Thread.php", "src/Worker.php", - "src/Pool.php" + "src/Pool.php", + "src/HttpProxy.php" ] } } diff --git a/src/HttpProxy.php b/src/HttpProxy.php new file mode 100644 index 000000000..bbdb4c129 --- /dev/null +++ b/src/HttpProxy.php @@ -0,0 +1,160 @@ +. +*/ + + +class HttpProxy implements \danog\MadelineProto\Proxy +{ + private $domain; + private $type; + private $protocol; + private $extra; + private $sock; + public function __construct($domain, $type, $protocol) { + if (!in_array($domain, [AF_INET, AF_INET6])) { + throw new \danog\MadelineProto\Exception('Wrong protocol family provided'); + } + if (!in_array($type, [SOCK_STREAM])) { + throw new \danog\MadelineProto\Exception('Wrong connection type provided'); + } + if (!in_array($protocol, [getprotobyname('tcp'), PHP_INT_MAX])) { + throw new \danog\MadelineProto\Exception('Wrong protocol provided'); + } + $this->domain = $domain; + $this->type = $type; + $this->protocol = $protocol; + } + public function setExtra(array $extra = []) { + $this->extra = $extra; + $this->sock = new \Socket(strlen(@inet_pton($this->extra['address'])) !== 4 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, $this->protocol); + } + public function setOption($level, $name, $value) { + return $this->sock->setOption($level, $name, $value); + } + + public function getOption($level, $name) { + return $this->sock->getOption($level, $name); + } + + public function setBlocking($blocking) { + return $this->sock->setBlocking($blocking); + } + + public function bind($address, $port = 0) { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + + public function listen($backlog = 0) { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + public function accept() { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + + + public function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0) { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + public function connect($address, $port = 0) { + $this->sock->connect($this->extra['address'], $this->extra['port']); + + try { + if (strlen(inet_pton($address)) !== 4) { + $address = '['.$address.']'; + } + } catch (\danog\MadelineProto\Exception $e) { + } + $this->sock->write("CONNECT $address:$port HTTP/1.1\r\nHost: $address:$port\r\n\r\n"); + $response = $this->read_http_payload(); + if ($response['code'] !== 200) { + \danog\MadelineProto\Logger::log(trim($response['body'])); + throw new \danog\MadelineProto\Exception($response['description'], $response['code']); + } + \danog\MadelineProto\Logger::log('Connected to '.$address.':'.$port.' via http'); + return true; + } + private function http_read($length) { + $packet = ''; + while (strlen($packet) < $length) { + $packet .= $this->sock->read($length - strlen($packet)); + if ($packet === false || strlen($packet) === 0) { + throw new \danog\MadelineProto\NothingInTheSocketException(\danog\MadelineProto\Lang::$current_lang['nothing_in_socket']); + } + } + return $packet; + } + public function read_http_line() + { + $line = ''; + while (($curchar = $this->http_read(1)) !== "\n") { + $line .= $curchar; + } + + return rtrim($line); + } + + public function read_http_payload() + { + $header = explode(' ', $this->read_http_line(), 3); + $protocol = $header[0]; + $code = (int) $header[1]; + $description = $header[2]; + $headers = []; + while (strlen($current_header = $this->read_http_line())) { + $current_header = explode(':', $current_header, 2); + $headers[strtolower($current_header[0])] = trim($current_header[1]); + } + + $read = ''; + if (isset($headers['content-length'])) { + $read = $this->http_read((int) $headers['content-length']); + }/* elseif (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] === 'chunked') { + do { + $length = hexdec($this->read_http_line()); + $read .= $this->http_read($length); + $this->read_http_line(); + } while ($length); + }*/ + + return ['protocol' => $protocol, 'code' => $code, 'description' => $description, 'body' => $read, 'headers' => $headers]; + } + + public function read($length, $flags = 0) { + $read = $this->sock->read($length, $flags); + if ($read === 0) { + throw new \danog\MadelineProto\Exception('pls reconnect'); + } + return $read; + } + + public function write($buffer, $length = -1) { + return $this->sock->write($buffer, $length); + } + + public function send($data, $length, $flags) { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + + public function close() { + $this->sock->close(); + } + + public function getPeerName($port = true) { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + + public function getSockName($port = true) { + throw new \danog\MadelineProto\Exception('Not Implemented'); + } + public function getProxyHeaders() { + + } +} \ No newline at end of file diff --git a/src/danog/MadelineProto/Connection.php b/src/danog/MadelineProto/Connection.php index 2954c9e8d..1d71989f7 100644 --- a/src/danog/MadelineProto/Connection.php +++ b/src/danog/MadelineProto/Connection.php @@ -151,7 +151,7 @@ class Connection if (strpos($this->protocol, 'https') === 0 && $proxy === '\\Socket') { $proxy = '\\FSocket'; } - $this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, strpos($this->protocol, 'https') === 0 ? PHP_INT_MAX : ($has_proxy ? PHP_INT_MAX - 1 : getprotobyname('tcp'))); + $this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, strpos($this->protocol, 'https') === 0 ? PHP_INT_MAX : getprotobyname('tcp')); if ($has_proxy) { if ($this->extra !== []) { $this->sock->setExtra($this->extra); diff --git a/src/danog/MadelineProto/DataCenter.php b/src/danog/MadelineProto/DataCenter.php index 0c56b589c..90bbfe8cf 100644 --- a/src/danog/MadelineProto/DataCenter.php +++ b/src/danog/MadelineProto/DataCenter.php @@ -116,6 +116,16 @@ class DataCenter $this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6']; \danog\MadelineProto\Logger::log('Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING); continue; + case 3: + $this->settings[$dc_config_number]['proxy'] = '\\HttpProxy'; + $this->settings[$dc_config_number]['proxy_extra'] = ['address' => 'localhost', 'port' => 80]; + $this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6']; + \danog\MadelineProto\Logger::log('Connection failed, retrying connection with localhost HTTP proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING); + continue; + case 4: + $this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6']; + \danog\MadelineProto\Logger::log('Connection failed, retrying connection with localhost HTTP proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING); + continue; default: throw new \danog\MadelineProto\Exception("Could not connect to DC $dc_number"); } diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 3b344a073..84f31b042 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -54,7 +54,7 @@ class MTProto /* const V = 71; */ - const V = 97; + const V = 98; const NOT_LOGGED_IN = 0; const WAITING_CODE = 1; const WAITING_SIGNUP = -1; @@ -369,6 +369,7 @@ class MTProto if (isset(Lang::$lang[$lang_code])) { Lang::$current_lang = &Lang::$lang[$lang_code]; } + $altervista = isset($_SERVER['SERVER_ADMIN']) && strpos($_SERVER['SERVER_ADMIN'], 'altervista.org'); // Set default settings $default_settings = ['authorization' => [ // Authorization settings @@ -443,9 +444,9 @@ class MTProto // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean 'timeout' => 2, // timeout for sockets - 'proxy' => '\\Socket', + 'proxy' => $altervista ? '\\HttpProxy' : '\\Socket', // The proxy class to use - 'proxy_extra' => [], + 'proxy_extra' => $altervista ? ['address' => 'localhost', 'port' => 80] : [], // Extra parameters to pass to the proxy class using setExtra 'pfs' => extension_loaded('gmp'), ],