From 1ded657739fc970bd2fda590af4bd6ef2a05cb00 Mon Sep 17 00:00:00 2001 From: danogentili Date: Wed, 16 Nov 2016 17:05:27 +0300 Subject: [PATCH] Added APIFactory for calling namespaced mtproto methods, cleaned up testing.php and API.php, moved authorization keys, session_id, seq_no and time delta to the Connection class, added close_and_reopen method to the Connection class, improved DataCenter class, renamed Logging class to Logger, added a bit more logging, added setup_logger, switch_dc, init_authorization methods to the MTProto class, added parameter to disable automatic switching to nearest DC in write_client_info, added a try catch block in the create_auth_key method, fixed switching of DCs in wait_for_response method, added arguments check in the method calling methods, added a message id check in MessageHandler, added method_name_namespaced array in TL for APIFactory, renamed a lot of stuff and removed a few checks in the TL class, moved sendCode test call to testing.php --- .gitignore | 1 + README.md | 2 +- credentials | 5 - src/danog/MadelineProto/API.php | 21 +- src/danog/MadelineProto/APIFactory.php | 34 + src/danog/MadelineProto/Connection.php | 45 +- src/danog/MadelineProto/DataCenter.php | 60 +- .../MadelineProto/{Logging.php => Logger.php} | 10 +- src/danog/MadelineProto/MTProto.php | 88 +- .../MadelineProto/MTProtoTools/AckHandler.php | 2 +- .../MTProtoTools/AuthKeyHandler.php | 834 +++++++++--------- .../MTProtoTools/CallHandler.php | 31 +- .../MTProtoTools/MessageHandler.php | 27 +- .../MTProtoTools/MsgIdHandler.php | 7 +- .../MTProtoTools/ResponseHandler.php | 20 +- .../MTProtoTools/SaltHandler.php | 4 +- src/danog/MadelineProto/RSA.php | 25 +- src/danog/MadelineProto/TL/TL.php | 201 +++-- testing.php | 21 +- 19 files changed, 782 insertions(+), 656 deletions(-) delete mode 100644 credentials create mode 100644 src/danog/MadelineProto/APIFactory.php rename src/danog/MadelineProto/{Logging.php => Logger.php} (93%) diff --git a/.gitignore b/.gitignore index 4db62e69..a1fcfdb9 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,4 @@ target/ vendor *save *bak +number.php diff --git a/README.md b/README.md index 6ea2517d..2721c40d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ src/danog/MadelineProto/ TLConstructor - Represents a TL Constructor TLMethod - Represents a TL method API - Wrapper class that istantiates the MTProto class, sets the error handler, and provides a wrapper for calling mtproto methods directly as class submethods + APIFactory - Provides a wrapper for calling namespaced mtproto methods directly as class submethods Connection - Handles tcp/udp/http connections and wrapping payloads generated by MTProtoTools/MessageHandler into the right message according to the protocol DataCenter - Handles mtproto datacenters DebugTools - Various debugging tools @@ -47,4 +48,3 @@ This project adheres to the [Hacktoberfest](https://hacktoberfest.digitalocean.c Browse the issues of this project and help close at least four of them with a pull request to get a free t-shirt! Check out the [Contribution guide first though](https://github.com/danog/MadelineProto/blob/master/CONTRIBUTING.md). -The name of this project is inspired by [this person](https://s-media-cache-ak0.pinimg.com/736x/f0/a1/70/f0a170718baeb0e3817c612d96f5d1cf.jpg). diff --git a/credentials b/credentials deleted file mode 100644 index bcd5101f..00000000 --- a/credentials +++ /dev/null @@ -1,5 +0,0 @@ -[App data] -api_id = 25628 -api_hash = 1fe17cda7d355166cdaa71f04122873c -ip_address = 149.154.167.50 -port = 443 diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index a5095f7e..8fc42029 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -20,11 +20,23 @@ class API extends Tools { set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']); $this->session = new MTProto($params); - $ping_res = $this->ping(3); - if (isset($ping['_']) && $ping['_'] == 'pong') { - $this->log->log('Pong: '.$ping['ping_id']); + + \danog\MadelineProto\Logger::log('Running APIFactory...'); + foreach ($this->session->tl->method_name_namespaced as $method) { + if (isset($method[1])) { + if (!isset($this->{$method[0]})) { + $this->{$method[0]} = new APIFactory($method[0], $this->session); + } + $this->{$method[0]}->allowed_methods[] = $method[1]; + } } + + \danog\MadelineProto\Logger::log('Ping...'); + $ping = $this->ping(3); + \danog\MadelineProto\Logger::log('Pong: '.$ping['ping_id']); + \danog\MadelineProto\Logger::log('Getting future salts...'); $future_salts = $this->get_future_salts(3); + \danog\MadelineProto\Logger::log('MadelineProto is ready!'); } public function __destruct() @@ -35,6 +47,9 @@ class API extends Tools public function __call($name, $arguments) { + if (!in_array($name, $this->session->tl->method_name_namespaced)) { + throw new Exception("The called method doesn't exist!"); + } return $this->session->method_call($name, $arguments); } } diff --git a/src/danog/MadelineProto/APIFactory.php b/src/danog/MadelineProto/APIFactory.php new file mode 100644 index 00000000..6edda9de --- /dev/null +++ b/src/danog/MadelineProto/APIFactory.php @@ -0,0 +1,34 @@ +. +*/ + +namespace danog\MadelineProto; + +class APIFactory +{ + public $namespace; + public $session; + public $allowed_methods = []; + + public function __construct($namespace, $session) { + $this->namespace = $namespace; + $this->session = $session; + } + + public function __call($name, $arguments) + { + if (!in_array($name, $this->allowed_methods)) { + throw new Exception("The called method doesn't exist!"); + } + return $this->session->method_call($this->namespace.'.'.$name, $arguments); + } + +} \ No newline at end of file diff --git a/src/danog/MadelineProto/Connection.php b/src/danog/MadelineProto/Connection.php index 39470497..f998e6e9 100644 --- a/src/danog/MadelineProto/Connection.php +++ b/src/danog/MadelineProto/Connection.php @@ -18,8 +18,14 @@ namespace danog\MadelineProto; class Connection extends Tools { public $sock = null; + public $protocol = null; - private $_delta = 0; + + public $time_delta = 0; + public $temp_auth_key; + public $auth_key; + public $session_id; + public $seq_no = 0; public function __construct($ip, $port, $protocol = 'tcp_full') { @@ -33,6 +39,8 @@ class Connection extends Tools - udp */ $this->protocol = $protocol; + $this->ip = $ip; + $this->port = $port; switch ($this->protocol) { case 'tcp_abridged': $this->sock = fsockopen('tcp://'.$ip.':'.$port); @@ -59,6 +67,10 @@ class Connection extends Tools $this->out_seq_no = -1; $this->in_seq_no = -1; break; + case 'http': + case 'https': + case 'udp': + throw new Exception("Connection: This protocol wasn't implemented yet."); default: throw new Exception('Connection: invalid protocol specified.'); break; @@ -73,20 +85,19 @@ class Connection extends Tools case 'tcp_full': fclose($this->sock); break; + case 'http': + case 'https': + case 'udp': + throw new Exception("Connection: This protocol wasn't implemented yet."); default: throw new Exception('Connection: invalid protocol specified.'); break; } } - public function set_time_delta($delta) - { - $this->_delta = $delta; - } - - public function get_time_delta() - { - return $this->_delta; + public function close_and_reopen() { + $this->__destruct(); + $this->__construct($this->ip, $this->port, $this->protocol); } /** @@ -114,6 +125,10 @@ class Connection extends Tools return fwrite($this->sock, $what); break; + case 'http': + case 'https': + case 'udp': + throw new Exception("Connection: This protocol wasn't implemented yet."); default: throw new Exception('Connection: invalid protocol specified.'); break; @@ -132,6 +147,10 @@ class Connection extends Tools return fread($this->sock, $length); break; + case 'http': + case 'https': + case 'udp': + throw new Exception("Connection: This protocol wasn't implemented yet."); default: throw new Exception('Connection: invalid protocol specified.'); break; @@ -182,6 +201,10 @@ class Connection extends Tools $packet = $this->sock->read($packet_length); $payload = $this->fopen_and_write('php://memory', 'rw+b', $packet); break; + case 'http': + case 'https': + case 'udp': + throw new Exception("Connection: This protocol wasn't implemented yet."); } return $payload; @@ -209,6 +232,10 @@ class Connection extends Tools } $this->write($step1); break; + case 'http': + case 'https': + case 'udp': + throw new Exception("Connection: This protocol wasn't implemented yet."); default: break; } diff --git a/src/danog/MadelineProto/DataCenter.php b/src/danog/MadelineProto/DataCenter.php index f629801f..f6a79d42 100644 --- a/src/danog/MadelineProto/DataCenter.php +++ b/src/danog/MadelineProto/DataCenter.php @@ -17,6 +17,8 @@ namespace danog\MadelineProto; */ class DataCenter extends Tools { + public $referenced_variables = ["time_delta", "temp_auth_key", "auth_key", "session_id", "seq_no"]; + public function __construct($dclist, $settings) { $this->dclist = $dclist; @@ -36,13 +38,12 @@ class DataCenter extends Tools ]; } } - $this->dc_connect($settings['default_dc']); } public function dc_disconnect($dc_number) { if (isset($this->sockets[$dc_number])) { - \danog\MadelineProto\Logging::log('Disconnecting from DC '.$dc_number.'...'); + \danog\MadelineProto\Logger::log('Disconnecting from DC '.$dc_number.'...'); unset($this->sockets[$dc_number]); unset($this->curdc); } @@ -51,9 +52,9 @@ class DataCenter extends Tools public function dc_connect($dc_number, $settings = []) { if (isset($this->sockets[$dc_number])) { - return; + return false; } - \danog\MadelineProto\Logging::log('Connecting to DC '.$dc_number.'...'); + \danog\MadelineProto\Logger::log('Connecting to DC '.$dc_number.'...'); if ($settings == []) { $settings = $this->settings[$dc_number]; @@ -65,47 +66,32 @@ class DataCenter extends Tools $address = 'https://'.$subdomain.'.web.telegram.org/'.$path; } $this->sockets[$dc_number] = new Connection($address, $settings['port'], $settings['protocol']); + $this->set_curdc($dc_number); + return true; + } + + public function set_curdc($dc_number) { $this->curdc = $dc_number; + foreach ($referenced_variables as $key) { + $this->{$key} = &$this->sockets[$dc_number]->{$key}; + } + } + public function unset_curdc($dc_number) { + unset($this->curdc); + foreach ($referenced_variables as $key) { + unset($this->sockets[$dc_number]->{$key}); + } } - public function set_time_delta($delta, $dc_number = -1) + + public function __call($name, $arguments) { - if ($dc_number == -1) { - $dc_number = $this->curdc; - } - - return $this->sockets[$dc_number]->set_time_delta($delta); - } - - public function get_time_delta($dc_number = -1) - { - if ($dc_number == -1) { - $dc_number = $this->curdc; - } - - return $this->sockets[$dc_number]->get_time_delta(); - } - - public function send_message($message, $dc_number = -1) - { - if ($dc_number == -1) { - $dc_number = $this->curdc; - } - - return $this->sockets[$dc_number]->send_message($message); - } - - public function read_message($dc_number = -1) - { - if ($dc_number == -1) { - $dc_number = $this->curdc; - } - - return $this->sockets[$dc_number]->read_message(); + return $this->sockets[$this->curdc]->{$name}(...$arguments); } public function __destroy() { + $this->unset_curdc(); foreach ($this->sockets as $n => $socket) { unset($this->sockets[$n]); } diff --git a/src/danog/MadelineProto/Logging.php b/src/danog/MadelineProto/Logger.php similarity index 93% rename from src/danog/MadelineProto/Logging.php rename to src/danog/MadelineProto/Logger.php index 3f346117..f818ddec 100644 --- a/src/danog/MadelineProto/Logging.php +++ b/src/danog/MadelineProto/Logger.php @@ -10,12 +10,12 @@ You should have received a copy of the GNU General Public License along with the If not, see . */ /* - * Logging class + * Logger class */ namespace danog\MadelineProto; -class Logging +class Logger { public static $mode = null; public static $optional = null; @@ -23,9 +23,9 @@ class Logging /* * Constructor function - * Accepts various logging modes: - * 0 - No logging - * 1 - Log to the default logging destination + * Accepts various logger modes: + * 0 - No logger + * 1 - Log to the default logger destination * 2 - Log to file defined in second parameter * 3 - Echo logs */ diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 35bd7f48..cca81e9b 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -24,10 +24,7 @@ class MTProto extends MTProtoTools // Set default settings $default_settings = [ 'authorization' => [ - 'auth_key' => null, - 'temp_auth_key' => null, 'default_temp_auth_key_expires_in' => 86400, - 'session_id' => \phpseclib\Crypt\Random::string(8), 'rsa_key' => '-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS @@ -61,7 +58,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB 'connection_settings' => [ 'all' => [ 'protocol' => 'tcp_full', - 'test_mode' => true, + 'test_mode' => false, 'port' => '443', ], 'default_dc' => 2, @@ -81,10 +78,10 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB __DIR__.'/TL_telegram_v55.json', ], ], - 'logging' => [ - 'logging' => 1, - 'logging_param' => '/tmp/MadelineProto.log', - 'logging' => 3, + 'logger' => [ + 'logger' => 1, + 'logger_param' => '/tmp/MadelineProto.log', + 'logger' => 3, ], 'max_tries' => [ 'query' => 5, @@ -107,42 +104,67 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB } } $this->settings = $settings; - // Set up logging class - \danog\MadelineProto\Logging::constructor($this->settings['logging']['logging'], $this->settings['logging']['logging_param']); + + // Setup logger + $this->setup_logger(); // Connect to servers - \danog\MadelineProto\Logging::log('Connecting to server...'); - $this->connection = new DataCenter($this->settings['connection'], $this->settings['connection_settings']); + \danog\MadelineProto\Logger::log('Istantiating DataCenter...'); + $this->datacenter = new DataCenter($this->settings['connection'], $this->settings['connection_settings']); // Load rsa key - \danog\MadelineProto\Logging::log('Loading RSA key...'); + \danog\MadelineProto\Logger::log('Loading RSA key...'); $this->key = new RSA($settings['authorization']['rsa_key']); // Istantiate TL class - \danog\MadelineProto\Logging::log('Translating tl schemas...'); + \danog\MadelineProto\Logger::log('Translating tl schemas...'); $this->tl = new TL\TL($this->settings['tl_schema']['src']); - $this->seq_no = 0; $this->incoming_messages = []; $this->outgoing_messages = []; $this->future_salts = []; - if ($this->settings['authorization']['temp_auth_key'] == null || $this->settings['authorization']['auth_key'] == null) { - if ($this->settings['authorization']['auth_key'] == null) { - \danog\MadelineProto\Logging::log('Generating permanent authorization key...'); - $this->settings['authorization']['auth_key'] = $this->create_auth_key(-1); - } - \danog\MadelineProto\Logging::log('Generating temporary authorization key...'); - $this->settings['authorization']['temp_auth_key'] = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']); - } - $this->write_client_info(); - $this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']); - \danog\MadelineProto\Logging::log('You may now login to Telegram.'); + $this->switch_dc($this->settings['connection_settings']['default_dc'], true); + } - public function write_client_info() + public function setup_logger() { - \danog\MadelineProto\Logging::log('Writing client info...'); + if (!\danog\MadelineProto\Logger::$constructed) { + // Set up logger class + \danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param']); + } + } + + // Switches to a new datacenter and if necessary creates authorization keys, binds them and writes client info + public function switch_dc($new_dc, $allow_nearestdc_switch = false) + { + \danog\MadelineProto\Logger::log('Switching to DC '.$new_dc.'...'); + if ($this->datacenter->dc_connect($new_dc)) { + $this->init_authorization(); + $this->write_client_info(); + $this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $allow_nearestdc_switch); + } + } + + // Creates authorization keys + public function init_authorization() + { + if ($this->datacenter->session_id == null) { + $this->datacenter->session_id = \phpseclib\Crypt\Random::string(8); + } + if ($this->datacenter->temp_auth_key == null || $this->datacenter->auth_key == null) { + if ($this->datacenter->auth_key == null) { + \danog\MadelineProto\Logger::log('Generating permanent authorization key...'); + $this->datacenter->auth_key = $this->create_auth_key(-1); + } + \danog\MadelineProto\Logger::log('Generating temporary authorization key...'); + $this->datacenter->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']); + } + } + public function write_client_info($allow_switch) { + + \danog\MadelineProto\Logger::log('Writing client info...'); $nearestDc = $this->method_call( 'invokeWithLayer', [ @@ -155,17 +177,17 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB ), ] ); - \danog\MadelineProto\Logging::log('Current dc is '.$nearestDc['this_dc'].', nearest dc is '.$nearestDc['nearest_dc'].' in '.$nearestDc['country'].'.'); + \danog\MadelineProto\Logger::log('Current dc is '.$nearestDc['this_dc'].', nearest dc is '.$nearestDc['nearest_dc'].' in '.$nearestDc['country'].'.'); - if ($nearestDc['nearest_dc'] != $nearestDc['this_dc']) { - \danog\MadelineProto\Logging::log('Switching to dc '.$nearestDc['nearest_dc'].'...'); - $this->connection->dc_connect($nearestDc['nearest_dc']); + if ($nearestDc['nearest_dc'] != $nearestDc['this_dc'] && $allow_switch) { + $this->switch_dc($nearestDc['nearest_dc']); $this->settings['connection_settings']['default_dc'] = $nearestDc['nearest_dc']; } + } public function __destruct() { - unset($this->sock); + unset($this->datacenter); } } diff --git a/src/danog/MadelineProto/MTProtoTools/AckHandler.php b/src/danog/MadelineProto/MTProtoTools/AckHandler.php index feb9d98e..57dedec8 100644 --- a/src/danog/MadelineProto/MTProtoTools/AckHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/AckHandler.php @@ -28,7 +28,7 @@ class AckHandler extends \danog\MadelineProto\PrimeModule public function ack_incoming_message_id($message_id) { - if ($this->settings['authorization']['temp_auth_key']['id'] === null || $this->settings['authorization']['temp_auth_key']['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) { + if ($this->datacenter->temp_auth_key['id'] === null || $this->datacenter->temp_auth_key['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) { return; } // I let the server know that I received its message diff --git a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php index 95118cc6..88126c6c 100644 --- a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php @@ -23,435 +23,439 @@ class AuthKeyHandler extends AckHandler public function create_auth_key($expires_in = -1) { foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id_total) { - \danog\MadelineProto\Logging::log('Requesting pq'); + try { + \danog\MadelineProto\Logger::log('Requesting pq'); - /** - * *********************************************************************** - * Make pq request, DH exchange initiation. - * - * @method req_pq - * - * @param [ - * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication - * ] - * - * @return ResPQ [ - * int128 $nonce : The value of nonce is selected randomly by the server - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * string $pq : This is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers - * Vector long $server_public_key_fingerprints : This is a list of public RSA key fingerprints - * ] - */ - $nonce = \phpseclib\Crypt\Random::string(16); - $ResPQ = $this->method_call('req_pq', - [ - 'nonce' => $nonce, - ] - ); - - /* - * *********************************************************************** - * Check if the client's nonce and the server's nonce are the same - */ - if ($ResPQ['nonce'] !== $nonce) { - throw new Exception('wrong nonce'); - } - - /* - * *********************************************************************** - * Find our key in the server_public_key_fingerprints vector - */ - foreach ($ResPQ['server_public_key_fingerprints'] as $curfp) { - $curfp_biginteger = new \phpseclib\Math\BigInteger($curfp); - - if ($this->key->fp->equals($curfp_biginteger)) { - $public_key_fingerprint = $curfp; - break; - } - } - - if (!isset($public_key_fingerprint)) { - throw new Exception("Couldn't find our key in the server_public_key_fingerprints vector."); - } - - $pq_bytes = $ResPQ['pq']; - $server_nonce = $ResPQ['server_nonce']; - - /* - * *********************************************************************** - * Compute p and q - */ - $pq = new \phpseclib\Math\BigInteger($pq_bytes, 256); - list($p, $q) = $this->PrimeFactors($pq); - $p = new \phpseclib\Math\BigInteger($p); - $q = new \phpseclib\Math\BigInteger($q); - - if ($p->compare($q) > 0) { - list($p, $q) = [$q, $p]; - } - - if (!($pq->equals($p->multiply($q)) && $p->compare($q) < 0)) { - throw new Exception("couldn't compute p and q."); - } - - \danog\MadelineProto\Logging::log('Factorization '.$pq.' = '.$p.' * '.$q); - - /* - * *********************************************************************** - * Serialize object for req_DH_params - */ - $p_bytes = \danog\PHP\Struct::pack('>I', (string) $p); - $q_bytes = \danog\PHP\Struct::pack('>I', (string) $q); - - $new_nonce = \phpseclib\Crypt\Random::string(32); - if ($expires_in < 0) { - $data = $this->tl->serialize_obj('p_q_inner_data', + /** + * *********************************************************************** + * Make pq request, DH exchange initiation. + * + * @method req_pq + * + * @param [ + * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication + * ] + * + * @return ResPQ [ + * int128 $nonce : The value of nonce is selected randomly by the server + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * string $pq : This is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers + * Vector long $server_public_key_fingerprints : This is a list of public RSA key fingerprints + * ] + */ + $nonce = \phpseclib\Crypt\Random::string(16); + $ResPQ = $this->method_call('req_pq', [ - 'pq' => $pq_bytes, - 'p' => $p_bytes, - 'q' => $q_bytes, - 'nonce' => $nonce, - 'server_nonce' => $server_nonce, - 'new_nonce' => $new_nonce, + 'nonce' => $nonce, ] ); - } else { - $data = $this->tl->serialize_obj('p_q_inner_data_temp', - [ - 'pq' => $pq_bytes, - 'p' => $p_bytes, - 'q' => $q_bytes, - 'nonce' => $nonce, - 'server_nonce' => $server_nonce, - 'new_nonce' => $new_nonce, - 'expires_in' => $expires_in, - ] - ); - } - - /* - * *********************************************************************** - * Encrypt serialized object - */ - $sha_digest = sha1($data, true); - $random_bytes = \phpseclib\Crypt\Random::string(255 - strlen($data) - strlen($sha_digest)); - $to_encrypt = $sha_digest.$data.$random_bytes; - $encrypted_data = $this->key->encrypt($to_encrypt); - - \danog\MadelineProto\Logging::log('Starting Diffie Hellman key exchange'); - /* - * *********************************************************************** - * Starting Diffie Hellman key exchange, Server authentication - * @method req_DH_params - * @param [ - * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * string $p : The value of BigInteger - * string $q : The value of BigInteger - * long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector - * string $encrypted_data - * ] - * @return Server_DH_Params [ - * int128 $nonce : The value of nonce is selected randomly by the server - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * string $new_nonce_hash : Return this value If server responds is server_DH_params_fail - * string $encrypted_answer : Return this value If server responds is server_DH_params_ok - * ] - */ - // - $server_dh_params = $this->method_call('req_DH_params', - [ - 'nonce' => $nonce, - 'server_nonce' => $server_nonce, - 'p' => $p_bytes, - 'q' => $q_bytes, - 'public_key_fingerprint' => $public_key_fingerprint, - 'encrypted_data' => $encrypted_data, - ] - ); - - /* - * *********************************************************************** - * Check if the client's nonce and the server's nonce are the same - */ - if ($nonce != $server_dh_params['nonce']) { - throw new Exception('wrong nonce.'); - } - - /* - * *********************************************************************** - * Check if server_nonce and new server_nonce are the same - */ - if ($server_nonce != $server_dh_params['server_nonce']) { - throw new Exception('wrong server nonce.'); - } - - /* - * *********************************************************************** - * Check valid new nonce hash if return from server - * new nonce hash return in server_DH_params_fail - */ - if (isset($server_dh_params['new_nonce_hash']) && substr(sha1($new_nonce), -32) != $server_dh_params['new_nonce_hash']) { - throw new Exception('wrong new nonce hash.'); - } - - /* - * *********************************************************************** - * Get key, iv and decrypt answer - */ - $encrypted_answer = $server_dh_params['encrypted_answer']; - - $tmp_aes_key = sha1($new_nonce.$server_nonce, true).substr(sha1($server_nonce.$new_nonce, true), 0, 12); - $tmp_aes_iv = substr(sha1($server_nonce.$new_nonce, true), 12, 8).sha1($new_nonce.$new_nonce, true).substr($new_nonce, 0, 4); - $answer_with_hash = $this->ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv); - - /* - * *********************************************************************** - * Separate answer and hash - */ - $answer_hash = substr($answer_with_hash, 0, 20); - $answer = substr($answer_with_hash, 20); - - /* - * *********************************************************************** - * Deserialize answer - * @return Server_DH_inner_data [ - * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * int $g - * string $dh_prime - * string $g_a - * int $server_time - * ] - */ - $server_DH_inner_data = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $answer)); - - /* - * *********************************************************************** - * Do some checks - */ - $server_DH_inner_data_length = $this->tl->get_length($this->fopen_and_write('php://memory', 'rw+b', $answer)); - if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) { - throw new Exception('answer_hash mismatch.'); - } - - if ($nonce != $server_DH_inner_data['nonce']) { - throw new Exception('wrong nonce'); - } - - if ($server_nonce != $server_DH_inner_data['server_nonce']) { - throw new Exception('wrong server nonce'); - } - - $g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']); - $g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256); - $dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256); - - /* - * *********************************************************************** - * Time delta - */ - $server_time = $server_DH_inner_data['server_time']; - $this->connection->set_time_delta($server_time - time()); - - \danog\MadelineProto\Logging::log(sprintf('Server-client time delta = %.1f s', $this->connection->get_time_delta())); - - - /* - * *********************************************************************** - * Define some needed numbers for BigInteger - */ - \danog\MadelineProto\Logging::log('Executing dh_prime checks...'); - $one = new \phpseclib\Math\BigInteger(1); - $two = new \phpseclib\Math\BigInteger(2); - $twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328'); - $twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656'); - - /* - * *********************************************************************** - * Check validity of dh_prime - * 2^2047 < dh_prime < 2^2048 - */ - if (!$dh_prime->isPrime()) { - throw new Exception("dh_prime isn't a safe 2048-bit prime (dh_prime isn't a prime)."); - } - - /* - // Almost always fails - if (!$dh_prime->subtract($one)->divide($two)[0]->isPrime()) { - throw new Exception("dh_prime isn't a safe 2048-bit prime ((dh_prime - 1) / 2 isn't a prime)."); - } - */ - - if ($dh_prime->compare($twoe2047) <= 0 // 2^2047 < dh_prime or dh_prime > 2^2047 or ! dh_prime <= 2^2047 - || $dh_prime->compare($twoe2048) >= 0 // dh_prime < 2^2048 or ! dh_prime >= 2^2048 - ) { - throw new Exception("g isn't a safe 2048-bit prime (2^2047 < dh_prime < 2^2048 is false)."); - } - - /* - * *********************************************************************** - * Check validity of g - * 1 < g < dh_prime - 1 - */ - if ($g->compare($one) <= 0 // 1 < g or g > 1 or ! g <= 1 - || $g->compare($dh_prime->subtract($one)) >= 0 // g < dh_prime - 1 or ! g >= dh_prime - 1 - ) { - throw new Exception('g is invalid (1 < g < dh_prime - 1 is false).'); - } - - /* - * *********************************************************************** - * Check validity of g_a - * 1 < g_a < dh_prime - 1 - */ - if ($g_a->compare($one) <= 0 // 1 < g_a or g_a > 1 or ! g_a <= 1 - || $g_a->compare($dh_prime->subtract($one)) >= 0 // g_a < dh_prime - 1 or ! g_a >= dh_prime - 1 - ) { - throw new Exception('g_a is invalid (1 < g_a < dh_prime - 1 is false).'); - } - - foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) { - $b = new \phpseclib\Math\BigInteger(\phpseclib\Crypt\Random::string(256), 256); - $g_b = $g->powMod($b, $dh_prime); /* - * *********************************************************************** - * Check validity of g_b - * 1 < g_b < dh_prime - 1 - */ - if ($g_b->compare($one) <= 0 // 1 < g_b or g_b > 1 or ! g_b <= 1 - || $g_b->compare($dh_prime->subtract($one)) >= 0 // g_b < dh_prime - 1 or ! g_b >= dh_prime - 1 - ) { - throw new Exception('g_b is invalid (1 < g_b < dh_prime - 1 is false).'); + * *********************************************************************** + * Check if the client's nonce and the server's nonce are the same + */ + if ($ResPQ['nonce'] !== $nonce) { + throw new Exception('wrong nonce'); } - $g_b_str = $g_b->toBytes(); + /* + * *********************************************************************** + * Find our key in the server_public_key_fingerprints vector + */ + foreach ($ResPQ['server_public_key_fingerprints'] as $curfp) { + $curfp_biginteger = new \phpseclib\Math\BigInteger($curfp); + + if ($this->key->fp->equals($curfp_biginteger)) { + $public_key_fingerprint = $curfp; + break; + } + } + + if (!isset($public_key_fingerprint)) { + throw new Exception("Couldn't find our key in the server_public_key_fingerprints vector."); + } + + $pq_bytes = $ResPQ['pq']; + $server_nonce = $ResPQ['server_nonce']; /* - * *********************************************************************** - * serialize client_DH_inner_data - * @method client_DH_inner_data - * @param Server_DH_inner_data [ - * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * long $retry_id : First attempt - * string $g_b : g^b mod dh_prime - * ] - */ - $data = $this->tl->serialize_obj('client_DH_inner_data', + * *********************************************************************** + * Compute p and q + */ + $pq = new \phpseclib\Math\BigInteger($pq_bytes, 256); + list($p, $q) = $this->PrimeFactors($pq); + $p = new \phpseclib\Math\BigInteger($p); + $q = new \phpseclib\Math\BigInteger($q); + + if ($p->compare($q) > 0) { + list($p, $q) = [$q, $p]; + } + + if (!($pq->equals($p->multiply($q)) && $p->compare($q) < 0)) { + throw new Exception("couldn't compute p and q."); + } + + \danog\MadelineProto\Logger::log('Factorization '.$pq.' = '.$p.' * '.$q); + + /* + * *********************************************************************** + * Serialize object for req_DH_params + */ + $p_bytes = \danog\PHP\Struct::pack('>I', (string) $p); + $q_bytes = \danog\PHP\Struct::pack('>I', (string) $q); + + $new_nonce = \phpseclib\Crypt\Random::string(32); + if ($expires_in < 0) { + $data = $this->tl->serialize_obj('p_q_inner_data', + [ + 'pq' => $pq_bytes, + 'p' => $p_bytes, + 'q' => $q_bytes, + 'nonce' => $nonce, + 'server_nonce' => $server_nonce, + 'new_nonce' => $new_nonce, + ] + ); + } else { + $data = $this->tl->serialize_obj('p_q_inner_data_temp', + [ + 'pq' => $pq_bytes, + 'p' => $p_bytes, + 'q' => $q_bytes, + 'nonce' => $nonce, + 'server_nonce' => $server_nonce, + 'new_nonce' => $new_nonce, + 'expires_in' => $expires_in, + ] + ); + } + + /* + * *********************************************************************** + * Encrypt serialized object + */ + $sha_digest = sha1($data, true); + $random_bytes = \phpseclib\Crypt\Random::string(255 - strlen($data) - strlen($sha_digest)); + $to_encrypt = $sha_digest.$data.$random_bytes; + $encrypted_data = $this->key->encrypt($to_encrypt); + + \danog\MadelineProto\Logger::log('Starting Diffie Hellman key exchange'); + /* + * *********************************************************************** + * Starting Diffie Hellman key exchange, Server authentication + * @method req_DH_params + * @param [ + * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * string $p : The value of BigInteger + * string $q : The value of BigInteger + * long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector + * string $encrypted_data + * ] + * @return Server_DH_Params [ + * int128 $nonce : The value of nonce is selected randomly by the server + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * string $new_nonce_hash : Return this value If server responds is server_DH_params_fail + * string $encrypted_answer : Return this value If server responds is server_DH_params_ok + * ] + */ + // + $server_dh_params = $this->method_call('req_DH_params', [ - 'nonce' => $nonce, - 'server_nonce' => $server_nonce, - 'retry_id' => $retry_id, - 'g_b' => $g_b_str, + 'nonce' => $nonce, + 'server_nonce' => $server_nonce, + 'p' => $p_bytes, + 'q' => $q_bytes, + 'public_key_fingerprint' => $public_key_fingerprint, + 'encrypted_data' => $encrypted_data, ] ); /* - * *********************************************************************** - * encrypt client_DH_inner_data - */ - $data_with_sha = sha1($data, true).$data; - $data_with_sha_padded = $data_with_sha.\phpseclib\Crypt\Random::string($this->posmod(-strlen($data_with_sha), 16)); - $encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv); - - /* - * *********************************************************************** - * Send set_client_DH_params query - * @method set_client_DH_params - * @param Server_DH_inner_data [ - * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * string $encrypted_data - * ] - * @return Set_client_DH_params_answer [ - * string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail - * int128 $server_nonce : The value of server_nonce is selected randomly by the server - * int128 $new_nonce_hash1 : Return this value If server responds is dh_gen_ok - * int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_retry - * int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_fail - * ] - */ - $Set_client_DH_params_answer = $this->method_call('set_client_DH_params', - [ - 'nonce' => $nonce, - 'server_nonce' => $server_nonce, - 'encrypted_data' => $encrypted_data, - ] - ); - - /* - * *********************************************************************** - * Generate auth_key - */ - $auth_key = $g_a->powMod($b, $dh_prime); - $auth_key_str = $auth_key->toBytes(); - $auth_key_sha = sha1($auth_key_str, true); - $auth_key_aux_hash = substr($auth_key_sha, 0, 8); - $new_nonce_hash1 = substr(sha1($new_nonce.chr(1).$auth_key_aux_hash, true), -16); - $new_nonce_hash2 = substr(sha1($new_nonce.chr(2).$auth_key_aux_hash, true), -16); - $new_nonce_hash3 = substr(sha1($new_nonce.chr(3).$auth_key_aux_hash, true), -16); - - - /* - * *********************************************************************** - * Check if the client's nonce and the server's nonce are the same - */ - if ($Set_client_DH_params_answer['nonce'] != $nonce) { + * *********************************************************************** + * Check if the client's nonce and the server's nonce are the same + */ + if ($nonce != $server_dh_params['nonce']) { throw new Exception('wrong nonce.'); } /* - * *********************************************************************** - * Check if server_nonce and new server_nonce are the same - */ - if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) { - throw new Exception('wrong server nonce'); + * *********************************************************************** + * Check if server_nonce and new server_nonce are the same + */ + if ($server_nonce != $server_dh_params['server_nonce']) { + throw new Exception('wrong server nonce.'); } /* - * *********************************************************************** - * Check Set_client_DH_params_answer type - */ - switch ($Set_client_DH_params_answer['_']) { - case 'dh_gen_ok': - if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) { - throw new Exception('wrong new_nonce_hash1'); - } - - \danog\MadelineProto\Logging::log('Diffie Hellman key exchange processed successfully'); - - $res_authorization['server_salt'] = \danog\PHP\Struct::unpack('ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv); + + /* + * *********************************************************************** + * Separate answer and hash + */ + $answer_hash = substr($answer_with_hash, 0, 20); + $answer = substr($answer_with_hash, 20); + + /* + * *********************************************************************** + * Deserialize answer + * @return Server_DH_inner_data [ + * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * int $g + * string $dh_prime + * string $g_a + * int $server_time + * ] + */ + $server_DH_inner_data = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $answer)); + + /* + * *********************************************************************** + * Do some checks + */ + $server_DH_inner_data_length = $this->tl->get_length($this->fopen_and_write('php://memory', 'rw+b', $answer)); + if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) { + throw new Exception('answer_hash mismatch.'); + } + + if ($nonce != $server_DH_inner_data['nonce']) { + throw new Exception('wrong nonce'); + } + + if ($server_nonce != $server_DH_inner_data['server_nonce']) { + throw new Exception('wrong server nonce'); + } + + $g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']); + $g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256); + $dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256); + + /* + * *********************************************************************** + * Time delta + */ + $server_time = $server_DH_inner_data['server_time']; + $this->datacenter->set_time_delta($server_time - time()); + + \danog\MadelineProto\Logger::log(sprintf('Server-client time delta = %.1f s', $this->datacenter->get_time_delta())); + + + /* + * *********************************************************************** + * Define some needed numbers for BigInteger + */ + \danog\MadelineProto\Logger::log('Executing dh_prime checks...'); + $one = new \phpseclib\Math\BigInteger(1); + $two = new \phpseclib\Math\BigInteger(2); + $twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328'); + $twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656'); + + /* + * *********************************************************************** + * Check validity of dh_prime + * 2^2047 < dh_prime < 2^2048 + */ + if (!$dh_prime->isPrime()) { + throw new Exception("dh_prime isn't a safe 2048-bit prime (dh_prime isn't a prime)."); + } + + /* + // Almost always fails + if (!$dh_prime->subtract($one)->divide($two)[0]->isPrime()) { + throw new Exception("dh_prime isn't a safe 2048-bit prime ((dh_prime - 1) / 2 isn't a prime)."); + } + */ + + if ($dh_prime->compare($twoe2047) <= 0 // 2^2047 < dh_prime or dh_prime > 2^2047 or ! dh_prime <= 2^2047 + || $dh_prime->compare($twoe2048) >= 0 // dh_prime < 2^2048 or ! dh_prime >= 2^2048 + ) { + throw new Exception("g isn't a safe 2048-bit prime (2^2047 < dh_prime < 2^2048 is false)."); + } + + /* + * *********************************************************************** + * Check validity of g + * 1 < g < dh_prime - 1 + */ + if ($g->compare($one) <= 0 // 1 < g or g > 1 or ! g <= 1 + || $g->compare($dh_prime->subtract($one)) >= 0 // g < dh_prime - 1 or ! g >= dh_prime - 1 + ) { + throw new Exception('g is invalid (1 < g < dh_prime - 1 is false).'); + } + + /* + * *********************************************************************** + * Check validity of g_a + * 1 < g_a < dh_prime - 1 + */ + if ($g_a->compare($one) <= 0 // 1 < g_a or g_a > 1 or ! g_a <= 1 + || $g_a->compare($dh_prime->subtract($one)) >= 0 // g_a < dh_prime - 1 or ! g_a >= dh_prime - 1 + ) { + throw new Exception('g_a is invalid (1 < g_a < dh_prime - 1 is false).'); + } + + foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) { + $b = new \phpseclib\Math\BigInteger(\phpseclib\Crypt\Random::string(256), 256); + $g_b = $g->powMod($b, $dh_prime); + + /* + * *********************************************************************** + * Check validity of g_b + * 1 < g_b < dh_prime - 1 + */ + if ($g_b->compare($one) <= 0 // 1 < g_b or g_b > 1 or ! g_b <= 1 + || $g_b->compare($dh_prime->subtract($one)) >= 0 // g_b < dh_prime - 1 or ! g_b >= dh_prime - 1 + ) { + throw new Exception('g_b is invalid (1 < g_b < dh_prime - 1 is false).'); + } + + $g_b_str = $g_b->toBytes(); + + /* + * *********************************************************************** + * serialize client_DH_inner_data + * @method client_DH_inner_data + * @param Server_DH_inner_data [ + * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * long $retry_id : First attempt + * string $g_b : g^b mod dh_prime + * ] + */ + $data = $this->tl->serialize_obj('client_DH_inner_data', + [ + 'nonce' => $nonce, + 'server_nonce' => $server_nonce, + 'retry_id' => $retry_id, + 'g_b' => $g_b_str, + ] + ); + + /* + * *********************************************************************** + * encrypt client_DH_inner_data + */ + $data_with_sha = sha1($data, true).$data; + $data_with_sha_padded = $data_with_sha.\phpseclib\Crypt\Random::string($this->posmod(-strlen($data_with_sha), 16)); + $encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv); + + /* + * *********************************************************************** + * Send set_client_DH_params query + * @method set_client_DH_params + * @param Server_DH_inner_data [ + * int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * string $encrypted_data + * ] + * @return Set_client_DH_params_answer [ + * string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail + * int128 $server_nonce : The value of server_nonce is selected randomly by the server + * int128 $new_nonce_hash1 : Return this value If server responds is dh_gen_ok + * int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_retry + * int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_fail + * ] + */ + $Set_client_DH_params_answer = $this->method_call('set_client_DH_params', + [ + 'nonce' => $nonce, + 'server_nonce' => $server_nonce, + 'encrypted_data' => $encrypted_data, + ] + ); + + /* + * *********************************************************************** + * Generate auth_key + */ + $auth_key = $g_a->powMod($b, $dh_prime); + $auth_key_str = $auth_key->toBytes(); + $auth_key_sha = sha1($auth_key_str, true); + $auth_key_aux_hash = substr($auth_key_sha, 0, 8); + $new_nonce_hash1 = substr(sha1($new_nonce.chr(1).$auth_key_aux_hash, true), -16); + $new_nonce_hash2 = substr(sha1($new_nonce.chr(2).$auth_key_aux_hash, true), -16); + $new_nonce_hash3 = substr(sha1($new_nonce.chr(3).$auth_key_aux_hash, true), -16); + + + /* + * *********************************************************************** + * Check if the client's nonce and the server's nonce are the same + */ + if ($Set_client_DH_params_answer['nonce'] != $nonce) { + throw new Exception('wrong nonce.'); + } + + /* + * *********************************************************************** + * Check if server_nonce and new server_nonce are the same + */ + if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) { + throw new Exception('wrong server nonce'); + } + + /* + * *********************************************************************** + * Check Set_client_DH_params_answer type + */ + switch ($Set_client_DH_params_answer['_']) { + case 'dh_gen_ok': + if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) { + throw new Exception('wrong new_nonce_hash1'); + } + + \danog\MadelineProto\Logger::log('Diffie Hellman key exchange processed successfully'); + + $res_authorization['server_salt'] = \danog\PHP\Struct::unpack('settings['authorization']['temp_auth_key']['id'])[0]; - $perm_auth_key_id = \danog\PHP\Struct::unpack('settings['authorization']['auth_key']['id'])[0]; - $temp_session_id = \danog\PHP\Struct::unpack('settings['authorization']['session_id'])[0]; + $temp_auth_key_id = \danog\PHP\Struct::unpack('datacenter->temp_auth_key['id'])[0]; + $perm_auth_key_id = \danog\PHP\Struct::unpack('datacenter->auth_key['id'])[0]; + $temp_session_id = \danog\PHP\Struct::unpack('datacenter->session_id)[0]; $message_data = $this->tl->serialize_obj('bind_auth_key_inner', [ 'nonce' => $nonce, @@ -476,18 +480,18 @@ class AuthKeyHandler extends AckHandler ] ); $int_message_id = $this->generate_message_id(); + $this->check_message_id($int_message_id, true); + $message_id = \danog\PHP\Struct::pack('posmod(-strlen($encrypted_data), 16)); - list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->settings['authorization']['auth_key']['auth_key']); - $encrypted_message = $this->settings['authorization']['auth_key']['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv); + list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->auth_key['auth_key']); + $encrypted_message = $this->datacenter->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv); if ($this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], $int_message_id)) { - \danog\MadelineProto\Logging::log('Successfully binded temporary and permanent authorization keys.'); - $this->write_client_info(); - + \danog\MadelineProto\Logger::log('Successfully binded temporary and permanent authorization keys.'); return true; } throw new Exception('An error occurred while binding temporary and permanent authorization keys.'); diff --git a/src/danog/MadelineProto/MTProtoTools/CallHandler.php b/src/danog/MadelineProto/MTProtoTools/CallHandler.php index f017ffeb..ef25a1d6 100644 --- a/src/danog/MadelineProto/MTProtoTools/CallHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/CallHandler.php @@ -25,7 +25,7 @@ class CallHandler extends AuthKeyHandler $response = null; $count = 0; while ($response == null && $count++ < $this->settings['max_tries']['response']) { - \danog\MadelineProto\Logging::log('Getting response (try number '.$count.' for '.$optional_name.')...'); + \danog\MadelineProto\Logger::log('Getting response (try number '.$count.' for '.$optional_name.')...'); $last_received = $this->recv_message(); $this->handle_message($last_sent, $last_received); if (isset($this->outgoing_messages[$last_sent]['response']) && isset($this->incoming_messages[$this->outgoing_messages[$last_sent]['response']]['content'])) { @@ -37,7 +37,17 @@ class CallHandler extends AuthKeyHandler } switch ($response['_']) { case 'rpc_error': - throw new Exception('Got rpc error '.$response['error_code'].': '.$response['error_message']); + switch ($response['error_code']) { + case 303: + $dc = preg_replace('/[^0-9]+/', '', $response['error_message']); + $this->switch_dc($dc); + return $this->method_call($this->outgoing_messages[$last_sent]['content']['method'], $this->outgoing_messages[$last_sent]['content']['args']); + + break; + default: + throw new Exception('Got rpc error '.$response['error_code'].': '.$response['error_message']); + break; + } break; default: return $response; @@ -47,6 +57,9 @@ class CallHandler extends AuthKeyHandler public function method_call($method, $args, $message_id = null) { + if (!is_array($args)) { + throw new Exception("Arguments aren't an array."); + } foreach (range(1, $this->settings['max_tries']['query']) as $i) { try { $args = $this->tl->get_named_method_args($method, $args); @@ -54,9 +67,8 @@ class CallHandler extends AuthKeyHandler $this->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args]; $server_answer = $this->wait_for_response($int_message_id, $method); } catch (Exception $e) { - \danog\MadelineProto\Logging::log('An error occurred while calling method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Recreating connection and retrying to call method...'); - unset($this->connection); - $this->connection = new \danog\MadelineProto\DataCenter($this->settings['connection'], $this->settings['connection_settings']); + \danog\MadelineProto\Logger::log('An error occurred while calling method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Recreating connection and retrying to call method...'); + $this->datacenter->close_and_reopen(); continue; } if ($server_answer == null) { @@ -70,15 +82,18 @@ class CallHandler extends AuthKeyHandler public function object_call($object, $args) { + if (!is_array($args)) { + throw new Exception("Arguments aren't an array."); + } + foreach (range(1, $this->settings['max_tries']['query']) as $i) { try { $int_message_id = $this->send_message($this->tl->serialize_obj($object, $args), $this->tl->content_related($object)); $this->outgoing_messages[$int_message_id]['content'] = ['object' => $object, 'args' => $args]; // $server_answer = $this->wait_for_response($int_message_id); } catch (Exception $e) { - \danog\MadelineProto\Logging::log('An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...'); - unset($this->connection); - $this->connection = new \danog\MadelineProto\DataCenter($this->settings['connection'], $this->settings['connection_settings']); + \danog\MadelineProto\Logger::log('An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...'); + $this->datacenter->close_and_reopen(); continue; } diff --git a/src/danog/MadelineProto/MTProtoTools/MessageHandler.php b/src/danog/MadelineProto/MTProtoTools/MessageHandler.php index 3e4c73a0..c50e51ce 100644 --- a/src/danog/MadelineProto/MTProtoTools/MessageHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/MessageHandler.php @@ -26,22 +26,21 @@ class MessageHandler extends Crypt if ($int_message_id == null) { $int_message_id = $this->generate_message_id(); } - if (!is_int($int_message_id)) { - throw new Exception("Given message id isn't an integer!"); - } + $this->check_message_id($int_message_id, true); + $message_id = \danog\PHP\Struct::pack('settings['authorization']['temp_auth_key']['auth_key'] == null) || ($this->settings['authorization']['temp_auth_key']['server_salt'] == null)) { + if ($this->datacenter->temp_auth_key['auth_key'] == null || $this->datacenter->temp_auth_key['server_salt'] == null) { $message = $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.\danog\PHP\Struct::pack('generate_seq_no($content_related); - $encrypted_data = \danog\PHP\Struct::pack('settings['authorization']['temp_auth_key']['server_salt']).$this->settings['authorization']['session_id'].$message_id.\danog\PHP\Struct::pack('datacenter->temp_auth_key['server_salt']).$this->datacenter->session_id.$message_id.\danog\PHP\Struct::pack('posmod(-strlen($encrypted_data), 16)); - list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->settings['authorization']['temp_auth_key']['auth_key']); - $message = $this->settings['authorization']['temp_auth_key']['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv); + list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key']); + $message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv); $this->outgoing_messages[$int_message_id]['seq_no'] = $seq_no; } - $this->connection->send_message($message); + $this->datacenter->send_message($message); return $int_message_id; } @@ -51,7 +50,7 @@ class MessageHandler extends Crypt */ public function recv_message() { - $payload = $this->connection->read_message(); + $payload = $this->datacenter->read_message(); if (fstat($payload)['size'] == 4) { throw new Exception('Server response error: '.abs(\danog\PHP\Struct::unpack('check_message_id($message_id, false); $message_data = fread($payload, $message_length); - } elseif ($auth_key_id == $this->settings['authorization']['temp_auth_key']['id']) { + } elseif ($auth_key_id == $this->datacenter->temp_auth_key['id']) { $message_key = fread($payload, 16); $encrypted_data = stream_get_contents($payload); - list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->settings['authorization']['temp_auth_key']['auth_key'], 'from server'); + list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server'); $decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv); $server_salt = \danog\PHP\Struct::unpack('settings['authorization']['temp_auth_key']['server_salt']) { - // throw new Exception('Server salt mismatch (my server salt '.$this->settings['authorization']['temp_auth_key']['server_salt'].' is not equal to server server salt '.$server_salt.').'); + if ($server_salt != $this->datacenter->temp_auth_key['server_salt']) { + throw new Exception('Server salt mismatch (my server salt '.$this->datacenter->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').'); } $session_id = substr($decrypted_data, 8, 8); - if ($session_id != $this->settings['authorization']['session_id']) { + if ($session_id != $this->datacenter->session_id) { throw new Exception('Session id mismatch.'); } diff --git a/src/danog/MadelineProto/MTProtoTools/MsgIdHandler.php b/src/danog/MadelineProto/MTProtoTools/MsgIdHandler.php index ace73dad..de1e2d0d 100644 --- a/src/danog/MadelineProto/MTProtoTools/MsgIdHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/MsgIdHandler.php @@ -19,10 +19,10 @@ class MsgIdHandler extends MessageHandler { public function check_message_id($new_message_id, $outgoing, $container = false) { - if (((int) ((time() + $this->connection->get_time_delta() - 300) * pow(2, 30)) * 4) > $new_message_id) { + if (((int) ((time() + $this->datacenter->get_time_delta() - 300) * pow(2, 30)) * 4) > $new_message_id) { throw new Exception('Given message id ('.$new_message_id.') is too old.'); } - if (((int) ((time() + $this->connection->get_time_delta() + 30) * pow(2, 30)) * 4) < $new_message_id) { + if (((int) ((time() + $this->datacenter->get_time_delta() + 30) * pow(2, 30)) * 4) < $new_message_id) { throw new Exception('Given message id ('.$new_message_id.') is too new.'); } if ($outgoing) { @@ -66,7 +66,7 @@ class MsgIdHandler extends MessageHandler public function generate_message_id() { - $int_message_id = (int) ((time() + $this->connection->get_time_delta()) << 32); + $int_message_id = (int) ((time() + $this->datacenter->get_time_delta()) << 32); /* $int_message_id = (int) ( ((int) ($ms_time / 1000) << 32) | ($this->posmod($ms_time, 1000) << 22) | @@ -78,7 +78,6 @@ class MsgIdHandler extends MessageHandler if ($int_message_id <= $keys) { $int_message_id = $keys + 4; } - $this->check_message_id($int_message_id, true); return $int_message_id; } diff --git a/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php b/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php index 5df42a95..e82dfd5a 100644 --- a/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/ResponseHandler.php @@ -57,7 +57,7 @@ class ResponseHandler extends MsgIdHandler throw new Exception('Received bad_msg_notification for '.$response['bad_msg_id'].': '.$error_codes[$response['error_code']]); break; case 'bad_server_salt': - $this->settings['authorization']['temp_auth_key']['server_salt'] = $response['new_server_salt']; + $this->datacenter->temp_auth_key['server_salt'] = $response['new_server_salt']; $this->ack_outgoing_message_id($response['bad_msg_id']); // Acknowledge that the server received my request $this->outgoing_messages[$response['bad_msg_id']]['response'] = $last_received; $this->incoming_messages[$last_received]['content'] = $response; @@ -73,15 +73,15 @@ class ResponseHandler extends MsgIdHandler } break; case 'new_session_created': - $this->settings['authorization']['temp_auth_key']['server_salt'] = $response['server_salt']; + $this->datacenter->temp_auth_key['server_salt'] = $response['server_salt']; $this->ack_incoming_message_id($last_received); // Acknowledge that I received the server's response - \danog\MadelineProto\Logging::log('new session created'); - \danog\MadelineProto\Logging::log($response); + \danog\MadelineProto\Logger::log('new session created'); + \danog\MadelineProto\Logger::log($response); break; case 'msg_container': $responses = []; - \danog\MadelineProto\Logging::log('Received container.'); - \danog\MadelineProto\Logging::log($response['messages']); + \danog\MadelineProto\Logger::log('Received container.'); + \danog\MadelineProto\Logger::log($response['messages']); foreach ($response['messages'] as $message) { $this->check_message_id($message['msg_id'], false, true); $this->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']]; @@ -100,8 +100,8 @@ class ResponseHandler extends MsgIdHandler return end($responses); break; default: - \danog\MadelineProto\Logging::log('Received multiple responses, returning last one'); - \danog\MadelineProto\Logging::log($responses); + \danog\MadelineProto\Logger::log('Received multiple responses, returning last one'); + \danog\MadelineProto\Logger::log($responses); return end($responses); break; @@ -119,8 +119,8 @@ class ResponseHandler extends MsgIdHandler } break; case 'http_wait': - \danog\MadelineProto\Logging::log('Received http wait.'); - \danog\MadelineProto\Logging::log($response); + \danog\MadelineProto\Logger::log('Received http wait.'); + \danog\MadelineProto\Logger::log($response); break; case 'gzip_packed': $this->incoming_messages[$last_received]['content'] = gzdecode($response); diff --git a/src/danog/MadelineProto/MTProtoTools/SaltHandler.php b/src/danog/MadelineProto/MTProtoTools/SaltHandler.php index fdca3f48..de2e48b9 100644 --- a/src/danog/MadelineProto/MTProtoTools/SaltHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/SaltHandler.php @@ -26,8 +26,8 @@ class SaltHandler extends ResponseHandler public function addsalt($valid_since, $valid_until, $salt) { - if (!isset($this->settings['authorization']['temp_auth_key']['salts'][$salt])) { - $settings['authorization']['temp_auth_key']['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until]; + if (!isset($this->datacenter->temp_auth_key['salts'][$salt])) { + $this->datacenter->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until]; } } } diff --git a/src/danog/MadelineProto/RSA.php b/src/danog/MadelineProto/RSA.php index 83068f76..655d3120 100644 --- a/src/danog/MadelineProto/RSA.php +++ b/src/danog/MadelineProto/RSA.php @@ -22,11 +22,34 @@ class RSA extends TL\TL public function __construct($key) { + \danog\MadelineProto\Logger::log('Istantiating \phpseclib\Crypt\RSA...'); $this->key = new \phpseclib\Crypt\RSA(); + + \danog\MadelineProto\Logger::log('Loading key...'); $this->key->loadKey($key); $this->n = $this->key->modulus; $this->e = $this->key->exponent; - $this->fp_bytes = substr(sha1($this->serialize_param('bytes', null, $this->n->toBytes()).$this->serialize_param('bytes', null, $this->e->toBytes()), true), -8); + + \danog\MadelineProto\Logger::log('Computing fingerprint...'); + $this->fp_bytes = substr( + sha1( + $this->serialize_param( + 'bytes', + null, + $this->n->toBytes() + ) + . + $this->serialize_param( + 'bytes', + null, + $this->e->toBytes() + ), + true + ), + -8 + ); + + \danog\MadelineProto\Logger::log('Generating BigInteger object for fingerprint...'); $this->fp = new \phpseclib\Math\BigInteger(strrev($this->fp_bytes), -256); } diff --git a/src/danog/MadelineProto/TL/TL.php b/src/danog/MadelineProto/TL/TL.php index d995ae91..04f9417f 100644 --- a/src/danog/MadelineProto/TL/TL.php +++ b/src/danog/MadelineProto/TL/TL.php @@ -23,98 +23,101 @@ class TL extends \danog\MadelineProto\Tools $TL_dict['methods'] = array_merge(json_decode(file_get_contents($file), true)['methods'], $TL_dict['methods']); } } else { - $TL_dict = json_decode(file_get_contents($file), true); + $TL_dict = json_decode(file_get_contents($filename), true); } + + \danog\MadelineProto\Logger::log('Translating objects...'); $this->constructors = $TL_dict['constructors']; $this->constructor_id = []; $this->constructor_type = []; foreach ($this->constructors as $elem) { - $z = new TLConstructor($elem); + $z = new \danog\MadelineProto\TL\TLConstructor($elem); $this->constructor_id[$z->id] = $z; $this->constructor_type[$z->predicate] = $z; } + + \danog\MadelineProto\Logger::log('Translating methods...'); $this->methods = $TL_dict['methods']; $this->method_id = []; $this->method_name = []; + $this->method_name_namespaced = []; foreach ($this->methods as $elem) { - $z = new TLMethod($elem); + $z = new \danog\MadelineProto\TL\TLMethod($elem); $this->method_id[$z->id] = $z; $this->method_name[$z->method] = $z; + $this->method_name_namespaced[$z->method] = explode('.', $z->method); } } - public function serialize_obj($type_, $kwargs) + public function get_named_method_args($method, $arguments) { - $bytes_io = ''; - if (isset($this->constructor_type[$type_])) { - $tl_constructor = $this->constructor_type[$type_]; - } else { - throw new Exception('Could not extract type: '.$type_); - } - $bytes_io .= \danog\PHP\Struct::pack('id); - foreach ($tl_constructor->params as $arg) { - $bytes_io .= $this->serialize_param($arg['type'], $arg['subtype'], $kwargs[$arg['name']]); + if (!isset($this->method_name[$method])) { + throw new Exception('Could not extract type: '.$method); } + $tl_method = $this->method_name[$method]; - return $bytes_io; - } - public function get_named_method_args($type_, $kwargs) - { - if (isset($this->method_name[$type_])) { - $tl_method = $this->method_name[$type_]; - } else { - throw new Exception('Could not extract type: '.$type_); - } - - if (count(array_filter(array_keys($kwargs), 'is_string')) == 0) { + if (count(array_filter(array_keys($arguments), 'is_string')) == 0) { $argcount = 0; $newargs = []; - foreach ($tl_method->params as $arg) { - $newargs[$arg['name']] = $kwargs[$argcount++]; + foreach ($tl_method->params as $current_argument) { + $newargs[$current_argument['name']] = $arguments[$argcount++]; } - $kwargs = $newargs; + $arguments = $newargs; } - return $kwargs; + return $arguments; } - public function serialize_method($type_, $kwargs) + public function serialize_obj($object, $arguments) { - $bytes_io = ''; - if (isset($this->method_name[$type_])) { - $tl_method = $this->method_name[$type_]; - } else { - throw new Exception('Could not extract type: '.$type_); + if (!isset($this->constructor_type[$object])) { + throw new Exception('Could not extract type: '.$object); } - $bytes_io .= \danog\PHP\Struct::pack('id); - foreach ($tl_method->params as $arg) { - if (!isset($kwargs[$arg['name']])) { - if ($arg['name'] == 'flags') { - $kwargs['flags'] = 0; + + $tl_method = $this->constructor_type[$object]; + $serialized = \danog\PHP\Struct::pack('id); + + foreach ($tl_constructor->params as $current_argument) { + $serialized .= $this->serialize_param($current_argument['type'], $current_argument['subtype'], $arguments[$current_argument['name']]); + } + + return $serialized; + } + + public function serialize_method($method, $arguments) + { + if (!isset($this->method_name[$method])) { + throw new Exception('Could not extract type: '.$method); + } + + $tl_method = $this->method_name[$method]; + $serialized = \danog\PHP\Struct::pack('id); + + foreach ($tl_method->params as $current_argument) { + if (!isset($arguments[$current_argument['name']])) { + if ($current_argument['name'] == 'flags') { + $arguments['flags'] = 0; } else { - if ($arg['opt']) { + if ($current_argument['opt']) { continue; } - throw new Exception('Missing required parameter ('.$arg['name'].')'); + throw new Exception('Missing required parameter ('.$current_argument['name'].')'); } } - $bytes_io .= $this->serialize_param($arg['type'], $arg['subtype'], $kwargs[$arg['name']]); + $serialized .= $this->serialize_param($current_argument['type'], $current_argument['subtype'], $arguments[$current_argument['name']]); } - return $bytes_io; + return $serialized; } - public function serialize_param($type_, $subtype, $value) + public function serialize_param($type, $subtype, $value) { - switch ($type_) { + switch ($type) { case 'int': if (!is_numeric($value)) { throw new Exception("serialize_param: given value isn't numeric"); } - if (!(strlen(decbin($value)) <= 32)) { - throw new Exception('Given value is too long.'); - } return \danog\PHP\Struct::pack('deserialize($bytes_io, $type_, $subtype); + $this->deserialize($bytes_io, $type, $subtype); return ftell($bytes_io); } @@ -190,12 +186,12 @@ class TL extends \danog\MadelineProto\Tools /** * :type bytes_io: io.BytesIO object. */ - public function deserialize($bytes_io, $type_ = null, $subtype = null) + public function deserialize($bytes_io, $type = null, $subtype = null) { if (!(get_resource_type($bytes_io) == 'file' || get_resource_type($bytes_io) == 'stream')) { - throw new Exception('An invalid bytes_io handle provided.'); + throw new Exception('An invalid bytes_io handle was provided.'); } - switch ($type_) { + switch ($type) { case 'int': $x = \danog\PHP\Struct::unpack('constructor_type[$type_])) { - $tl_elem = $this->constructor_type[$type_]; + if (isset($this->constructor_type[$type])) { + $tl_elem = $this->constructor_type[$type]; } else { - $Idata = fread($bytes_io, 4); - $i = \danog\PHP\Struct::unpack('constructor_id[$i])) { - $tl_elem = $this->constructor_id[$i]; - } else { - throw new Exception('Could not extract type: '.$type_); + $i = \danog\PHP\Struct::unpack('constructor_id[$i])) { + throw new Exception('Could not extract type: '.$type); } + $tl_elem = $this->constructor_id[$i]; } $base_boxed_types = ['Vector t', 'Int', 'Long', 'Double', 'String', 'Int128', 'Int256']; @@ -278,37 +272,40 @@ class TL extends \danog\MadelineProto\Tools public function content_related($method) { - return !in_array($method, [ - 'rpc_result', - 'rpc_error', - 'rpc_drop_answer', - 'rpc_answer_unknown', - 'rpc_answer_dropped_running', - 'rpc_answer_dropped', - 'get_future_salts', - 'future_salt', - 'future_salts', - 'ping', - 'pong', - 'ping_delay_disconnect', - 'destroy_session', - 'destroy_session_ok', - 'destroy_session_none', - 'new_session_created', - 'msg_container', - 'msg_copy', - 'gzip_packed', - 'http_wait', - 'msgs_ack', - 'bad_msg_notification', - 'bad_server_salt', - 'msgs_state_req', - 'msgs_state_info', - 'msgs_all_info', - 'msg_detailed_info', - 'msg_new_detailed_info', - 'msg_resend_req', - 'msg_resend_ans_req', - ]); + return !in_array( + $method, + [ + 'rpc_result', + 'rpc_error', + 'rpc_drop_answer', + 'rpc_answer_unknown', + 'rpc_answer_dropped_running', + 'rpc_answer_dropped', + 'get_future_salts', + 'future_salt', + 'future_salts', + 'ping', + 'pong', + 'ping_delay_disconnect', + 'destroy_session', + 'destroy_session_ok', + 'destroy_session_none', + 'new_session_created', + 'msg_container', + 'msg_copy', + 'gzip_packed', + 'http_wait', + 'msgs_ack', + 'bad_msg_notification', + 'bad_server_salt', + 'msgs_state_req', + 'msgs_state_info', + 'msgs_all_info', + 'msg_detailed_info', + 'msg_new_detailed_info', + 'msg_resend_req', + 'msg_resend_ans_req', + ] + ); } } diff --git a/testing.php b/testing.php index cd46cbbc..4a86611c 100755 --- a/testing.php +++ b/testing.php @@ -2,10 +2,19 @@ auth->sendCode( + [ + 'phone_number' => $number, + 'sms_type' => 5, + 'api_id' => $this->settings['app_info']['api_id'], + 'api_hash' => $this->settings['app_info']['api_hash'], + 'lang_code' => $this->settings['app_info']['lang_code'], + ] + ); + var_dump($sendCode); +}