Rewrote request/response module
This commit is contained in:
parent
0489abba48
commit
016bb106f6
@ -21,7 +21,6 @@ class API
|
||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
||||
$this->session = new MTProto($params);
|
||||
$future_salts = $this->get_future_salts(3);
|
||||
$future_salts = $this->get_future_salts(3);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
|
@ -1,77 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016 Daniil Gentili
|
||||
(https://daniil.it)
|
||||
This file is part of MadelineProto.
|
||||
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with the MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
class Crypt
|
||||
{
|
||||
public static function ige_encrypt($message, $key, $iv)
|
||||
{
|
||||
return self::_ige($message, $key, $iv, 'encrypt');
|
||||
}
|
||||
|
||||
public static function ige_decrypt($message, $key, $iv)
|
||||
{
|
||||
return self::_ige($message, $key, $iv, 'decrypt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a key, given an iv, and message
|
||||
* do whatever operation asked in the operation field.
|
||||
* Operation will be checked for: "decrypt" and "encrypt" strings.
|
||||
* Returns the message encrypted/decrypted.
|
||||
* message must be a multiple by 16 bytes (for division in 16 byte blocks)
|
||||
* key must be 32 byte
|
||||
* iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
|
||||
* needed for IGE).
|
||||
*/
|
||||
public static function _ige($message, $key, $iv, $operation = 'decrypt')
|
||||
{
|
||||
if (strlen($key) != 32) {
|
||||
throw new Exception('key must be 32 bytes long (was '.strlen($key).' bytes)');
|
||||
}
|
||||
if (strlen($iv) != 32) {
|
||||
throw new Exception('iv must be 32 bytes long (was '.strlen($iv).' bytes)');
|
||||
}
|
||||
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_ECB);
|
||||
$cipher->setKey($key);
|
||||
$cipher->paddable = false;
|
||||
$blocksize = $cipher->block_size;
|
||||
if ((strlen($message) % $blocksize) != 0) {
|
||||
throw new Exception('message must be a multiple of 16 bytes (try adding '.(16 - (strlen($message) % 16)).' bytes of padding)');
|
||||
}
|
||||
$ivp = substr($iv, 0, $blocksize);
|
||||
$ivp2 = substr($iv, $blocksize);
|
||||
$ciphered = '';
|
||||
foreach (Tools::range(0, strlen($message), $blocksize) as $i) {
|
||||
$indata = substr($message, $i, $blocksize);
|
||||
if ($operation == 'decrypt') {
|
||||
$xored = $indata ^ $ivp2;
|
||||
$decrypt_xored = $cipher->decrypt($xored);
|
||||
$outdata = $decrypt_xored ^ $ivp;
|
||||
$ivp = $indata;
|
||||
$ivp2 = $outdata;
|
||||
} elseif ($operation == 'encrypt') {
|
||||
$xored = $indata ^ $ivp;
|
||||
$encrypt_xored = $cipher->encrypt($xored);
|
||||
$outdata = $encrypt_xored ^ $ivp2;
|
||||
$ivp = $outdata;
|
||||
$ivp2 = $indata;
|
||||
} else {
|
||||
throw new Exception('Crypt: operation must be either \'decrypt\' or \'encrypt\'');
|
||||
}
|
||||
$ciphered .= $outdata;
|
||||
}
|
||||
|
||||
return $ciphered;
|
||||
}
|
||||
}
|
@ -36,7 +36,6 @@ Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+
|
||||
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n
|
||||
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
-----END RSA PUBLIC KEY-----',
|
||||
'message_ids_limit' => 5,
|
||||
],
|
||||
'connection' => [
|
||||
'ip_address' => '149.154.167.50',
|
||||
@ -62,7 +61,12 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
'max_tries' => [
|
||||
'query' => 5,
|
||||
'authorization' => 5,
|
||||
'response' => 5
|
||||
],
|
||||
'msg_array_limit' => [
|
||||
'incoming' => 30,
|
||||
'outgoing' => 30,
|
||||
]
|
||||
];
|
||||
foreach ($default_settings as $key => $param) {
|
||||
if (!isset($settings[$key])) {
|
||||
@ -92,10 +96,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
|
||||
$this->seq_no = 0;
|
||||
$this->timedelta = 0; // time delta
|
||||
$this->incoming_message_ids = [];
|
||||
$this->outgoing_message_ids = [];
|
||||
$this->ack_incoming_message_ids = [];
|
||||
$this->ack_outgoing_message_ids = [];
|
||||
$this->incoming_messages = [];
|
||||
$this->outgoing_messages = [];
|
||||
$this->future_salts = [];
|
||||
|
||||
if ($this->settings['authorization']['temp_auth_key'] == null || $this->settings['authorization']['auth_key'] == null) {
|
||||
|
@ -20,13 +20,10 @@ class AckHandler extends \danog\MadelineProto\Tools
|
||||
public function ack_outgoing_message_id($message_id)
|
||||
{
|
||||
// The server acknowledges that it received my message
|
||||
if (!in_array($message_id, $this->outgoing_message_ids)) {
|
||||
throw new Exception("Couldn't find message id ".$message_id.' in the array of outgoing message ids. Maybe try to increase its size?');
|
||||
}
|
||||
$this->ack_outgoing_message_ids[] = $message_id;
|
||||
if (count($this->ack_outgoing_message_ids) > $this->settings['authorization']['message_ids_limit']) {
|
||||
array_shift($this->ack_outgoing_message_ids);
|
||||
if (!isset($this->outgoing_messages[$message_id])) {
|
||||
throw new Exception("Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?');
|
||||
}
|
||||
$this->outgoing_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
|
||||
public function ack_incoming_message_id($message_id)
|
||||
@ -35,13 +32,10 @@ class AckHandler extends \danog\MadelineProto\Tools
|
||||
return;
|
||||
}
|
||||
// I let the server know that I received its message
|
||||
if (!in_array($message_id, $this->incoming_message_ids)) {
|
||||
if (!isset($this->incoming_messages[$message_id])) {
|
||||
throw new Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
|
||||
}
|
||||
$this->object_call('msgs_ack', ['msg_ids' => [$message_id]]);
|
||||
$this->ack_incoming_message_ids[] = $message_id;
|
||||
if (count($this->ack_incoming_message_ids) > $this->settings['authorization']['message_ids_limit']) {
|
||||
array_shift($this->ack_incoming_message_ids);
|
||||
}
|
||||
$this->incoming_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ class AuthKeyHandler extends AckHandler
|
||||
$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 = \danog\MadelineProto\Crypt::ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
|
||||
$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);
|
||||
@ -187,7 +187,7 @@ class AuthKeyHandler extends AckHandler
|
||||
$data_with_sha_padded = $data_with_sha.\phpseclib\Crypt\Random::string(\danog\MadelineProto\Tools::posmod(-strlen($data_with_sha), 16));
|
||||
|
||||
// encrypt client_DH_inner_data
|
||||
$encrypted_data = \danog\MadelineProto\Crypt::ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
|
||||
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
|
||||
|
||||
// Send set_client_DH_params query
|
||||
$Set_client_DH_params_answer = $this->method_call('set_client_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'encrypted_data' => $encrypted_data]);
|
||||
|
@ -17,41 +17,64 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
class CallHandler extends AuthKeyHandler
|
||||
{
|
||||
public function wait_for_response($res_id) {
|
||||
$response = null;
|
||||
$count = 0;
|
||||
while ($response == null && $count++ < $this->settings['max_tries']['response']) {
|
||||
$server_answer = $this->recv_message();
|
||||
$deserialized = $this->tl->deserialize(\danog\MadelineProto\Tools::fopen_and_write('php://memory', 'rw+b', $server_answer));
|
||||
$tempres = $this->handle_message($deserialized);
|
||||
if ($tempres == null) {
|
||||
if (isset($this->outgoing_messages[$res_id]["response"])) {
|
||||
$response = $this->outgoing_messages[$res_id]["response"];
|
||||
}
|
||||
} else {
|
||||
$response = $tempres;
|
||||
}
|
||||
}
|
||||
switch ($response["_"]) {
|
||||
case 'rpc_error':
|
||||
throw new Exception('Got rpc error '.$response['error_code'].': '.$response['error_message']);
|
||||
break;
|
||||
default:
|
||||
return $response;
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function method_call($method, $args)
|
||||
{
|
||||
$opts = $this->tl->get_opts($method);
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $i) {
|
||||
try {
|
||||
$this->send_message($this->tl->serialize_method($method, $args), $this->tl->content_related($method));
|
||||
if ($opts['requires_answer'] || true) {
|
||||
$server_answer = $this->recv_message();
|
||||
}
|
||||
$int_message_id = $this->send_message($this->tl->serialize_method($method, $args), $this->tl->content_related($method));
|
||||
$this->outgoing_messages[$int_message_id]["method"] = $method;
|
||||
$this->outgoing_messages[$int_message_id]["args"] = $args;
|
||||
$server_answer = $this->wait_for_response($int_message_id);
|
||||
} catch (Exception $e) {
|
||||
$this->log->log('An error occurred while calling method '.$method.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call method...');
|
||||
unset($this->sock);
|
||||
$this->sock = new Connection($this->settings['connection']['ip_address'], $this->settings['connection']['port'], $this->settings['connection']['protocol']);
|
||||
$this->sock = new \danog\MadelineProto\Connection($this->settings['connection']['ip_address'], $this->settings['connection']['port'], $this->settings['connection']['protocol']);
|
||||
continue;
|
||||
}
|
||||
if ($server_answer == null) {
|
||||
throw new Exception('An error occurred while calling method '.$method.'.');
|
||||
}
|
||||
$deserialized = $this->tl->deserialize(\danog\MadelineProto\Tools::fopen_and_write('php://memory', 'rw+b', $server_answer));
|
||||
|
||||
return $this->handle_response($deserialized, $method, $args);
|
||||
return $server_answer;
|
||||
}
|
||||
throw new Exception('An error occurred while calling method '.$method.'.');
|
||||
}
|
||||
|
||||
public function object_call($object, $kwargs)
|
||||
public function object_call($object, $args)
|
||||
{
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $i) {
|
||||
try {
|
||||
$this->send_message($this->tl->serialize_obj($object, $kwargs), $this->tl->content_related($object));
|
||||
$int_message_id = $this->send_message($this->tl->serialize_obj($object, $args), $this->tl->content_related($object));
|
||||
// $server_answer = $this->recv_message();
|
||||
$this->outgoing_messages[$int_message_id]["method"] = $object;
|
||||
$this->outgoing_messages[$int_message_id]["args"] = $args;
|
||||
} catch (Exception $e) {
|
||||
$this->log->log('An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...');
|
||||
unset($this->sock);
|
||||
$this->sock = new Connection($this->settings['connection']['ip_address'], $this->settings['connection']['port'], $this->settings['connection']['protocol']);
|
||||
$this->sock = new \danog\MadelineProto\Connection($this->settings['connection']['ip_address'], $this->settings['connection']['port'], $this->settings['connection']['protocol']);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -26,4 +26,66 @@ class Crypt extends CallHandler
|
||||
|
||||
return [$aes_key, $aes_iv];
|
||||
}
|
||||
|
||||
public function ige_encrypt($message, $key, $iv)
|
||||
{
|
||||
return $this->_ige($message, $key, $iv, 'encrypt');
|
||||
}
|
||||
|
||||
public function ige_decrypt($message, $key, $iv)
|
||||
{
|
||||
return $this->_ige($message, $key, $iv, 'decrypt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a key, given an iv, and message
|
||||
* do whatever operation asked in the operation field.
|
||||
* Operation will be checked for: "decrypt" and "encrypt" strings.
|
||||
* Returns the message encrypted/decrypted.
|
||||
* message must be a multiple by 16 bytes (for division in 16 byte blocks)
|
||||
* key must be 32 byte
|
||||
* iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
|
||||
* needed for IGE).
|
||||
*/
|
||||
public function _ige($message, $key, $iv, $operation = 'decrypt')
|
||||
{
|
||||
if (strlen($key) != 32) {
|
||||
throw new Exception('key must be 32 bytes long (was '.strlen($key).' bytes)');
|
||||
}
|
||||
if (strlen($iv) != 32) {
|
||||
throw new Exception('iv must be 32 bytes long (was '.strlen($iv).' bytes)');
|
||||
}
|
||||
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_ECB);
|
||||
$cipher->setKey($key);
|
||||
$cipher->paddable = false;
|
||||
$blocksize = $cipher->block_size;
|
||||
if ((strlen($message) % $blocksize) != 0) {
|
||||
throw new Exception('message must be a multiple of 16 bytes (try adding '.(16 - (strlen($message) % 16)).' bytes of padding)');
|
||||
}
|
||||
$ivp = substr($iv, 0, $blocksize);
|
||||
$ivp2 = substr($iv, $blocksize);
|
||||
$ciphered = '';
|
||||
foreach (\danog\MadelineProto\Tools::range(0, strlen($message), $blocksize) as $i) {
|
||||
$indata = substr($message, $i, $blocksize);
|
||||
if ($operation == 'decrypt') {
|
||||
$xored = $indata ^ $ivp2;
|
||||
$decrypt_xored = $cipher->decrypt($xored);
|
||||
$outdata = $decrypt_xored ^ $ivp;
|
||||
$ivp = $indata;
|
||||
$ivp2 = $outdata;
|
||||
} elseif ($operation == 'encrypt') {
|
||||
$xored = $indata ^ $ivp;
|
||||
$encrypt_xored = $cipher->encrypt($xored);
|
||||
$outdata = $encrypt_xored ^ $ivp2;
|
||||
$ivp = $outdata;
|
||||
$ivp2 = $indata;
|
||||
} else {
|
||||
throw new Exception('Crypt: operation must be either \'decrypt\' or \'encrypt\'');
|
||||
}
|
||||
$ciphered .= $outdata;
|
||||
}
|
||||
|
||||
return $ciphered;
|
||||
}
|
||||
|
||||
}
|
||||
|
40
src/danog/MadelineProto/MTProtoTools/Exception.php
Normal file
40
src/danog/MadelineProto/MTProtoTools/Exception.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016 Daniil Gentili
|
||||
(https://daniil.it)
|
||||
This file is part of MadelineProto.
|
||||
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with the MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
public function __construct($message, $code = 0, Exception $previous = null)
|
||||
{
|
||||
// some code
|
||||
if (isset($GLOBALS['doingphptests']) && $GLOBALS['doingphptests']) {
|
||||
var_dump($message);
|
||||
}
|
||||
// make sure everything is assigned properly
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* ExceptionErrorHandler.
|
||||
*
|
||||
* Error handler
|
||||
*/
|
||||
public static function ExceptionErrorHandler($errno = 0, $errstr = null, $errfile = null, $errline = null)
|
||||
{
|
||||
// If error is suppressed with @, don't throw an exception
|
||||
if (error_reporting() === 0) {
|
||||
return true; // return true to continue through the others error handlers
|
||||
}
|
||||
throw new self($errstr.' on line '.$errline.' of file '.$errfile, $errno);
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ If not, see <http://www.gnu.org/licenses/>.
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages packing and unpacking of messages.
|
||||
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
||||
*/
|
||||
class MessageHandler extends Crypt
|
||||
{
|
||||
@ -28,17 +28,17 @@ class MessageHandler extends Crypt
|
||||
$this->check_message_id($int_message_id, true);
|
||||
if (($this->settings['authorization']['temp_auth_key']['auth_key'] == null) || ($this->settings['authorization']['temp_auth_key']['server_salt'] == null)) {
|
||||
$message = \danog\MadelineProto\Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.$this->struct->pack('<I', strlen($message_data)).$message_data;
|
||||
$this->last_sent = ['message_id' => $int_message_id];
|
||||
} else {
|
||||
$seq_no = $this->generate_seq_no($content_related);
|
||||
$encrypted_data = $this->settings['authorization']['temp_auth_key']['server_salt'].$this->settings['authorization']['session_id'].$message_id.$this->struct->pack('<II', $seq_no, strlen($message_data)).$message_data;
|
||||
$message_key = substr(sha1($encrypted_data, true), -16);
|
||||
$padding = \phpseclib\Crypt\Random::string(\danog\MadelineProto\Tools::posmod(-strlen($encrypted_data), 16));
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key);
|
||||
$message = $this->settings['authorization']['temp_auth_key']['id'].$message_key.\danog\MadelineProto\Crypt::ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
$this->last_sent = ['message_id' => $int_message_id, 'seq_no' => $seq_no];
|
||||
$message = $this->settings['authorization']['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->sock->send_message($message);
|
||||
return $int_message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,12 +55,11 @@ class MessageHandler extends Crypt
|
||||
list($message_id, $message_length) = $this->struct->unpack('<QI', fread($payload, 12));
|
||||
$this->check_message_id($message_id, false);
|
||||
$message_data = fread($payload, $message_length);
|
||||
$this->last_received = ['message_id' => $message_id];
|
||||
} elseif ($auth_key_id == $this->settings['authorization']['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, 'from server');
|
||||
$decrypted_data = \danog\MadelineProto\Crypt::ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
|
||||
$server_salt = substr($decrypted_data, 0, 8);
|
||||
if ($server_salt != $this->settings['authorization']['temp_auth_key']['server_salt']) {
|
||||
@ -100,7 +99,7 @@ class MessageHandler extends Crypt
|
||||
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
|
||||
throw new Exception('msg_key mismatch');
|
||||
}
|
||||
$this->last_received = ['message_id' => $message_id, 'seq_no' => $seq_no];
|
||||
$this->incoming_messages[$message_id]['seq_no'] = $seq_no;
|
||||
} else {
|
||||
throw new Exception('Got unknown auth_key id');
|
||||
}
|
||||
|
@ -29,22 +29,22 @@ class MsgIdHandler extends MessageHandler
|
||||
if ($new_message_id % 4 != 0) {
|
||||
throw new Exception('Given message id ('.$new_message_id.') is not divisible by 4.');
|
||||
}
|
||||
$this->outgoing_message_ids[] = $new_message_id;
|
||||
if (count($this->outgoing_message_ids) > $this->settings['authorization']['message_ids_limit']) {
|
||||
array_shift($this->outgoing_message_ids);
|
||||
$this->outgoing_messages[$new_message_id] = [];
|
||||
if (count($this->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
|
||||
array_shift($this->outgoing_messages);
|
||||
}
|
||||
} else {
|
||||
if ($new_message_id % 4 != 1 && $new_message_id % 4 != 3) {
|
||||
throw new Exception('message id mod 4 != 1 or 3');
|
||||
}
|
||||
foreach ($this->incoming_message_ids as $message_id) {
|
||||
foreach (array_keys($this->incoming_messages) as $message_id) {
|
||||
if ($new_message_id <= $message_id) {
|
||||
throw new Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$message_id.').');
|
||||
}
|
||||
}
|
||||
$this->incoming_message_ids[] = $new_message_id;
|
||||
if (count($this->incoming_message_ids) > $this->settings['authorization']['message_ids_limit']) {
|
||||
array_shift($this->incoming_message_ids);
|
||||
$this->incoming_messages[$new_message_id] = [];
|
||||
if (count($this->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
|
||||
array_shift($this->incoming_messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,59 +17,46 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
class ResponseHandler extends MsgIdHandler
|
||||
{
|
||||
public function handle_response($response, $name, $args)
|
||||
public function handle_message($response)
|
||||
{
|
||||
switch ($response['_']) {
|
||||
case 'rpc_result':
|
||||
$this->ack_incoming_message_id($this->last_received['message_id']); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received my
|
||||
if ($response['req_msg_id'] != $this->last_sent['message_id']) {
|
||||
throw new Exception('Message id mismatch; req_msg_id ('.$response['req_msg_id'].') != last sent msg id ('.$this->last_sent['message_id'].').');
|
||||
}
|
||||
|
||||
return $this->handle_response($response['result'], $name, $args);
|
||||
break;
|
||||
case 'rpc_error':
|
||||
throw new Exception('Got rpc error '.$response['error_code'].': '.$response['error_message']);
|
||||
break;
|
||||
case 'rpc_answer_unknown':
|
||||
$this->ack_outgoing_message_id($this->last_sent['message_id']); // Acknowledge that the server received my message
|
||||
return $response; // I'm not handling this error
|
||||
break;
|
||||
case 'rpc_answer_dropped_running':
|
||||
$this->ack_incoming_message_id($this->last_received['message_id']); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($this->last_sent['message_id']); // Acknowledge that the server received my message
|
||||
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received the original query (the same one, the response to which we wish to forget)
|
||||
return $response; // I'm not handling this
|
||||
break;
|
||||
case 'rpc_answer_dropped':
|
||||
$this->ack_incoming_message_id($this->last_received['message_id']); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($this->last_sent['message_id']); // Acknowledge that the server received my message
|
||||
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received the original query (the same one, the response to which we wish to forget)
|
||||
return $response; // I'm not handling this
|
||||
break;
|
||||
case 'future_salts':
|
||||
$this->ack_outgoing_message_id($this->last_sent['message_id']); // Acknowledge that the server received my message
|
||||
if ($response['req_msg_id'] != $this->last_sent['message_id']) {
|
||||
throw new Exception('Message id mismatch; req_msg_id ('.$response['req_msg_id'].') != last sent msg id ('.$this->last_sent['message_id'].').');
|
||||
}
|
||||
$this->log->log('Received future salts.');
|
||||
$this->future_salts = $response['salts'];
|
||||
break;
|
||||
case 'pong':
|
||||
$this->ack_incoming_message_id($this->last_received['message_id']); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($this->last_sent['message_id']); // Acknowledge that the server received my message
|
||||
$this->log->log('pong');
|
||||
break;
|
||||
case 'msgs_ack':
|
||||
foreach ($response['msg_ids'] as $msg_id) {
|
||||
$this->ack_outgoing_message_id($msg_id); // Acknowledge that the server received my message
|
||||
}
|
||||
break;
|
||||
|
||||
case 'rpc_result':
|
||||
end($this->incoming_messages);
|
||||
end($this->outgoing_messages);
|
||||
$this->ack_incoming_message_id(key($this->incoming_messages)); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received my request
|
||||
if ($response['req_msg_id'] != key($this->outgoing_messages)) {
|
||||
throw new Exception('Message id mismatch; req_msg_id ('.$response['req_msg_id'].') != last sent msg id ('.key($this->outgoing_messages).').');
|
||||
}
|
||||
return $this->handle_response($response['result'], $response['req_msg_id']);
|
||||
break;
|
||||
|
||||
case 'future_salts':
|
||||
end($this->outgoing_messages);
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received my request
|
||||
if ($response['req_msg_id'] != key($this->outgoing_messages)) {
|
||||
throw new Exception('Message id mismatch; req_msg_id ('.$response['req_msg_id'].') != last sent msg id ('.key($this->outgoing_messages).').');
|
||||
}
|
||||
$this->log->log('Received future salts.');
|
||||
return $this->handle_response($response, $response['req_msg_id']);
|
||||
break;
|
||||
|
||||
case 'pong':
|
||||
foreach ($this->outgoing_messages as $omessage) {
|
||||
if ($omessage["args"]["ping_id"] == $response["ping_id"]) {
|
||||
$this->outgoing_messages[$response["msg_id"]]["response"] = $response;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'new_session_created':
|
||||
$this->ack_incoming_message_id($this->last_received['message_id']); // Acknowledge that I received the server's response
|
||||
end($this->incoming_messages);
|
||||
$this->ack_incoming_message_id(key($this->incoming_messages)); // Acknowledge that I received the server's response
|
||||
$this->log->log('new session created');
|
||||
$this->log->log($response);
|
||||
break;
|
||||
@ -78,17 +65,17 @@ class ResponseHandler extends MsgIdHandler
|
||||
$this->log->log('Received container.');
|
||||
$this->log->log($response['messages']);
|
||||
foreach ($response['messages'] as $message) {
|
||||
$this->last_recieved = ['message_id' => $message['msg_id'], 'seq_no' => $message['seqno']];
|
||||
$responses[] = $this->handle_response($message['body'], $name, $args);
|
||||
$this->incoming_messages[$message['msg_id']] = [ 'seq_no' => $message['seqno']];
|
||||
$responses[] = $this->handle_message($message['body']);
|
||||
}
|
||||
foreach ($responses as $response) {
|
||||
if ($response != null) {
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'msg_copy':
|
||||
$this->handle_response($response['orig_message'], $name, $args);
|
||||
return $this->handle_response($response['orig_message']);
|
||||
break;
|
||||
case 'gzip_packed':
|
||||
return $this->handle_response(gzdecode($response));
|
||||
@ -98,8 +85,22 @@ class ResponseHandler extends MsgIdHandler
|
||||
$this->log->log($response);
|
||||
break;
|
||||
default:
|
||||
$this->ack_incoming_message_id($this->last_received['message_id']); // Acknowledge that I received the server's response
|
||||
return $response;
|
||||
end($this->incoming_messages);
|
||||
$this->ack_incoming_message_id(key($this->incoming_messages)); // Acknowledge that I received the server's response
|
||||
return $this->handle_response($response);
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function handle_response($response, $res_id = null) {
|
||||
if ($res_id == null) {
|
||||
return $response;
|
||||
}
|
||||
switch ($response['_']) {
|
||||
case 'rpc_answer_dropped_running':
|
||||
case 'rpc_answer_dropped':
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received the original query (the same one, the response to which we wish to forget)
|
||||
default:
|
||||
$this->outgoing_messages[$res_id]["response"] = $response;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -263,11 +263,4 @@ class TL
|
||||
'msg_resend_ans_req',
|
||||
]);
|
||||
}
|
||||
|
||||
public function get_opts($method)
|
||||
{
|
||||
$opts = ['requires_answer' => !in_array($method, [
|
||||
'msgs_ack',
|
||||
])];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user