From 3b8cc9d5ee2c48b1cead0b0382b399d9b60792cc Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 12 Jan 2017 11:04:17 +0100 Subject: [PATCH] Bugfixes and added support for downloading only specified ranges of a file --- README.md | 2 +- docs/index.md | 6 +--- src/danog/MadelineProto/MTProto.php | 4 +-- .../MTProtoTools/AuthKeyHandler.php | 16 +++++----- .../MTProtoTools/MessageHandler.php | 2 +- .../MTProtoTools/UpdateHandler.php | 24 +++++++++++++++ src/danog/MadelineProto/TL/TL.php | 6 ++-- src/danog/MadelineProto/Tools.php | 4 +++ .../MadelineProto/Wrappers/FilesHandler.php | 29 +++++++++++++------ 9 files changed, 64 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index f5766a52..a8d025ac 100644 --- a/README.md +++ b/README.md @@ -341,7 +341,7 @@ The first parameter of these functions must always be a [messageMediaPhoto](http $output_file_name = $MadelineProto->download_to_dir($message_media, '/tmp/dldir'); $custom_output_file_name = $MadelineProto->download_to_file($message_media, '/tmp/dldir/customname.ext'); $stream = fopen('php://output', 'w'); // Stream to browser like with echo -$MadelineProto->download_to_stream($message_media, $stream); +$MadelineProto->download_to_stream($message_media, $stream, $cb, $offset, $endoffset); // offset and endoffset are optional parameters that specify the byte from which to start downloading and the byte where to stop downloading (the latter non-inclusive), if not specified default to 0 and the size of the file ``` diff --git a/docs/index.md b/docs/index.md index 6767c1b9..a8d025ac 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,3 @@ ---- -title: MadelineProto documentation -description: PHP implementation of telegram's MTProto protocol ---- # MadelineProto [![StyleCI](https://styleci.io/repos/61838413/shield)](https://styleci.io/repos/61838413) [![Build Status](https://travis-ci.org/danog/MadelineProto.svg?branch=master)](https://travis-ci.org/danog/MadelineProto) @@ -345,7 +341,7 @@ The first parameter of these functions must always be a [messageMediaPhoto](http $output_file_name = $MadelineProto->download_to_dir($message_media, '/tmp/dldir'); $custom_output_file_name = $MadelineProto->download_to_file($message_media, '/tmp/dldir/customname.ext'); $stream = fopen('php://output', 'w'); // Stream to browser like with echo -$MadelineProto->download_to_stream($message_media, $stream); +$MadelineProto->download_to_stream($message_media, $stream, $cb, $offset, $endoffset); // offset and endoffset are optional parameters that specify the byte from which to start downloading and the byte where to stop downloading (the latter non-inclusive), if not specified default to 0 and the size of the file ``` diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 7dfd8f70..c1af692a 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -235,7 +235,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB { foreach ($this->datacenter->sockets as $id => &$socket) { \danog\MadelineProto\Logger::log('Resetting session id and seq_no in DC '.$id.'...'); - $socket->session_id = \phpseclib\Crypt\Random::string(8); + $socket->session_id = \danog\MadelineProto\Tools::random(8); $socket->seq_no = 0; $socket->incoming_messages = []; $socket->outgoing_messages = []; @@ -275,7 +275,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB public function init_authorization() { if ($this->datacenter->session_id == null) { - $this->datacenter->session_id = \phpseclib\Crypt\Random::string(8); + $this->datacenter->session_id = \danog\MadelineProto\Tools::random(8); } if ($this->datacenter->temp_auth_key == null || $this->datacenter->auth_key == null) { if ($this->datacenter->auth_key == null) { diff --git a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php index 516bfee6..aa35e05b 100644 --- a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php @@ -43,7 +43,7 @@ trait AuthKeyHandler * Vector long $server_public_key_fingerprints : This is a list of public RSA key fingerprints * ] */ - $nonce = \phpseclib\Crypt\Random::string(16); + $nonce = \danog\MadelineProto\Tools::random(16); $ResPQ = $this->method_call('req_pq', [ 'nonce' => $nonce, @@ -103,7 +103,7 @@ trait AuthKeyHandler $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); + $new_nonce = \danog\MadelineProto\Tools::random(32); $data_unserialized = [ 'pq' => $pq_bytes, @@ -121,7 +121,7 @@ trait AuthKeyHandler * Encrypt serialized object */ $sha_digest = sha1($p_q_inner_data, true); - $random_bytes = \phpseclib\Crypt\Random::string(255 - strlen($p_q_inner_data) - strlen($sha_digest)); + $random_bytes = \danog\MadelineProto\Tools::random(255 - strlen($p_q_inner_data) - strlen($sha_digest)); $to_encrypt = $sha_digest.$p_q_inner_data.$random_bytes; $encrypted_data = $this->key->encrypt($to_encrypt); @@ -316,7 +316,7 @@ trait AuthKeyHandler foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) { \danog\MadelineProto\Logger::log('Generating b...'); - $b = new \phpseclib\Math\BigInteger(\phpseclib\Crypt\Random::string(256), 256); + $b = new \phpseclib\Math\BigInteger(\danog\MadelineProto\Tools::random(256), 256); \danog\MadelineProto\Logger::log('Generating g_b...'); $g_b = $g->powMod($b, $dh_prime); @@ -362,7 +362,7 @@ trait AuthKeyHandler * 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)); + $data_with_sha_padded = $data_with_sha.\danog\MadelineProto\Tools::random($this->posmod(-strlen($data_with_sha), 16)); $encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv); \danog\MadelineProto\Logger::log('Executing set_client_DH_params...'); @@ -481,7 +481,7 @@ trait AuthKeyHandler public function bind_temp_auth_key($expires_in) { \danog\MadelineProto\Logger::log('Binding authorization keys...'); - $nonce = \danog\PHP\Struct::unpack('datacenter->temp_auth_key['id'])[0]; $perm_auth_key_id = \danog\PHP\Struct::unpack('datacenter->auth_key['id'])[0]; @@ -499,9 +499,9 @@ trait AuthKeyHandler $message_id = \danog\PHP\Struct::pack('posmod(-strlen($encrypted_data), 16)); + $padding = \danog\MadelineProto\Tools::random($this->posmod(-strlen($encrypted_data), 16)); 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); diff --git a/src/danog/MadelineProto/MTProtoTools/MessageHandler.php b/src/danog/MadelineProto/MTProtoTools/MessageHandler.php index 0f8f4a51..da3962f6 100644 --- a/src/danog/MadelineProto/MTProtoTools/MessageHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/MessageHandler.php @@ -37,7 +37,7 @@ trait MessageHandler $seq_no = $this->generate_seq_no($content_related); $encrypted_data = \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)); + $padding = \danog\MadelineProto\Tools::random($this->posmod(-strlen($encrypted_data), 16)); 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->datacenter->outgoing_messages[$int_message_id]['seq_no'] = $seq_no; diff --git a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php index 6912d276..26812764 100644 --- a/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/UpdateHandler.php @@ -25,12 +25,18 @@ trait UpdateHandler public function get_updates_update_handler($update) { + if (!$this->settings['updates']['handle_updates']) { + return; + } $this->updates[$this->updates_key++] = $update; //\danog\MadelineProto\Logger::log('Stored ', $update); } public function get_updates($params = []) { + if (!$this->settings['updates']['handle_updates']) { + return; + } $time = microtime(true); $this->force_get_updates_difference(); $default_params = ['offset' => 0, 'limit' => null, 'timeout' => 0]; @@ -75,6 +81,9 @@ trait UpdateHandler public function get_channel_difference($channel) { + if (!$this->settings['updates']['handle_updates']) { + return; + } if (!$this->get_channel_state($channel)['sync_loading']) { $this->get_channel_state($channel)['sync_loading'] = true; $this->get_channel_state($channel)['pending_pts_updates'] = []; @@ -133,6 +142,9 @@ trait UpdateHandler public function get_updates_difference() { + if (!$this->settings['updates']['handle_updates']) { + return; + } if (!$this->get_update_state()['sync_loading']) { $this->get_update_state()['sync_loading'] = true; $this->get_update_state()['pending_pts_updates'] = []; @@ -313,6 +325,9 @@ trait UpdateHandler public function pop_pending_seq_update() { + if (!$this->settings['updates']['handle_updates']) { + return; + } $next_seq = $this->get_update_state()['seq'] + 1; if (empty($this->get_update_state()['pending_seq_updates'][$next_seq]['updates'])) { return false; @@ -332,6 +347,9 @@ trait UpdateHandler public function pop_pending_pts_update($channel_id) { + if (!$this->settings['updates']['handle_updates']) { + return; + } if ($channel_id === false) { $cur_state = &$this->get_update_state(); } else { @@ -363,6 +381,9 @@ trait UpdateHandler public function handle_multiple_update($updates, $options = [], $channel = false) { + if (!$this->settings['updates']['handle_updates']) { + return; + } if ($channel === false) { foreach ($updates as $update) { switch ($update['_']) { @@ -383,6 +404,9 @@ trait UpdateHandler public function handle_update_messages($messages, $channel = false) { + if (!$this->settings['updates']['handle_updates']) { + return; + } foreach ($messages as $message) { $this->save_update(['_' => $channel == false ? 'updateNewMessage' : 'updateNewChannelMessage', 'message' => $message, 'pts' => $channel == false ? $this->get_update_state()['pts'] : $this->get_channel_state($channel)['pts'], 'pts_count' => 0]); } diff --git a/src/danog/MadelineProto/TL/TL.php b/src/danog/MadelineProto/TL/TL.php index e692cc76..c46fd6aa 100644 --- a/src/danog/MadelineProto/TL/TL.php +++ b/src/danog/MadelineProto/TL/TL.php @@ -252,16 +252,16 @@ trait TL if ($current_argument['name'] == 'random_id') { switch ($current_argument['type']) { case 'long': - $serialized .= \phpseclib\Crypt\Random::string(8); + $serialized .= \danog\MadelineProto\Tools::random(8); continue 2; case 'int': - $serialized .= \phpseclib\Crypt\Random::string(4); + $serialized .= \danog\MadelineProto\Tools::random(4); continue 2; case 'Vector t': if (isset($arguments['id'])) { $serialized .= \danog\PHP\Struct::pack('constructors->find_by_predicate('vector')['id']); $serialized .= \danog\PHP\Struct::pack(' 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart'; $constructor = $file_size > 10 * 1024 * 1024 ? 'inputFileBig' : 'inputFile'; - $file_id = \danog\PHP\Struct::unpack(' 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret']]; + $res['mime'] = 'image/jpeg'; return $res; @@ -492,7 +494,7 @@ trait FilesHandler } $res['name'] .= '_'.$message_media['document']['id']; $res['size'] = $message_media['document']['size']; - + $res['mime'] = $message_media['document']['mime_type']; $res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => $message_media['document']['version']]; return $res; @@ -506,7 +508,7 @@ trait FilesHandler { $info = $this->get_download_info($message_media); - return $this->download_to_file($message_media, $dir.'/'.$info['name'].$info['ext'], $cb); + return $this->download_to_file($info, $dir.'/'.$info['name'].$info['ext'], $cb); } public function download_to_file($message_media, $file, $cb = null) @@ -515,13 +517,15 @@ trait FilesHandler touch($file); } $stream = fopen($file, 'w'); - fseek($stream, filesize($file)); - $this->download_to_stream($message_media, $stream, $cb); + $info = $this->get_download_info($message_media); + + + $this->download_to_stream($info, $stream, $cb, filesize($file), $info['size']); return $file; } - public function download_to_stream($message_media, $stream, $cb = null) + public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1) { if ($cb === null) { $cb = function ($percent) { @@ -529,10 +533,17 @@ trait FilesHandler }; } $info = $this->get_download_info($message_media); - $offset = ftell($stream); + if ($end === -1) $end = $info['size']; + if (stream_get_meta_data($stream)['seekable']) fseek($stream, $offset); + $size = $end - $offset; $part_size = 512 * 1024; - while (fwrite($stream, $this->API->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset += $part_size, 'limit' => $part_size], null, true)['bytes']) != false) { - $cb(ftell($stream) * 100 / $info['size']); + $percent = 0; + while ($percent < 100) { + $real_part_size = ($offset + $part_size > $end) ? $part_size - (($offset + $part_size) - $end) : $part_size; +\danog\MadelineProto\Logger::log($real_part_size, $offset); + fwrite($stream, $this->API->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], null, true)['bytes']); +\danog\MadelineProto\Logger::log($offset, $size, ftell($stream)); + $cb($percent = ($offset += $real_part_size) * 100 / $size); } return true;