Rewrote request/response module

This commit is contained in:
danogentili 2016-09-14 00:14:50 +02:00
parent 0489abba48
commit 016bb106f6
12 changed files with 216 additions and 180 deletions

View File

@ -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()

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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]);

View File

@ -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;
}

View File

@ -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;
}
}

View 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);
}
}

View File

@ -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');
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -263,11 +263,4 @@ class TL
'msg_resend_ans_req',
]);
}
public function get_opts($method)
{
$opts = ['requires_answer' => !in_array($method, [
'msgs_ack',
])];
}
}