Merge alpha into master (async, huge bugfixes and more) (#546)
* Implement async and lots of bugfixes * Implement more async * Implement async, implement bugfixes for the connection module, for the datacenter module, huge bugfixes, huge perfomance improvements, media DCs for https, advanced selecting, custom var_dump, totally rewritten IOLoop and response mechanism, promises, improvements to the TL parser, custom mb_substr * Apply fixes from StyleCI * Bugfixes * Apply fixes from StyleCI * Bugfixes, implement combined promises * Apply fixes from StyleCI * Support passing method arguments as callable * Starting to write async upload logic * Apply fixes from StyleCI * Start implementing async file upload * Apply fixes from StyleCI * bugfix * Apply fixes from StyleCI * Start rewriting connection module * Add PHP file docblocks for all classes * Start working on new async stream API * Finish writing stream API * More stream API fixes * Apply fixes from StyleCI * Rewrite DataCenter and Connection modules * Clean up stream API documentation * Fixes * Apply fixes from StyleCI * Add referenced parameter to get length of buffer to read in getReadBuffer API * Moved all MessageHandler code in the Connection module, added a PHP version warning in the phar * Start fixing reads * Fix all protocol stream wrappers * Apply fixes from StyleCI * Implement disconnection, and remove end function * Working async RPC * Implement async file upload * Bugfix * Method recall bugfixes * Bugfixes * Trait bugfixes * Fix FIFO buffer * Bugfixes and speedtests * Async logging * Implement websocket streams * Implement loop API, signal API, clean closing and start changing layer * Small magna, websocket and HTTP fixes * Clean up loop API * Improved stack traces, 2FA and async * Login fixes * Added instructions for manual verification * Small fixes * More app info improvements * More app info improvements * TL and 2FA fixes * Update to layer 89 * More bugfixes * Implement broken media reporting * Remove debug comments * PHP 7.2 backwards compatibility * Bugfixes * Async key generation * Some simplifications * Transport fixes * Cleanup * async API * Performance fixes * Fixes to async API * Bugfixes * Implement one-time async loop * Authorization and logging fixes * Update to layer 91 * 7to5 fix * Null coalesce conversion * Implement socks5 proxy * Implement HTTP proxy * Fixes to HTTP proxy * MTProxy and socks5 fixes * Disable PHP 5 conversion * Proxies have higher priority * Avoid error handling in vendor * Override composer dependencies * Fix travis build * Final composer fixes * Proxy logic fixes * Fix get_updates update handling * Do not use parallel file driver if not supported * Refactor loader and implement HTTP fixes * Suppress errors in loader * HTTP and authorization fixes * HTTP fixes * Improved peer management * Use HTTP protocol on altervista * Small bugfixes * Minor fixes * Docufix * Docufix * Legacy fixes * Fix message queue * Avoid updating if using MTProxy * Improve logs and examples * Trim final newlines while converting parse mode * Reimplement noResponse flag * Async combined event handler and APIFactory fixes * Actually return config * Case-insensitive methods * Bugfix * Apply fixes from StyleCI (#545) * MTProxy fixes * PHP 5 warning * Improved PHP 5 warning * Use <br> along with newlines in web logs * Update docs
This commit is contained in:
parent
367b0536b9
commit
df24fa4611
1
.gitignore
vendored
1
.gitignore
vendored
@ -110,3 +110,4 @@ phar5
|
||||
madeline.phar
|
||||
madeline.phar.version
|
||||
big
|
||||
*.phar
|
||||
|
10
bot.php
10
bot.php
@ -32,13 +32,18 @@ class EventHandler extends \danog\MadelineProto\EventHandler
|
||||
if (isset($update['message']['out']) && $update['message']['out']) {
|
||||
return;
|
||||
}
|
||||
if (isset($update['message']['media'])) {
|
||||
yield $this->messages->sendMedia(['peer' => $update, 'message' => $update['message']['message'], 'media' => $update]);
|
||||
}
|
||||
|
||||
$res = json_encode($update, JSON_PRETTY_PRINT);
|
||||
if ($res == '') {
|
||||
$res = var_export($update, true);
|
||||
}
|
||||
yield $this->sleep_async(3);
|
||||
|
||||
try {
|
||||
$this->messages->sendMessage(['peer' => $update, 'message' => $res, 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'entities' => [['_' => 'messageEntityPre', 'offset' => 0, 'length' => strlen($res), 'language' => 'json']]]);
|
||||
yield $this->messages->sendMessage(['peer' => $update, 'message' => "<code>$res</code>\n\nDopo 3 secondi, in modo asincrono", 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'parse_mode' => 'HTML']); //'entities' => [['_' => 'messageEntityPre', 'offset' => 0, 'length' => strlen($res), 'language' => 'json']]]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
\danog\MadelineProto\Logger::log((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
@ -48,8 +53,9 @@ class EventHandler extends \danog\MadelineProto\EventHandler
|
||||
}
|
||||
}
|
||||
|
||||
$MadelineProto = new \danog\MadelineProto\API('bot.madeline');
|
||||
$MadelineProto = new \danog\MadelineProto\API('bot.madeline', ['logger' => ['logger_level' => 5]]);
|
||||
|
||||
$MadelineProto->start();
|
||||
$MadelineProto->async(true);
|
||||
$MadelineProto->setEventHandler('\EventHandler');
|
||||
$MadelineProto->loop();
|
||||
|
@ -43,9 +43,9 @@ $docs = [
|
||||
'readme' => false,
|
||||
],
|
||||
[
|
||||
'tl_schema' => ['telegram' => __DIR__.'/src/danog/MadelineProto/TL_telegram_v82.tl', 'calls' => __DIR__.'/src/danog/MadelineProto/TL_calls.tl', 'secret' => __DIR__.'/src/danog/MadelineProto/TL_secret.tl', 'td' => __DIR__.'/src/danog/MadelineProto/TL_td.tl'],
|
||||
'title' => 'MadelineProto API documentation (layer 82)',
|
||||
'description' => 'MadelineProto API documentation (layer 82)',
|
||||
'tl_schema' => ['telegram' => __DIR__.'/src/danog/MadelineProto/TL_telegram_v91.tl', 'calls' => __DIR__.'/src/danog/MadelineProto/TL_calls.tl', 'secret' => __DIR__.'/src/danog/MadelineProto/TL_secret.tl', 'td' => __DIR__.'/src/danog/MadelineProto/TL_td.tl'],
|
||||
'title' => 'MadelineProto API documentation (layer 91)',
|
||||
'description' => 'MadelineProto API documentation (layer 91)',
|
||||
'output_dir' => __DIR__.'/docs/docs/API_docs',
|
||||
'readme' => false,
|
||||
],
|
||||
|
72
combined_bot.php
Executable file
72
combined_bot.php
Executable file
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
set_include_path(get_include_path().':'.realpath(dirname(__FILE__).'/MadelineProto/'));
|
||||
|
||||
/*
|
||||
* Various ways to load MadelineProto
|
||||
*/
|
||||
if (!file_exists(__DIR__.'/vendor/autoload.php')) {
|
||||
echo 'You did not run composer update, using madeline.php'.PHP_EOL;
|
||||
if (!file_exists('madeline.php')) {
|
||||
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
|
||||
}
|
||||
include 'madeline.php';
|
||||
} else {
|
||||
require_once 'vendor/autoload.php';
|
||||
}
|
||||
|
||||
class EventHandler extends \danog\MadelineProto\CombinedEventHandler
|
||||
{
|
||||
public function onAny($update, $path)
|
||||
{
|
||||
if (isset($update['message']['out']) && $update['message']['out']) {
|
||||
return;
|
||||
}
|
||||
$MadelineProto = $this->{$path};
|
||||
|
||||
if (isset($update['message']['media'])) {
|
||||
yield $MadelineProto->messages->sendMedia(['peer' => $update, 'message' => $update['message']['message'], 'media' => $update]);
|
||||
}
|
||||
|
||||
$res = json_encode($update, JSON_PRETTY_PRINT);
|
||||
if ($res == '') {
|
||||
$res = var_export($update, true);
|
||||
}
|
||||
yield $MadelineProto->sleep_async(3);
|
||||
|
||||
try {
|
||||
yield $MadelineProto->messages->sendMessage(['peer' => $update, 'message' => "<code>$res</code>\n\nDopo 3 secondi, in modo asincrono", 'reply_to_msg_id' => isset($update['message']['id']) ? $update['message']['id'] : null, 'parse_mode' => 'HTML']); //'entities' => [['_' => 'messageEntityPre', 'offset' => 0, 'length' => strlen($res), 'language' => 'json']]]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
\danog\MadelineProto\Logger::log((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
\danog\MadelineProto\Logger::log((string) $e, \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
//$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$settings = ['logger' => ['logger_level' => 5]];
|
||||
$CombinedMadelineProto = new \danog\MadelineProto\CombinedAPI('combined_session.madeline', ['bot.madeline' => $settings, 'user.madeline' => $settings]);
|
||||
|
||||
\danog\MadelineProto\Logger::log('Bot login', \danog\MadelineProto\Logger::WARNING);
|
||||
$CombinedMadelineProto->instances['bot.madeline']->start();
|
||||
|
||||
\danog\MadelineProto\Logger::log('Userbot login');
|
||||
$CombinedMadelineProto->instances['user.madeline']->start();
|
||||
|
||||
$CombinedMadelineProto->setEventHandler('\EventHandler');
|
||||
$CombinedMadelineProto->loop();
|
||||
|
||||
$CombinedMadelineProto->async(true);
|
||||
$CombinedMadelineProto->setEventHandler('\EventHandler');
|
||||
$CombinedMadelineProto->loop();
|
@ -19,13 +19,21 @@
|
||||
"ext-curl": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-json": "*",
|
||||
"ext-xml": "*"
|
||||
"ext-xml": "*",
|
||||
"amphp/amp": "^2.0",
|
||||
"amphp/socket": "^0.10.11",
|
||||
"amphp/log": "^1.0",
|
||||
"amphp/parser": "^1.0",
|
||||
"amphp/dns": "dev-master#861cc857b1ba6e02e8a7439c30403682785fce96 as 0.9.9",
|
||||
"amphp/file": "dev-master#5a69fca406ac5fd220de0aa68c887bc8046eb93c as 0.3.3",
|
||||
"amphp/uri": "dev-master#f3195b163275383909ded7770a11d8eb865cbc86 as 0.1.3",
|
||||
"amphp/websocket-client": "dev-master"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpdocumentor/reflection-docblock": "^3.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libtgvoip": "Install the php-libtgvoip extension to make phone calls (https:/github.com/danog/php-libtgvoip)",
|
||||
"ext-libtgvoip": "Install the php-libtgvoip extension to make phone calls (https://github.com/danog/php-libtgvoip)",
|
||||
"ext-sockets": "Install the socket extension to speed up MadelineProto"
|
||||
},
|
||||
"authors": [
|
||||
|
2
docs
2
docs
@ -1 +1 @@
|
||||
Subproject commit 20e50a34f0b4aa2efd6b36fed66504999a404a57
|
||||
Subproject commit 0fcff8e5a31af300511949c4dbafc7be6c0d4dd7
|
21
magna.php
21
magna.php
@ -21,8 +21,9 @@ if (!file_exists(__DIR__.'/vendor/autoload.php')) {
|
||||
} else {
|
||||
require_once 'vendor/autoload.php';
|
||||
}
|
||||
if (file_exists('web_data.php')) {
|
||||
require_once 'web_data.php';
|
||||
|
||||
if (!file_exists('songs.php')) {
|
||||
copy('https://github.com/danog/MadelineProto/raw/master/songs.php', 'songs.php');
|
||||
}
|
||||
|
||||
echo 'Deserializing MadelineProto from session.madeline...'.PHP_EOL;
|
||||
@ -88,8 +89,10 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p
|
||||
if (!isset($this->calls[$from_id]) && $message === '/call') {
|
||||
$call = $this->request_call($from_id);
|
||||
$this->configureCall($call);
|
||||
$this->calls[$call->getOtherID()] = $call;
|
||||
$this->times[$call->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']];
|
||||
if ($call->getCallState() !== \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
|
||||
$this->calls[$call->getOtherID()] = $call;
|
||||
$this->times[$call->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']];
|
||||
}
|
||||
}
|
||||
if (strpos($message, '/program') === 0) {
|
||||
$time = strtotime(str_replace('/program ', '', $message));
|
||||
@ -198,8 +201,10 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p
|
||||
try {
|
||||
$call = $this->request_call($user);
|
||||
$this->configureCall($call);
|
||||
$this->calls[$call->getOtherID()] = $call;
|
||||
$this->times[$call->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']];
|
||||
if ($call->getCallState() !== \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
|
||||
$this->calls[$call->getOtherID()] = $call;
|
||||
$this->times[$call->getOtherID()] = [time(), $this->messages->sendMessage(['peer' => $call->getOtherID(), 'message' => 'Total running calls: '.count($this->calls).PHP_EOL.PHP_EOL.$call->getDebugString()])['id']];
|
||||
}
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
try {
|
||||
if ($e->rpc === 'USER_PRIVACY_RESTRICTED') {
|
||||
@ -262,6 +267,8 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p
|
||||
],
|
||||
]);
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
echo $e;
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
echo $e;
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
@ -282,7 +289,7 @@ Propic art by @magnaluna on [deviantart](https://magnaluna.deviantart.com).", 'p
|
||||
}
|
||||
}
|
||||
}
|
||||
$MadelineProto = new \danog\MadelineProto\API('session.madeline', ['secret_chats' => ['accept_chats' => false], 'logger' => ['logger' => 2, 'logger_param' => getcwd().'/MadelineProto.log']]);
|
||||
$MadelineProto = new \danog\MadelineProto\API('session.madeline', ['secret_chats' => ['accept_chats' => false], 'logger' => ['logger' => 3, 'logger_param' => getcwd().'/MadelineProto.log']]);
|
||||
$MadelineProto->start();
|
||||
|
||||
if (!isset($MadelineProto->programmed_call)) {
|
||||
|
@ -25,9 +25,9 @@ $p->addFromString('.git/refs/heads/master', $argv[3]);
|
||||
|
||||
$p->setStub('<?php
|
||||
$backtrace = debug_backtrace();
|
||||
if (in_array(basename($backtrace[0]["file"]), ["madeline.php", "phar.php"])) {
|
||||
if (in_array(basename($backtrace[0]["file"]), ["madeline.php", "phar.php"]) && isset($backtrace[1]["file"]) && !defined("PHAR_DEBUG")) {
|
||||
chdir(dirname($backtrace[1]["file"]));
|
||||
if (!isset($phar_debug) && ($contents = file_get_contents("https://phar.madelineproto.xyz/phar.php?v=new"))) {
|
||||
if ($contents = file_get_contents("https://phar.madelineproto.xyz/phar.php?v=new")) {
|
||||
file_put_contents($backtrace[0]["file"], $contents);
|
||||
}
|
||||
}
|
||||
|
75
phar.php
75
phar.php
@ -1,24 +1,69 @@
|
||||
<?php
|
||||
|
||||
if (!file_exists('madeline.phar') || !file_exists('madeline.phar.version') || (file_get_contents('madeline.phar.version') !== file_get_contents('https://phar.madelineproto.xyz/release?v=new') && file_get_contents('https://phar.madelineproto.xyz/release?v=new'))) {
|
||||
$release = file_get_contents('https://phar.madelineproto.xyz/release?v=new');
|
||||
$phar = file_get_contents('https://phar.madelineproto.xyz/madeline.phar?v=new');
|
||||
if ($release && $phar) {
|
||||
file_put_contents('madeline.phar', $phar);
|
||||
file_put_contents('madeline.phar.version', $release);
|
||||
if (PHP_MAJOR_VERSION === 5) {
|
||||
if (PHP_MINOR_VERSION < 6) {
|
||||
throw new \Exception('MadelineProto requires at least PHP 5.6 to run');
|
||||
}
|
||||
unset($release);
|
||||
unset($phar);
|
||||
$newline = PHP_EOL;
|
||||
if (php_sapi_name() !== 'cli') $newline = '<br>'.$newline;
|
||||
echo "**********************************************************************$newline";
|
||||
echo "**********************************************************************$newline$newline";
|
||||
echo "YOU ARE USING AN OLD AND BUGGED VERSION OF PHP, PLEASE UPDATE TO PHP 7$newline";
|
||||
echo "PHP 5 USERS WILL NOT RECEIVE MADELINEPROTO UPDATES AND BUGFIXES$newline$newline";
|
||||
echo "RECOMMENDED VERSION: PHP 7.3$newline";
|
||||
echo "ALL PHP 7 VERSIONS (7.0, 7.1, 7.2, 7.3) ARE SUPPORTED$newline$newline";
|
||||
echo "**********************************************************************$newline";
|
||||
echo "**********************************************************************$newline";
|
||||
unset($newline);
|
||||
}
|
||||
$file = debug_backtrace(0, 1)[0]['file'];
|
||||
if (file_exists($file)) {
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
// Should've added the self-update code in mtproxyd right away, but it's too late now
|
||||
if (strpos($contents, 'new \danog\MadelineProto\Server') && in_array($contents, [file_get_contents('https://github.com/danog/MadelineProtoPhar/raw/2270bd9a94d168a5e6731ffd7e61821ea244beff/mtproxyd'), file_get_contents('https://github.com/danog/MadelineProtoPhar/raw/7cabb718ec3ccb79e3c8e3d34f5bccbe3f63b0fd/mtproxyd')]) && ($mtproxyd = file_get_contents('https://phar.madelineproto.xyz/mtproxyd?v=new'))) {
|
||||
file_put_contents($file, $mtproxyd);
|
||||
unset($mtproxyd);
|
||||
function ___install_madeline()
|
||||
{
|
||||
if (count(debug_backtrace(0)) === 1) {
|
||||
die('You must include this file in another PHP script'.PHP_EOL);
|
||||
}
|
||||
|
||||
// MTProxy update
|
||||
$file = debug_backtrace(0, 1)[0]['file'];
|
||||
if (file_exists($file)) {
|
||||
$contents = file_get_contents($file);
|
||||
|
||||
if (strpos($contents, 'new \danog\MadelineProto\Server') && in_array($contents, [file_get_contents('https://github.com/danog/MadelineProtoPhar/raw/2270bd9a94d168a5e6731ffd7e61821ea244beff/mtproxyd'), file_get_contents('https://github.com/danog/MadelineProtoPhar/raw/7cabb718ec3ccb79e3c8e3d34f5bccbe3f63b0fd/mtproxyd')]) && ($mtproxyd = file_get_contents('https://phar.madelineproto.xyz/mtproxyd?v=new'))) {
|
||||
file_put_contents($file, $mtproxyd);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// MadelineProto update
|
||||
$release_template = 'https://phar.madelineproto.xyz/release%s?v=new';
|
||||
$phar_template = 'https://phar.madelineproto.xyz/madeline%s.phar?v=new';
|
||||
|
||||
$release_branch = defined('MADELINE_BRANCH') ? '-'.MADELINE_BRANCH : '';
|
||||
$release_default = '';
|
||||
|
||||
if (PHP_MAJOR_VERSION === 5) {
|
||||
$release_branch = '5'.$release_branch;
|
||||
$release_default = '5';
|
||||
}
|
||||
|
||||
if (!($release = @file_get_contents(sprintf($release_template, $release_branch)))) {
|
||||
if (!($release = @file_get_contents(sprintf($release_template, $release_default)))) {
|
||||
return;
|
||||
}
|
||||
$release_branch = $release_default;
|
||||
}
|
||||
|
||||
if (!file_exists('madeline.phar') || !file_exists('madeline.phar.version') || file_get_contents('madeline.phar.version') !== $release) {
|
||||
$phar = file_get_contents(sprintf($phar_template, $release_branch));
|
||||
|
||||
if ($phar) {
|
||||
file_put_contents('madeline.phar', $phar);
|
||||
file_put_contents('madeline.phar.version', $release);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
___install_madeline();
|
||||
|
||||
require 'madeline.phar';
|
||||
|
@ -1,6 +1,9 @@
|
||||
<?php
|
||||
|
||||
$songs = glob('*raw');
|
||||
if (!$songs) {
|
||||
die('No songs defined! Convert some songs as described in https://docs.madelineproto.xyz/docs/CALLS.html#playing-mp3-files');
|
||||
}
|
||||
$songs_length = count($songs);
|
||||
|
||||
for ($x = 0; $x < $songs_length; $x++) {
|
||||
|
@ -1,9 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* BigInteger placeholder for deserialization.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace phpseclib\Math;
|
||||
|
||||
if (PHP_MAJOR_VERSION < 7 && !(class_exists('\\Phar') && \Phar::running())) {
|
||||
throw new \Exception('MadelineProto requires php 7 to run');
|
||||
throw new \Exception('MadelineProto requires php 7 to run natively, use phar.madelineproto.xyz to run on PHP 5.6');
|
||||
}
|
||||
if (defined('HHVM_VERSION')) {
|
||||
$engines = [['PHP64', ['OpenSSL']], ['BCMath', ['OpenSSL']], ['PHP32', ['OpenSSL']]];
|
||||
|
@ -1,5 +1,20 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CustomHTTPProxy module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
class CustomHTTPProxy implements \danog\MadelineProto\Proxy
|
||||
{
|
||||
private $sock;
|
||||
|
@ -1,15 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2017 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* HttpProxy module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
class HttpProxy implements \danog\MadelineProto\Proxy
|
||||
{
|
||||
private $domain;
|
||||
|
@ -1,14 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Socket module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
class FSocket
|
||||
{
|
||||
private $sock;
|
||||
|
@ -1,15 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2017 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SocksProxy module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
class SocksProxy implements \danog\MadelineProto\Proxy
|
||||
{
|
||||
private $domain;
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* API module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -103,6 +109,19 @@ class API extends APIFactory
|
||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['madelineproto_ready'], Logger::NOTICE);
|
||||
}
|
||||
|
||||
public function async($async)
|
||||
{
|
||||
$this->async = $async;
|
||||
foreach ($this->API->get_methods_namespaced() as $pair) {
|
||||
$namespace = key($pair);
|
||||
$this->{$namespace}->async = $async;
|
||||
}
|
||||
|
||||
if ($this->API->event_handler && class_exists($this->API->event_handler) && is_subclass_of($this->API->event_handler, '\danog\MadelineProto\EventHandler')) {
|
||||
$this->API->setEventHandler($this->API->event_handler);
|
||||
}
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->APIFactory();
|
||||
@ -161,13 +180,61 @@ class API extends APIFactory
|
||||
unset($this->API->storage[$name]);
|
||||
}
|
||||
|
||||
private function from_camel_case($input)
|
||||
{
|
||||
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
|
||||
$ret = $matches[0];
|
||||
foreach ($ret as &$match) {
|
||||
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
|
||||
}
|
||||
|
||||
return implode('_', $ret);
|
||||
}
|
||||
|
||||
public function APIFactory()
|
||||
{
|
||||
if ($this->API) {
|
||||
foreach ($this->API->get_method_namespaces() as $namespace) {
|
||||
$this->{$namespace} = new APIFactory($namespace, $this->API);
|
||||
}
|
||||
$methods = get_class_methods($this->API);
|
||||
foreach ($methods as $key => $method) {
|
||||
if ($method == 'method_call_async_read') {
|
||||
unset($methods[array_search('method_call', $methods)]);
|
||||
} elseif (stripos($method, 'async') !== false) {
|
||||
if (strpos($method, '_async') !== false) {
|
||||
unset($methods[array_search(str_ireplace('_async', '', $method), $methods)]);
|
||||
} else {
|
||||
unset($methods[array_search(str_ireplace('async', '', $method), $methods)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->methods = [];
|
||||
foreach ($methods as $method) {
|
||||
$actual_method = $method;
|
||||
|
||||
if ($method == 'method_call_async_read') {
|
||||
$method = 'method_call';
|
||||
} elseif (stripos($method, 'async') !== false) {
|
||||
if (strpos($method, '_async') !== false) {
|
||||
$method = str_ireplace('_async', '', $method);
|
||||
} else {
|
||||
$method = str_ireplace('async', '', $method);
|
||||
}
|
||||
}
|
||||
|
||||
$this->methods[strtolower($method)] = [$this->API, $actual_method];
|
||||
if (strpos($method, '_') !== false) {
|
||||
$this->methods[strtolower(str_replace('_', '', $method))] = [$this->API, $actual_method];
|
||||
} else {
|
||||
$this->methods[strtolower($this->from_camel_case($method))] = [$this->API, $actual_method];
|
||||
}
|
||||
}
|
||||
|
||||
$this->API->wrapper = $this;
|
||||
if ($this->API->event_handler && class_exists($this->API->event_handler) && is_subclass_of($this->API->event_handler, '\danog\MadelineProto\EventHandler')) {
|
||||
$this->API->setEventHandler($this->API->event_handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,18 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* APIFactory module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Promise;
|
||||
|
||||
class APIFactory
|
||||
{
|
||||
/**
|
||||
@ -105,10 +113,14 @@ class APIFactory
|
||||
* @var auth
|
||||
*/
|
||||
public $auth;
|
||||
|
||||
use Tools;
|
||||
public $namespace = '';
|
||||
public $API;
|
||||
public $lua = false;
|
||||
public $async = false;
|
||||
|
||||
protected $methods = [];
|
||||
|
||||
public function __construct($namespace, $API)
|
||||
{
|
||||
@ -132,9 +144,7 @@ class APIFactory
|
||||
$this->API->__construct($this->API->settings);
|
||||
}
|
||||
$this->API->get_config([], ['datacenter' => $this->API->datacenter->curdc]);
|
||||
$aargs = isset($arguments[1]) && is_array($arguments[1]) ? $arguments[1] : [];
|
||||
$aargs['datacenter'] = $this->API->datacenter->curdc;
|
||||
$aargs['apifactory'] = true;
|
||||
|
||||
if (isset($this->session) && !is_null($this->session) && time() - $this->serialized > $this->API->settings['serialization']['serialization_interval']) {
|
||||
Logger::log("Didn't serialize in a while, doing that now...");
|
||||
$this->serialize($this->session);
|
||||
@ -142,13 +152,15 @@ class APIFactory
|
||||
if ($name !== 'accept_tos' && $name !== 'decline_tos') {
|
||||
$this->API->check_tos();
|
||||
}
|
||||
$lower_name = strtolower($name);
|
||||
|
||||
if ($this->lua === false) {
|
||||
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
|
||||
return $this->namespace !== '' || !isset($this->methods[$lower_name]) ? $this->__mtproto_call($this->namespace.$name, $arguments) : $this->__api_call($lower_name, $arguments);
|
||||
}
|
||||
|
||||
try {
|
||||
$deserialized = method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
|
||||
$deserialized = $this->namespace !== '' || !isset($this->methods[$lower_name]) ? $this->__mtproto_call($this->namespace.$name, $arguments) : $this->__api_call($lower_name, $arguments);
|
||||
|
||||
Lua::convert_objects($deserialized);
|
||||
|
||||
return $deserialized;
|
||||
@ -168,4 +180,40 @@ class APIFactory
|
||||
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
public function __api_call($name, $arguments)
|
||||
{
|
||||
$result = $this->methods[$name](...$arguments);
|
||||
if (is_object($result) && ($result instanceof \Generator || $result instanceof Promise)) {
|
||||
$async = isset(end($arguments)['async']) ? end($arguments)['async'] : $this->async;
|
||||
if ($async && ($name !== 'loop' || isset(end($arguments)['async']))) {
|
||||
return $result;
|
||||
} else {
|
||||
return $this->wait($result);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __mtproto_call($name, $arguments)
|
||||
{
|
||||
if (array_key_exists($name, \danog\MadelineProto\MTProto::DISALLOWED_METHODS)) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$name], 0, null, 'MadelineProto', 1);
|
||||
}
|
||||
|
||||
$aargs = isset($arguments[1]) && is_array($arguments[1]) ? $arguments[1] : [];
|
||||
$aargs['datacenter'] = $this->API->datacenter->curdc;
|
||||
$aargs['apifactory'] = true;
|
||||
$args = isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [];
|
||||
|
||||
$async = isset(end($arguments)['async']) ? end($arguments)['async'] : $this->async;
|
||||
$res = $this->API->method_call_async_read($name, $args, $aargs);
|
||||
|
||||
if ($async) {
|
||||
return $res;
|
||||
} else {
|
||||
return $this->wait($res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Absolute module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* AnnotationsBuilder module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
69
src/danog/MadelineProto/Async/AsyncParameters.php
Normal file
69
src/danog/MadelineProto/Async/AsyncParameters.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Async parameters class.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Async;
|
||||
|
||||
use Amp\Success;
|
||||
|
||||
/**
|
||||
* Async parameters class.
|
||||
*
|
||||
* Manages asynchronous generation of method parameters
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class AsyncParameters extends Parameters
|
||||
{
|
||||
private $callable;
|
||||
private $refetchable = true;
|
||||
|
||||
public function __construct(callable $callable, bool $refetchable = true)
|
||||
{
|
||||
$this->callable = $callable;
|
||||
$this->refetchable = $refetchable;
|
||||
}
|
||||
|
||||
public function setRefetchable(bool $refetchable)
|
||||
{
|
||||
$this->refetchable = $refetchable;
|
||||
}
|
||||
|
||||
public function setCallable(callable $callable)
|
||||
{
|
||||
$this->callable = $callable;
|
||||
}
|
||||
|
||||
public function isRefetchable(): bool
|
||||
{
|
||||
return $this->refetchable;
|
||||
}
|
||||
|
||||
public function getParameters(): \Generator
|
||||
{
|
||||
$callable = $this->callable;
|
||||
$params = $callable();
|
||||
|
||||
if ($params instanceof \Generator) {
|
||||
$params = yield coroutine($params);
|
||||
} else {
|
||||
$params = yield new Success($params);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
79
src/danog/MadelineProto/Async/Parameters.php
Normal file
79
src/danog/MadelineProto/Async/Parameters.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/**
|
||||
* Parameters module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Async;
|
||||
|
||||
use Amp\Promise;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Parameters module.
|
||||
*
|
||||
* Manages asynchronous generation of method parameters
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
abstract class Parameters
|
||||
{
|
||||
private $fetched = false;
|
||||
private $params = [];
|
||||
|
||||
/**
|
||||
* Fetch parameters asynchronously.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function fetchParameters(): Promise
|
||||
{
|
||||
return call([$this, 'fetchParametersAsync']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch parameters asynchronously.
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function fetchParametersAsync(): \Generator
|
||||
{
|
||||
$refetchable = $this->isRefetchable();
|
||||
if ($this->fetched && !$refetchable) {
|
||||
return $this->params;
|
||||
}
|
||||
$params = yield call([$this, 'getParameters']);
|
||||
|
||||
if (!$refetchable) {
|
||||
$this->params = $params;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parameters can be fetched more than once.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isRefetchable(): bool;
|
||||
|
||||
/**
|
||||
* Gets the parameters asynchronously.
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
abstract public function getParameters(): \Generator;
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Bug74586Exception module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,27 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* CombinedAPI module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Loop;
|
||||
use function Amp\Promise\all;
|
||||
|
||||
class CombinedAPI
|
||||
{
|
||||
use \danog\Serializable;
|
||||
use Tools;
|
||||
public $session;
|
||||
public $instance_paths = [];
|
||||
public $instances = [];
|
||||
public $timeout = 5;
|
||||
public $serialization_interval = 30;
|
||||
public $serialized = 0;
|
||||
protected $async;
|
||||
|
||||
public function __magic_construct($session, $paths = [])
|
||||
{
|
||||
@ -153,6 +164,7 @@ class CombinedAPI
|
||||
|
||||
public $event_handler;
|
||||
private $event_handler_instance;
|
||||
private $event_handler_methods = [];
|
||||
|
||||
public function setEventHandler($event_handler)
|
||||
{
|
||||
@ -168,23 +180,41 @@ class CombinedAPI
|
||||
} elseif ($this->event_handler_instance) {
|
||||
$this->event_handler_instance->__construct($this);
|
||||
}
|
||||
if (method_exists($this->event_handler_instance, 'onLoop')) {
|
||||
$this->loop_callback = [$this->event_handler_instance, 'onLoop'];
|
||||
$this->event_handler_methods = [];
|
||||
|
||||
foreach (\get_class_methods($this->event_handler) as $method) {
|
||||
if ($method === 'onLoop') {
|
||||
$this->loop_callback = [$this->event_handler_instance, 'onLoop'];
|
||||
} elseif ($method === 'onAny') {
|
||||
foreach (end($this->instances)->API->constructors->by_id as $id => $constructor) {
|
||||
if ($constructor['type'] === 'Update' && !isset($this->event_handler_methods[$constructor['predicate']])) {
|
||||
$this->event_handler_methods[$constructor['predicate']] = [$this->event_handler_instance, 'onAny'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$method_name = lcfirst(substr($method, 2));
|
||||
$this->event_handler_methods[$method_name] = [$this->event_handler_instance, $method];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function event_update_handler($update, $instance)
|
||||
{
|
||||
$method_name = 'on'.ucfirst($update['_']);
|
||||
if (method_exists($this->event_handler_instance, $method_name)) {
|
||||
$this->event_handler_instance->$method_name($update, $instance);
|
||||
} elseif (method_exists($this->event_handler_instance, 'onAny')) {
|
||||
$this->event_handler_instance->onAny($update, $instance);
|
||||
if (isset($this->event_handler_methods[$update['_']])) {
|
||||
return $this->event_handler_methods[$update['_']]($update, $instance);
|
||||
}
|
||||
}
|
||||
|
||||
private $loop_callback;
|
||||
|
||||
public function async($async)
|
||||
{
|
||||
$this->async = $async;
|
||||
foreach ($this->instances as $instance) {
|
||||
$instance->async($async);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLoopCallback($callback)
|
||||
{
|
||||
$this->loop_callback = $callback;
|
||||
@ -207,89 +237,31 @@ class CombinedAPI
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$loops = [];
|
||||
foreach ($this->instances as $path => $instance) {
|
||||
if ($instance->API->authorized !== MTProto::LOGGED_IN) {
|
||||
continue;
|
||||
}
|
||||
if (!$instance->API->settings['updates']['handle_updates']) {
|
||||
$instance->API->settings['updates']['handle_updates'] = true;
|
||||
$instance->API->datacenter->sockets[$instance->API->settings['connection_settings']['default_dc']]->updater->start();
|
||||
}
|
||||
ksort($instance->API->updates);
|
||||
foreach ($instance->API->updates as $key => $value) {
|
||||
unset($instance->API->updates[$key]);
|
||||
$this->event_update_handler($value, $path);
|
||||
}
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Started update loop', \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
while (true) {
|
||||
$read = [];
|
||||
$write = [];
|
||||
$except = [];
|
||||
foreach ($this->instances as $path => $instance) {
|
||||
if ($instance->API->authorized !== MTProto::LOGGED_IN) {
|
||||
continue;
|
||||
}
|
||||
if (time() - $instance->API->last_getdifference > $instance->API->settings['updates']['getdifference_interval']) {
|
||||
$instance->API->get_updates_difference();
|
||||
}
|
||||
if (isset($instance->session) && !is_null($instance->session) && time() - $instance->serialized > $instance->API->settings['serialization']['serialization_interval']) {
|
||||
$instance->API->logger->logger("Didn't serialize in a while, doing that now...");
|
||||
$instance->serialize($instance->session);
|
||||
}
|
||||
|
||||
foreach ($instance->API->datacenter->sockets as $id => $connection) {
|
||||
$read[$id.'-'.$path] = $connection->getSocket();
|
||||
}
|
||||
}
|
||||
if (time() - $this->serialized > $this->serialization_interval) {
|
||||
\danog\MadelineProto\Logger::log('Serializing combined event handler');
|
||||
$this->serialize();
|
||||
}
|
||||
|
||||
try {
|
||||
\Socket::select($read, $write, $except, $this->timeout);
|
||||
if (count($read)) {
|
||||
foreach (array_keys($read) as $id) {
|
||||
list($dc, $path) = explode('-', $id, 2);
|
||||
if (($error = $this->instances[$path]->API->recv_message($dc)) !== true) {
|
||||
if ($error === -404) {
|
||||
if ($this->instances[$path]->API->datacenter->sockets[$dc]->temp_auth_key !== null) {
|
||||
$this->instances[$path]->API->logger->logger('WARNING: Resetting auth key...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->instances[$path]->API->datacenter->sockets[$dc]->temp_auth_key = null;
|
||||
$this->instances[$path]->API->init_authorization();
|
||||
|
||||
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
$only_updates = $this->instances[$path]->API->handle_messages($dc);
|
||||
}
|
||||
}
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
foreach ($this->instances as $instance) {
|
||||
$instance->get_updates_difference();
|
||||
}
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->rpc !== 'RPC_CALL_FAIL') {
|
||||
throw $e;
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->instances[$path]->API->connect_to_all_dcs();
|
||||
}
|
||||
foreach ($this->instances as $path => $instance) {
|
||||
ksort($instance->API->updates);
|
||||
foreach ($instance->API->updates as $key => $value) {
|
||||
unset($instance->API->updates[$key]);
|
||||
$this->event_update_handler($value, $path);
|
||||
}
|
||||
}
|
||||
$instance->setCallback(function ($update) use ($path) {
|
||||
return $this->event_update_handler($update, $path);
|
||||
});
|
||||
if ($this->loop_callback !== null) {
|
||||
$callback = $this->loop_callback;
|
||||
$callback();
|
||||
$instance->setLoopCallback($this->loop_callback);
|
||||
}
|
||||
$loops[] = $this->call($instance->loop(0, ['async' => true]));
|
||||
}
|
||||
|
||||
Loop::repeat($this->serialization_interval * 1000, function () {
|
||||
\danog\MadelineProto\Logger::log('Serializing combined event handler');
|
||||
$this->serialize();
|
||||
});
|
||||
|
||||
\danog\MadelineProto\Logger::log('Started update loop', \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->wait(all($loops));
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* CombinedEventHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,37 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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.
|
||||
The PWRTelegram API 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Connection module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Loop\Connection\CheckLoop;
|
||||
use danog\MadelineProto\Loop\Connection\HttpWaitLoop;
|
||||
use danog\MadelineProto\Loop\Connection\ReadLoop;
|
||||
use danog\MadelineProto\Loop\Connection\UpdateLoop;
|
||||
use danog\MadelineProto\Loop\Connection\WriteLoop;
|
||||
use danog\MadelineProto\MTProtoTools\Crypt;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoTools\MsgIdHandler;
|
||||
use danog\MadelineProto\Stream\MTProtoTools\SeqNoHandler;
|
||||
|
||||
/**
|
||||
* Manages connection to telegram servers.
|
||||
* Connection class.
|
||||
*
|
||||
* Manages connection to Telegram datacenters
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class Connection
|
||||
{
|
||||
use Crypt;
|
||||
use MsgIdHandler;
|
||||
use SeqNoHandler;
|
||||
use \danog\Serializable;
|
||||
use \danog\MadelineProto\Tools;
|
||||
use Tools;
|
||||
|
||||
const API_ENDPOINT = 0;
|
||||
const VOIP_UDP_REFLECTOR_ENDPOINT = 1;
|
||||
const VOIP_TCP_REFLECTOR_ENDPOINT = 2;
|
||||
const VOIP_UDP_P2P_ENDPOINT = 3;
|
||||
const VOIP_UDP_LAN_ENDPOINT = 4;
|
||||
|
||||
public $sock = null;
|
||||
public $protocol = null;
|
||||
public $ip = null;
|
||||
public $port = null;
|
||||
public $timeout = null;
|
||||
public $parsed = [];
|
||||
const PENDING_MAX = 2000000000;
|
||||
|
||||
public $stream;
|
||||
|
||||
public $time_delta = 0;
|
||||
public $type = 0;
|
||||
public $peer_tag;
|
||||
@ -40,372 +63,227 @@ class Connection
|
||||
public $session_id;
|
||||
public $session_out_seq_no = 0;
|
||||
public $session_in_seq_no = 0;
|
||||
public $ipv6 = false;
|
||||
public $incoming_messages = [];
|
||||
public $outgoing_messages = [];
|
||||
public $new_incoming = [];
|
||||
public $new_outgoing = [];
|
||||
public $pending_outgoing = [];
|
||||
public $pending_outgoing_key = 0;
|
||||
public $max_incoming_id;
|
||||
public $max_outgoing_id;
|
||||
public $proxy = '\\Socket';
|
||||
public $extra = [];
|
||||
public $obfuscated = [];
|
||||
public $authorized = false;
|
||||
public $call_queue = [];
|
||||
public $object_queue = [];
|
||||
public $ack_queue = [];
|
||||
public $i = [];
|
||||
public $must_open = true;
|
||||
public $last_recv = 0;
|
||||
public $last_http_wait = 0;
|
||||
|
||||
public function __magic_construct($proxy, $extra, $ip, $port, $protocol, $timeout, $ipv6)
|
||||
public $datacenter;
|
||||
public $API;
|
||||
public $resumeWriterDeferred;
|
||||
public $ctx;
|
||||
public $pendingCheckWatcherId;
|
||||
|
||||
public $http_req_count = 0;
|
||||
public $http_res_count = 0;
|
||||
|
||||
public function getCtx()
|
||||
{
|
||||
// Can use:
|
||||
/*
|
||||
- tcp_full
|
||||
- tcp_abridged
|
||||
- tcp_intermediate
|
||||
- http
|
||||
- https
|
||||
- udp
|
||||
*/
|
||||
if ($proxy === '\\MTProxySocket') {
|
||||
$proxy = '\\Socket';
|
||||
$protocol = 'obfuscated2';
|
||||
return $this->ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect function.
|
||||
*
|
||||
* Connects to a telegram DC using the specified protocol, proxy and connection parameters
|
||||
*
|
||||
* @param string $proxy Proxy class name
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return \Amp\Promise
|
||||
*/
|
||||
public function connect(ConnectionContext $ctx): Promise
|
||||
{
|
||||
return $this->call($this->connectAsync($ctx));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect function.
|
||||
*
|
||||
* Connects to a telegram DC using the specified protocol, proxy and connection parameters
|
||||
*
|
||||
* @param string $proxy Proxy class name
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return \Amp\Promise
|
||||
*/
|
||||
public function connectAsync(ConnectionContext $ctx): \Generator
|
||||
{
|
||||
$this->API->logger->logger("Trying connection via $ctx", \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
$this->ctx = $ctx->getCtx();
|
||||
$this->datacenter = $ctx->getDc();
|
||||
$this->stream = yield $ctx->getStream();
|
||||
if (isset($this->old)) {
|
||||
unset($this->old);
|
||||
}
|
||||
|
||||
$this->protocol = $protocol;
|
||||
$this->timeout = $timeout;
|
||||
$this->ipv6 = $ipv6;
|
||||
$this->ip = $ip;
|
||||
$this->port = $port;
|
||||
$this->proxy = $proxy;
|
||||
$this->extra = $extra;
|
||||
if (($has_proxy = !in_array($proxy, ['\\MTProxySocket', '\\Socket'])) && !isset(class_implements($proxy)['danog\\MadelineProto\\Proxy'])) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['proxy_class_invalid']);
|
||||
if (!isset($this->writer)) {
|
||||
$this->writer = new WriteLoop($this->API, $this->datacenter);
|
||||
}
|
||||
switch ($this->protocol) {
|
||||
case 'tcp_abridged':
|
||||
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
|
||||
if ($has_proxy && $this->extra !== []) {
|
||||
$this->sock->setExtra($this->extra);
|
||||
}
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
if (!$this->sock->connect($ip, $port)) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
|
||||
}
|
||||
$this->sock->setBlocking(true);
|
||||
$this->write(chr(239));
|
||||
break;
|
||||
case 'tcp_intermediate':
|
||||
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
|
||||
if ($has_proxy && $this->extra !== []) {
|
||||
$this->sock->setExtra($this->extra);
|
||||
}
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
if (!$this->sock->connect($ip, $port)) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
|
||||
}
|
||||
$this->sock->setBlocking(true);
|
||||
$this->write(str_repeat(chr(238), 4));
|
||||
break;
|
||||
case 'tcp_full':
|
||||
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
|
||||
if ($has_proxy && $this->extra !== []) {
|
||||
$this->sock->setExtra($this->extra);
|
||||
}
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
if (!$this->sock->connect($ip, $port)) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
|
||||
}
|
||||
$this->sock->setBlocking(true);
|
||||
$this->out_seq_no = -1;
|
||||
$this->in_seq_no = -1;
|
||||
break;
|
||||
case 'obfuscated2':
|
||||
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
|
||||
if ($has_proxy && $this->extra !== []) {
|
||||
$this->sock->setExtra($this->extra);
|
||||
}
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
if (!$this->sock->connect($ip, $port)) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
|
||||
}
|
||||
$this->sock->setBlocking(true);
|
||||
do {
|
||||
$random = $this->random(64);
|
||||
} while (in_array(substr($random, 0, 4), ['PVrG', 'GET ', 'POST', 'HEAD', str_repeat(chr(238), 4)]) || $random[0] === chr(0xef) || substr($random, 4, 4) === "\0\0\0\0");
|
||||
$random[56] = $random[57] = $random[58] = $random[59] = chr(0xef);
|
||||
if (!isset($this->reader)) {
|
||||
$this->reader = new ReadLoop($this->API, $this->datacenter);
|
||||
}
|
||||
if (!isset($this->checker)) {
|
||||
$this->checker = new CheckLoop($this->API, $this->datacenter);
|
||||
}
|
||||
if (!isset($this->waiter)) {
|
||||
$this->waiter = new HttpWaitLoop($this->API, $this->datacenter);
|
||||
}
|
||||
if (!isset($this->updater)) {
|
||||
$this->updater = new UpdateLoop($this->API, $this->datacenter);
|
||||
}
|
||||
foreach ($this->new_outgoing as $message_id) {
|
||||
if ($this->outgoing_messages[$message_id]['unencrypted']) {
|
||||
$promise = $this->outgoing_messages[$message_id]['promise'];
|
||||
\Amp\Loop::defer(function () use ($promise) {
|
||||
$promise->fail(new Exception('Restart'));
|
||||
});
|
||||
unset($this->new_outgoing[$message_id]);
|
||||
unset($this->outgoing_messages[$message_id]);
|
||||
}
|
||||
}
|
||||
$this->http_req_count = 0;
|
||||
$this->http_res_count = 0;
|
||||
|
||||
$reversed = strrev(substr($random, 8, 48));
|
||||
$this->obfuscated = ['encryption' => new \phpseclib\Crypt\AES('ctr'), 'decryption' => new \phpseclib\Crypt\AES('ctr')];
|
||||
$this->obfuscated['encryption']->enableContinuousBuffer();
|
||||
$this->obfuscated['decryption']->enableContinuousBuffer();
|
||||
$this->obfuscated['encryption']->setKey(substr($random, 8, 32));
|
||||
$this->obfuscated['encryption']->setIV(substr($random, 40, 16));
|
||||
$this->obfuscated['decryption']->setKey(substr($reversed, 0, 32));
|
||||
$this->obfuscated['decryption']->setIV(substr($reversed, 32, 16));
|
||||
$random = substr_replace($random, substr(@$this->obfuscated['encryption']->encrypt($random), 56, 8), 56, 8);
|
||||
$wrote = 0;
|
||||
if (($wrote += $this->sock->write($random)) !== 64) {
|
||||
while (($wrote += $this->sock->write(substr($what, $wrote))) !== 64) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
$this->parsed = parse_url($ip);
|
||||
if ($this->parsed['host'][0] === '[') {
|
||||
$this->parsed['host'] = substr($this->parsed['host'], 1, -1);
|
||||
}
|
||||
if (strpos($this->protocol, 'https') === 0 && $proxy === '\\Socket') {
|
||||
$proxy = '\\FSocket';
|
||||
}
|
||||
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, strpos($this->protocol, 'https') === 0 ? PHP_INT_MAX : getprotobyname('tcp'));
|
||||
if ($has_proxy) {
|
||||
if ($this->extra !== []) {
|
||||
$this->sock->setExtra($this->extra);
|
||||
}/*
|
||||
if ($this->protocol === 'http') {
|
||||
$this->parsed['path'] = $this->parsed['scheme'].'://'.$this->parsed['host'].
|
||||
$this->parsed['path'];
|
||||
$port = 80;
|
||||
} elseif ($this->protocol === 'https') {
|
||||
$port = 443;
|
||||
}*/
|
||||
}
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
|
||||
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
|
||||
if (!$this->sock->connect($this->parsed['host'], $port)) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
|
||||
}
|
||||
$this->sock->setBlocking(true);
|
||||
break;
|
||||
case 'udp':
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
$this->writer->start();
|
||||
$this->reader->start();
|
||||
if (!$this->checker->start()) {
|
||||
$this->checker->resume();
|
||||
}
|
||||
$this->waiter->start();
|
||||
|
||||
if ($this->datacenter === $this->API->settings['connection_settings']['default_dc']) {
|
||||
$this->updater->start();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
public function sendMessage($message, $flush = true): Promise
|
||||
{
|
||||
switch ($this->protocol) {
|
||||
case 'tcp_abridged':
|
||||
case 'tcp_intermediate':
|
||||
case 'tcp_full':
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'obfuscated2':
|
||||
try {
|
||||
unset($this->sock);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
}
|
||||
break;
|
||||
case 'udp':
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
return $this->call($this->sendMessageGenerator($message, $flush));
|
||||
}
|
||||
|
||||
public function sendMessageGenerator($message, $flush = true): \Generator
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
|
||||
if (!isset($message['serialized_body'])) {
|
||||
$body = is_object($message['body']) ? yield $message['body'] : $message['body'];
|
||||
|
||||
$refresh_next = isset($message['refresh_next']) && $message['refresh_next'];
|
||||
//$refresh_next = true;
|
||||
|
||||
if ($refresh_next) {
|
||||
$this->API->referenceDatabase->refreshNext(true);
|
||||
}
|
||||
|
||||
if ($message['method']) {
|
||||
$body = $this->API->serialize_method($message['_'], $body);
|
||||
} else {
|
||||
$body = $this->API->serialize_object(['type' => $message['_']], $body, $message['_']);
|
||||
}
|
||||
if ($refresh_next) {
|
||||
$this->API->referenceDatabase->refreshNext(false);
|
||||
}
|
||||
$message['serialized_body'] = $body;
|
||||
}
|
||||
|
||||
$message['send_promise'] = $deferred;
|
||||
$this->pending_outgoing[$this->pending_outgoing_key++] = $message;
|
||||
$this->pending_outgoing_key %= self::PENDING_MAX;
|
||||
if ($flush) {
|
||||
$this->writer->resume();
|
||||
}
|
||||
|
||||
return yield $deferred->promise();
|
||||
}
|
||||
|
||||
public function setExtra($extra)
|
||||
{
|
||||
$this->API = $extra;
|
||||
}
|
||||
|
||||
public function disconnect()
|
||||
{
|
||||
$this->old = true;
|
||||
foreach (['reader', 'writer', 'checker', 'waiter', 'updater'] as $loop) {
|
||||
if (isset($this->{$loop}) && $this->{$loop}) {
|
||||
$this->{$loop}->signal($loop === 'reader' ? new NothingInTheSocketException() : true);
|
||||
}
|
||||
}
|
||||
if ($this->stream) {
|
||||
$this->stream->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public function close_and_reopen()
|
||||
public function reconnect(): Promise
|
||||
{
|
||||
$this->__destruct();
|
||||
\danog\MadelineProto\Logger::log('Reopening...', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->must_open = true;
|
||||
return $this->call($this->reconnectAsync());
|
||||
}
|
||||
|
||||
public function reconnectAsync(): \Generator
|
||||
{
|
||||
$this->API->logger->logger('Reconnecting');
|
||||
$this->disconnect();
|
||||
yield $this->API->datacenter->dc_connect_async($this->ctx->getDc());
|
||||
}
|
||||
|
||||
public function hasPendingCalls()
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
|
||||
$dc_config_number = isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all';
|
||||
$timeout = $API->settings['connection_settings'][$dc_config_number]['timeout'];
|
||||
foreach ($this->new_outgoing as $message_id) {
|
||||
if (isset($this->outgoing_messages[$message_id]['sent'])
|
||||
&& $this->outgoing_messages[$message_id]['sent'] + $timeout < time()
|
||||
&& ($this->temp_auth_key === null) === $this->outgoing_messages[$message_id]['unencrypted']
|
||||
&& $this->outgoing_messages[$message_id]['_'] !== 'msgs_state_req'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep function.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return ['proxy', 'extra', 'protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'peer_tag', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id', 'obfuscated', 'authorized', 'object_queue', 'ack_queue'];
|
||||
return ['peer_tag', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'max_incoming_id', 'max_outgoing_id', 'authorized', 'ack_queue'];
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$keys = array_keys((array) get_object_vars($this));
|
||||
if (count($keys) !== count(array_unique($keys))) {
|
||||
throw new Bug74586Exception();
|
||||
}
|
||||
$this->time_delta = 0;
|
||||
}
|
||||
|
||||
public function write($what, $length = null)
|
||||
{
|
||||
if ($length !== null) {
|
||||
$what = substr($what, 0, $length);
|
||||
} else {
|
||||
$length = strlen($what);
|
||||
}
|
||||
switch ($this->protocol) {
|
||||
case 'obfuscated2':
|
||||
$what = @$this->obfuscated['encryption']->encrypt($what);
|
||||
case 'tcp_abridged':
|
||||
case 'tcp_intermediate':
|
||||
case 'tcp_full':
|
||||
case 'http':
|
||||
case 'https':
|
||||
return $this->sock->write($what);
|
||||
case 'udp':
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function read($length)
|
||||
{
|
||||
//\danog\MadelineProto\Logger::log("Asked to read $length", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
switch ($this->protocol) {
|
||||
case 'obfuscated2':
|
||||
return @$this->obfuscated['decryption']->encrypt($this->sock->read($length));
|
||||
case 'tcp_abridged':
|
||||
case 'tcp_intermediate':
|
||||
case 'tcp_full':
|
||||
case 'http':
|
||||
case 'https':
|
||||
return $this->sock->read($length);
|
||||
case 'udp':
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function read_message()
|
||||
{
|
||||
switch ($this->protocol) {
|
||||
case 'tcp_full':
|
||||
$packet_length_data = $this->read(4);
|
||||
$packet_length = unpack('V', $packet_length_data)[1];
|
||||
$packet = $this->read($packet_length - 4);
|
||||
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
|
||||
throw new Exception('CRC32 was not correct!');
|
||||
}
|
||||
$this->in_seq_no++;
|
||||
$in_seq_no = unpack('V', substr($packet, 0, 4))[1];
|
||||
if ($in_seq_no != $this->in_seq_no) {
|
||||
throw new Exception('Incoming seq_no mismatch');
|
||||
}
|
||||
|
||||
return substr($packet, 4, $packet_length - 12);
|
||||
case 'tcp_intermediate':
|
||||
return $this->read(unpack('V', $this->read(4))[1]);
|
||||
case 'obfuscated2':
|
||||
case 'tcp_abridged':
|
||||
$packet_length = ord($this->read(1));
|
||||
|
||||
return $this->read($packet_length < 127 ? $packet_length << 2 : unpack('V', $this->read(3)."\0")[1] << 2);
|
||||
case 'http':
|
||||
case 'https':
|
||||
$response = $this->read_http_payload();
|
||||
if ($response['code'] !== 200) {
|
||||
Logger::log($response['body']);
|
||||
|
||||
return $this->pack_signed_int(-$response['code']);
|
||||
//throw new Exception($response['description'], $response['code']);
|
||||
}
|
||||
$close = $response['protocol'] === 'HTTP/1.0';
|
||||
if (isset($response['headers']['connection'])) {
|
||||
$close = strtolower($response['headers']['connection']) === 'close';
|
||||
}
|
||||
if ($close) {
|
||||
$this->close_and_reopen();
|
||||
}
|
||||
|
||||
return $response['body'];
|
||||
case 'udp':
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function send_message($message)
|
||||
{
|
||||
$this->must_open = $this->must_open || $this->sock === null || $this->sock->getResource() === null;
|
||||
|
||||
if ($this->must_open) {
|
||||
$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
|
||||
$this->must_open = false;
|
||||
}
|
||||
switch ($this->protocol) {
|
||||
case 'tcp_full':
|
||||
$this->out_seq_no++;
|
||||
$step1 = pack('VV', strlen($message) + 12, $this->out_seq_no).$message;
|
||||
$step2 = $step1.strrev(hash('crc32b', $step1, true));
|
||||
$this->write($step2);
|
||||
break;
|
||||
case 'tcp_intermediate':
|
||||
$this->write(pack('V', strlen($message)).$message);
|
||||
break;
|
||||
case 'obfuscated2':
|
||||
case 'tcp_abridged':
|
||||
$len = strlen($message) / 4;
|
||||
if ($len < 127) {
|
||||
$message = chr($len).$message;
|
||||
} else {
|
||||
$message = chr(127).substr(pack('V', $len), 0, 3).$message;
|
||||
}
|
||||
$this->write($message);
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
$this->write('POST '.$this->parsed['path']." HTTP/1.1\r\nHost: ".$this->parsed['host'].':'.$this->port."\r\n".$this->sock->getProxyHeaders()."Content-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nKeep-Alive: timeout=100000, max=10000000\r\nContent-Length: ".strlen($message)."\r\n\r\n".$message);
|
||||
break;
|
||||
case 'udp':
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function read_http_line()
|
||||
{
|
||||
$line = $lastchar = $curchar = '';
|
||||
while ($lastchar.$curchar !== "\r\n") {
|
||||
$line .= $lastchar;
|
||||
$lastchar = $curchar;
|
||||
$curchar = $this->sock->read(1);
|
||||
}
|
||||
|
||||
return $line;
|
||||
}
|
||||
|
||||
public function read_http_payload()
|
||||
{
|
||||
list($protocol, $code, $description) = explode(' ', $this->read_http_line(), 3);
|
||||
list($protocol, $protocol_version) = explode('/', $protocol);
|
||||
if ($protocol !== 'HTTP') {
|
||||
throw new \danog\MadelineProto\Exception('Wrong protocol');
|
||||
}
|
||||
$code = (int) $code;
|
||||
$headers = [];
|
||||
while (strlen($current_header = $this->read_http_line())) {
|
||||
$current_header = explode(':', $current_header, 2);
|
||||
$headers[strtolower($current_header[0])] = trim($current_header[1]);
|
||||
}
|
||||
|
||||
$read = '';
|
||||
if (isset($headers['content-length'])) {
|
||||
$read = $this->sock->read((int) $headers['content-length']);
|
||||
}/* elseif (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] === 'chunked') {
|
||||
do {
|
||||
$length = hexdec($this->read_http_line());
|
||||
$read .= $this->sock->read($length);
|
||||
$this->read_http_line();
|
||||
} while ($length);
|
||||
}*/
|
||||
|
||||
return ['protocol' => $protocol, 'protocol_version' => $protocol_version, 'code' => $code, 'description' => $description, 'body' => $read, 'headers' => $headers];
|
||||
}
|
||||
|
||||
public function getSocket()
|
||||
{
|
||||
return $this->sock;
|
||||
$this->pending_outgoing = [];
|
||||
$this->new_outgoing = [];
|
||||
$this->new_incoming = [];
|
||||
$this->outgoing_messages = [];
|
||||
$this->incoming_messages = [];
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Conversion module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
156
src/danog/MadelineProto/Coroutine.php
Normal file
156
src/danog/MadelineProto/Coroutine.php
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/**
|
||||
* Coroutine (modified version of AMP Coroutine).
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* @copyright 2015-2018 amphp
|
||||
* @copyright 2016 PHP Asynchronous Interoperability Group
|
||||
* @license https://opensource.org/licenses/MIT MIT
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Failure;
|
||||
use Amp\Internal;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
|
||||
/**
|
||||
* Creates a promise from a generator function yielding promises.
|
||||
*
|
||||
* When a promise is yielded, execution of the generator is interrupted until the promise is resolved. A success
|
||||
* value is sent into the generator, while a failure reason is thrown into the generator. Using a coroutine,
|
||||
* asynchronous code can be written without callbacks and be structured like synchronous code.
|
||||
*/
|
||||
final class Coroutine implements Promise
|
||||
{
|
||||
use Internal\Placeholder;
|
||||
/** @var \Generator */
|
||||
private $generator;
|
||||
/** @var callable(\Throwable|null $exception, mixed $value): void */
|
||||
private $onResolve;
|
||||
/** @var bool Used to control iterative coroutine continuation. */
|
||||
private $immediate = true;
|
||||
/** @var \Throwable|null Promise failure reason when executing next coroutine step, null at all other times. */
|
||||
private $exception;
|
||||
/** @var mixed Promise success value when executing next coroutine step, null at all other times. */
|
||||
private $value;
|
||||
|
||||
/**
|
||||
* @param \Generator $generator
|
||||
*/
|
||||
public function __construct(\Generator $generator)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
|
||||
try {
|
||||
$yielded = $this->generator->current();
|
||||
if (!$yielded instanceof Promise) {
|
||||
if (!$this->generator->valid()) {
|
||||
$this->resolve($this->generator->getReturn());
|
||||
|
||||
return;
|
||||
}
|
||||
$yielded = $this->transform($yielded);
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
$this->fail($exception);
|
||||
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* @param \Throwable|null $exception Exception to be thrown into the generator.
|
||||
* @param mixed $value Value to be sent into the generator.
|
||||
*/
|
||||
$this->onResolve = function ($exception, $value) {
|
||||
$this->exception = $exception;
|
||||
$this->value = $value;
|
||||
if (!$this->immediate) {
|
||||
$this->immediate = true;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
do {
|
||||
if ($this->exception) {
|
||||
// Throw exception at current execution point.
|
||||
$yielded = $this->generator->throw($this->exception);
|
||||
} else {
|
||||
// Send the new value and execute to next yield statement.
|
||||
$yielded = $this->generator->send($this->value);
|
||||
}
|
||||
if (!$yielded instanceof Promise) {
|
||||
if (!$this->generator->valid()) {
|
||||
$this->resolve($this->generator->getReturn());
|
||||
$this->onResolve = null;
|
||||
|
||||
return;
|
||||
}
|
||||
$yielded = $this->transform($yielded);
|
||||
}
|
||||
$this->immediate = false;
|
||||
$yielded->onResolve($this->onResolve);
|
||||
} while ($this->immediate);
|
||||
$this->immediate = true;
|
||||
} catch (\Throwable $exception) {
|
||||
$this->fail($exception);
|
||||
$this->onResolve = null;
|
||||
} finally {
|
||||
$this->exception = null;
|
||||
$this->value = null;
|
||||
}
|
||||
};
|
||||
$yielded->onResolve($this->onResolve);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to transform the non-promise yielded from the generator into a promise, otherwise returns an instance
|
||||
* `Amp\Failure` failed with an instance of `Amp\InvalidYieldError`.
|
||||
*
|
||||
* @param mixed $yielded Non-promise yielded from generator.
|
||||
*
|
||||
* @return \Amp\Promise
|
||||
*/
|
||||
private function transform($yielded): Promise
|
||||
{
|
||||
try {
|
||||
if (\is_array($yielded)) {
|
||||
foreach ($yielded as &$val) {
|
||||
if ($val instanceof \Generator) {
|
||||
$val = new self($val);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise\all($yielded);
|
||||
}
|
||||
if ($yielded instanceof \Generator) {
|
||||
return new self($yielded);
|
||||
}
|
||||
// No match, continue to returning Failure below.
|
||||
} catch (\Throwable $exception) {
|
||||
// Conversion to promise failed, fall-through to returning Failure below.
|
||||
}
|
||||
|
||||
return $yielded instanceof \Throwable || $yielded instanceof \Exception ? new Failure($yielded) : new Success($yielded);
|
||||
}
|
||||
}
|
@ -1,18 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* DataCenter module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\Socket\ClientConnectContext;
|
||||
use danog\MadelineProto\Stream\Common\BufferedRawStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\AbridgedStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\FullStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\IntermediatePaddedStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\IntermediateStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\ObfuscatedStream;
|
||||
use danog\MadelineProto\Stream\Proxy\HttpProxy;
|
||||
use danog\MadelineProto\Stream\Proxy\SocksProxy;
|
||||
use danog\MadelineProto\Stream\Transport\DefaultStream;
|
||||
use danog\MadelineProto\Stream\Transport\WssStream;
|
||||
use danog\MadelineProto\Stream\Transport\WsStream;
|
||||
|
||||
/**
|
||||
* Manages datacenters.
|
||||
*/
|
||||
@ -22,6 +44,7 @@ class DataCenter
|
||||
use \danog\Serializable;
|
||||
public $sockets = [];
|
||||
public $curdc = 0;
|
||||
private $API;
|
||||
private $dclist = [];
|
||||
private $settings = [];
|
||||
|
||||
@ -30,15 +53,16 @@ class DataCenter
|
||||
return ['sockets', 'curdc', 'dclist', 'settings'];
|
||||
}
|
||||
|
||||
public function __magic_construct($dclist, $settings)
|
||||
public function __magic_construct($API, $dclist, $settings)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->dclist = $dclist;
|
||||
$this->settings = $settings;
|
||||
foreach ($this->sockets as $key => $socket) {
|
||||
if ($socket instanceof Connection) {
|
||||
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['dc_con_stop'], $key), \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->API->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['dc_con_stop'], $key), \danog\MadelineProto\Logger::VERBOSE);
|
||||
$socket->old = true;
|
||||
$socket->__destruct();
|
||||
$socket->disconnect();
|
||||
} else {
|
||||
unset($this->sockets[$key]);
|
||||
}
|
||||
@ -46,109 +70,243 @@ class DataCenter
|
||||
}
|
||||
|
||||
public function dc_connect($dc_number)
|
||||
{
|
||||
return $this->wait($this->dc_connect_async($dc_number));
|
||||
}
|
||||
|
||||
public function dc_connect_async($dc_number): \Generator
|
||||
{
|
||||
if (isset($this->sockets[$dc_number]) && !isset($this->sockets[$dc_number]->old)) {
|
||||
return false;
|
||||
}
|
||||
$dc_config_number = isset($this->settings[$dc_number]) ? $dc_number : 'all';
|
||||
$test = $this->settings[$dc_config_number]['test_mode'] ? 'test' : 'main';
|
||||
$x = 0;
|
||||
do {
|
||||
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) {
|
||||
unset($this->sockets[$dc_number]);
|
||||
$ctxs = $this->generate_contexts($dc_number);
|
||||
foreach ($ctxs as $ctx) {
|
||||
try {
|
||||
if (isset($this->sockets[$dc_number]->old)) {
|
||||
$this->sockets[$dc_number]->setExtra($this->API);
|
||||
yield $this->sockets[$dc_number]->connect($ctx);
|
||||
} else {
|
||||
$this->sockets[$dc_number] = new Connection();
|
||||
$this->sockets[$dc_number]->setExtra($this->API);
|
||||
yield $this->sockets[$dc_number]->connect($ctx);
|
||||
}
|
||||
$this->API->logger->logger('OK!', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
\danog\MadelineProto\Logger::log("No info for DC $dc_number", \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
$this->API->logger->logger('Connection failed: '.$e->getMessage(), \danog\MadelineProto\Logger::ERROR);
|
||||
} catch (\Exception $e) {
|
||||
$this->API->logger->logger('Connection failed: '.$e->getMessage(), \danog\MadelineProto\Logger::ERROR);
|
||||
}
|
||||
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
|
||||
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
|
||||
if (isset($this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) && $this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) {
|
||||
if ($dc_config_number === 'all') {
|
||||
$dc_config_number = $dc_number;
|
||||
}
|
||||
if (!isset($this->settings[$dc_config_number])) {
|
||||
$this->settings[$dc_config_number] = $this->settings['all'];
|
||||
}
|
||||
$this->settings[$dc_config_number]['protocol'] = 'obfuscated2';
|
||||
}
|
||||
if (strpos($this->settings[$dc_config_number]['protocol'], 'https') === 0) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][preg_replace('/\D+/', '', $dc_number)];
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
$address = 'https://'.$subdomain.'.web.telegram.org/'.$path;
|
||||
$port = 443;
|
||||
}
|
||||
if ($this->settings[$dc_config_number]['protocol'] === 'http') {
|
||||
if ($ipv6) {
|
||||
$address = '['.$address.']';
|
||||
}
|
||||
$address = $this->settings[$dc_config_number]['protocol'].'://'.$address.'/api';
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['dc_con_test_start'], $dc_number, $test, $ipv6, $this->settings[$dc_config_number]['protocol']), \danog\MadelineProto\Logger::VERBOSE);
|
||||
foreach (array_unique([$port, 443, 80, 88]) as $port) {
|
||||
\danog\MadelineProto\Logger::log('Trying connection on port '.$port.' of '.$address.'...', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
try {
|
||||
if (isset($this->sockets[$dc_number]->old)) {
|
||||
$this->sockets[$dc_number]->__construct($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
|
||||
unset($this->sockets[$dc_number]->old);
|
||||
} else {
|
||||
$this->sockets[$dc_number] = new Connection($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('OK!', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return true;
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
\danog\MadelineProto\Logger::log('Connection failed: '.$e->getMessage(), \danog\MadelineProto\Logger::ERROR);
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
\danog\MadelineProto\Logger::log('Connection failed: read timeout', \danog\MadelineProto\Logger::ERROR);
|
||||
}
|
||||
if (isset($this->settings[$dc_config_number]['do_not_retry']) && $this->settings[$dc_config_number]['do_not_retry']) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch ($x) {
|
||||
case 0:
|
||||
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
case 1:
|
||||
if (isset($this->dclist[$test][$ipv6][$dc_number.'_bk']['ip_address'])) {
|
||||
$dc_number .= '_bk';
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection on backup DCs with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
case 2:
|
||||
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection on backup DCs with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
case 3:
|
||||
$this->settings[$dc_config_number]['proxy'] = '\\Socket';
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
case 4:
|
||||
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
case 5:
|
||||
$this->settings[$dc_config_number]['proxy'] = '\\HttpProxy';
|
||||
$this->settings[$dc_config_number]['proxy_extra'] = ['address' => 'localhost', 'port' => 80];
|
||||
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection with localhost HTTP proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
case 6:
|
||||
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
|
||||
\danog\MadelineProto\Logger::log('Connection failed, retrying connection with localhost HTTP proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...', \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Could not connect to DC $dc_number");
|
||||
}
|
||||
} while (++$x);
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception("Could not connect to DC $dc_number");
|
||||
}
|
||||
|
||||
public function generate_contexts($dc_number)
|
||||
{
|
||||
$ctxs = [];
|
||||
$combos = [];
|
||||
|
||||
$dc_config_number = isset($this->settings[$dc_number]) ? $dc_number : 'all';
|
||||
$test = $this->settings[$dc_config_number]['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
|
||||
switch ($this->settings[$dc_config_number]['protocol']) {
|
||||
case 'tcp_abridged':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [AbridgedStream::getName(), []]];
|
||||
break;
|
||||
case 'tcp_intermediate':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [IntermediateStream::getName(), []]];
|
||||
break;
|
||||
case 'tcp_intermediate_padded':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [IntermediatePaddedStream::getName(), []]];
|
||||
break;
|
||||
case 'tcp_full':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [FullStream::getName(), []]];
|
||||
break;
|
||||
case 'http':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpStream::getName(), []]];
|
||||
break;
|
||||
case 'https':
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpsStream::getName(), []]];
|
||||
break;
|
||||
default:
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_invalid']);
|
||||
}
|
||||
if ($this->settings[$dc_config_number]['obfuscated'] && !in_array($default[1][0], [HttpsStream::getName(), HttpStream::getName()])) {
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
|
||||
}
|
||||
if ($this->settings[$dc_config_number]['transport'] && !in_array($default[1][0], [HttpsStream::getName(), HttpStream::getName()])) {
|
||||
switch ($this->settings[$dc_config_number]['transport']) {
|
||||
case 'tcp':
|
||||
if ($this->settings[$dc_config_number]['obfuscated']) {
|
||||
$default = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
|
||||
}
|
||||
break;
|
||||
case 'wss':
|
||||
if ($this->settings[$dc_config_number]['obfuscated']) {
|
||||
$default = [[DefaultStream::getName(), []], [WssStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
|
||||
} else {
|
||||
$default = [[DefaultStream::getName(), []], [WssStream::getName(), []], [BufferedRawStream::getName(), []], end($default)];
|
||||
}
|
||||
break;
|
||||
case 'ws':
|
||||
if ($this->settings[$dc_config_number]['obfuscated']) {
|
||||
$default = [[DefaultStream::getName(), []], [WsStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), []], end($default)];
|
||||
} else {
|
||||
$default = [[DefaultStream::getName(), []], [WsStream::getName(), []], [BufferedRawStream::getName(), []], end($default)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$combos[] = $default;
|
||||
|
||||
if (!isset($this->settings[$dc_config_number]['do_not_retry'])) {
|
||||
if ((isset($this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) && $this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) || isset($this->dclist[$test][$ipv6][$dc_number]['secret'])) {
|
||||
$extra = isset($this->dclist[$test][$ipv6][$dc_number]['secret']) ? ['secret' => $this->dclist[$test][$ipv6][$dc_number]['secret']] : [];
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [ObfuscatedStream::getName(), $extra], [IntermediatePaddedStream::getName(), []]];
|
||||
}
|
||||
|
||||
if (is_array($this->settings[$dc_config_number]['proxy'])) {
|
||||
$proxies = $this->settings[$dc_config_number]['proxy'];
|
||||
$proxy_extras = $this->settings[$dc_config_number]['proxy_extra'];
|
||||
} else {
|
||||
$proxies = [$this->settings[$dc_config_number]['proxy']];
|
||||
$proxy_extras = [$this->settings[$dc_config_number]['proxy_extra']];
|
||||
}
|
||||
foreach ($proxies as $key => $proxy) {
|
||||
// Convert old settings
|
||||
if ($proxy === '\\Socket') {
|
||||
$proxy = DefaultStream::getName();
|
||||
}
|
||||
if ($proxy === '\\SocksProxy') {
|
||||
$proxy = SocksProxy::getName();
|
||||
}
|
||||
if ($proxy === '\\HttpProxy') {
|
||||
$proxy = HttpProxy::getName();
|
||||
}
|
||||
if ($proxy === '\\MTProxySocket') {
|
||||
$proxy = ObfuscatedStream::getName();
|
||||
}
|
||||
if ($proxy === DefaultStream::getName()) {
|
||||
continue;
|
||||
}
|
||||
$extra = $proxy_extras[$key];
|
||||
if (!isset(class_implements($proxy)['danog\\MadelineProto\\Stream\\StreamInterface'])) {
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['proxy_class_invalid']);
|
||||
}
|
||||
if ($proxy === ObfuscatedStream::getName() && in_array(strlen($extra['secret']), [17, 34])) {
|
||||
$combos []= [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [$proxy, $extra], [IntermediatePaddedStream::getName(), []]];
|
||||
}
|
||||
foreach ($combos as $k => $orig) {
|
||||
$combo = [];
|
||||
if ($proxy === ObfuscatedStream::getName()) {
|
||||
$combo = $orig;
|
||||
if ($combo[count($combo) - 2][0] === ObfuscatedStream::getName()) {
|
||||
$combo[count($combo) - 2][1] = $extra;
|
||||
} else {
|
||||
$mtproto = end($combo);
|
||||
$combo[count($combo) - 1] = [$proxy, $extra];
|
||||
$combo[] = $mtproto;
|
||||
}
|
||||
} else {
|
||||
if ($orig[1][0] === BufferedRawStream::getName()) {
|
||||
list($first, $second) = [array_slice($orig, 0, 2), array_slice($orig, 2)];
|
||||
$first[] = [$proxy, $extra];
|
||||
$combo = array_merge($first, $second);
|
||||
} elseif ($orig[1][0] === WssStream::getName()) {
|
||||
list($first, $second) = [array_slice($orig, 0, 1), array_slice($orig, 1)];
|
||||
$first[] = [BufferedRawStream::getName(), []];
|
||||
$first[] = [$proxy, $extra];
|
||||
$combo = array_merge($first, $second);
|
||||
}
|
||||
}
|
||||
|
||||
array_unshift($combos, $combo);
|
||||
//unset($combos[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
$combos[] = [[DefaultStream::getName(), []], [BufferedRawStream::getName(), []], [HttpsStream::getName(), []]];
|
||||
$combos = array_unique($combos, SORT_REGULAR);
|
||||
}
|
||||
/* @var $context \Amp\ClientConnectContext */
|
||||
$context = (new ClientConnectContext())->withMaxAttempts(1)->withConnectTimeout(1000 * $this->settings[$dc_config_number]['timeout']);
|
||||
|
||||
foreach ($combos as $combo) {
|
||||
$ipv6 = [$this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4', $this->settings[$dc_config_number]['ipv6'] ? 'ipv4' : 'ipv6'];
|
||||
|
||||
foreach ($ipv6 as $ipv6) {
|
||||
if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) {
|
||||
unset($this->sockets[$dc_number]);
|
||||
|
||||
$this->API->logger->logger("No info for DC $dc_number", \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
continue;
|
||||
}
|
||||
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
|
||||
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
|
||||
|
||||
foreach (array_unique([$port, 443, 80, 88, 5222]) as $port) {
|
||||
$stream = end($combo)[0];
|
||||
|
||||
if ($stream === HttpsStream::getName()) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][preg_replace('/\D+/', '', $dc_number)];
|
||||
if (strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
|
||||
$uri = 'tcp://'.$subdomain.'.web.telegram.org:'.$port.'/'.$path;
|
||||
} elseif ($stream === HttpStream::getName()) {
|
||||
$uri = 'tcp://'.$address.':'.$port.'/api';
|
||||
} else {
|
||||
$uri = 'tcp://'.$address.':'.$port;
|
||||
}
|
||||
|
||||
if ($combo[1][0] === WssStream::getName()) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][preg_replace('/\D+/', '', $dc_number)];
|
||||
if (strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiws_test' : 'apiws';
|
||||
|
||||
$uri = 'tcp://'.$subdomain.'.web.telegram.org:'.$port.'/'.$path;
|
||||
} elseif ($combo[1][0] === WsStream::getName()) {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][preg_replace('/\D+/', '', $dc_number)];
|
||||
if (strpos($dc_number, '_media') !== false) {
|
||||
$subdomain .= '-1';
|
||||
}
|
||||
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiws_test' : 'apiws';
|
||||
|
||||
//$uri = 'tcp://' . $subdomain . '.web.telegram.org:' . $port . '/' . $path;
|
||||
$uri = 'tcp://'.$address.':'.$port.'/'.$path;
|
||||
}
|
||||
|
||||
/** @var $ctx \danog\MadelineProto\Stream\ConnectionContext */
|
||||
$ctx = (new ConnectionContext())
|
||||
->setDc($dc_number)
|
||||
->setTest($this->settings[$dc_config_number]['test_mode'])
|
||||
->setSocketContext($context)
|
||||
->setUri($uri)
|
||||
->setIpv6($ipv6 === 'ipv6');
|
||||
|
||||
foreach ($combo as $stream) {
|
||||
$ctx->addStream(...$stream);
|
||||
}
|
||||
$ctxs[] = $ctx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->dclist[$test][$ipv6][$dc_number.'_bk']['ip_address'])) {
|
||||
$ctxs = array_merge($ctxs, $this->generate_contexts($dc_number.'_bk'));
|
||||
}
|
||||
|
||||
return $ctxs;
|
||||
}
|
||||
|
||||
public function get_dcs($all = true)
|
||||
{
|
||||
$test = $this->settings['all']['test_mode'] ? 'test' : 'main';
|
||||
@ -156,17 +314,4 @@ class DataCenter
|
||||
|
||||
return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
|
||||
}
|
||||
|
||||
public function select($poll = false)
|
||||
{
|
||||
$read = [];
|
||||
$write = [];
|
||||
$except = [];
|
||||
foreach ($this->sockets as $dc_id => $socket) {
|
||||
$read[$dc_id] = $socket->getSocket();
|
||||
}
|
||||
\Socket::select($read, $write, $except, $poll ? 0 : $this->settings['all']['timeout']);
|
||||
|
||||
return array_keys($read);
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* DocsBuilder module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -601,16 +607,21 @@ Any json-encodable data.
|
||||
}
|
||||
|
||||
public $template = '<?php
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Lang module
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Constructors module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\DocsBuilder;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Methods module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\DocsBuilder;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* EventHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -17,9 +23,12 @@ class EventHandler extends APIFactory
|
||||
{
|
||||
public function __construct($MadelineProto)
|
||||
{
|
||||
$this->API = $MadelineProto;
|
||||
$this->API = $MadelineProto->API;
|
||||
$this->async = $MadelineProto->async;
|
||||
$this->methods = $MadelineProto->methods;
|
||||
foreach ($this->API->get_method_namespaces() as $namespace) {
|
||||
$this->{$namespace} = new APIFactory($namespace, $this->API);
|
||||
$this->{$namespace}->async = $MadelineProto->async;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Exception module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -20,7 +26,11 @@ class Exception extends \Exception
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.'Revision: '.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.$this->getTLTrace();
|
||||
$result = $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.\danog\MadelineProto\Magic::$revision.PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.$this->getTLTrace();
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
$result = str_replace(PHP_EOL, '<br>'.PHP_EOL, $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
|
||||
@ -74,9 +84,8 @@ class Exception extends \Exception
|
||||
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
|
||||
if (error_reporting() === 0 || $errfile && strpos($errfile, 'vendor/amphp') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new self($errstr, $errno, null, $errfile, $errline);
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* FileCallback module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* FileCallbackInterface module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -100,7 +100,7 @@ interface auth
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* bytes password_hash,
|
||||
* InputCheckPasswordSRP password,
|
||||
* ]
|
||||
*
|
||||
* @return auth_Authorization
|
||||
@ -348,7 +348,7 @@ interface account
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* bytes current_password_hash,
|
||||
* InputCheckPasswordSRP password,
|
||||
* ]
|
||||
*
|
||||
* @return account_PasswordSettings
|
||||
@ -357,7 +357,7 @@ interface account
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* bytes current_password_hash,
|
||||
* InputCheckPasswordSRP password,
|
||||
* account_PasswordInputSettings new_settings,
|
||||
* ]
|
||||
*
|
||||
@ -388,7 +388,7 @@ interface account
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* bytes password_hash,
|
||||
* InputCheckPasswordSRP password,
|
||||
* int period,
|
||||
* ]
|
||||
*
|
||||
@ -536,6 +536,49 @@ interface account
|
||||
* @return bool
|
||||
*/
|
||||
public function finishTakeoutSession(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string code,
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function confirmPasswordEmail(array $params);
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function resendPasswordEmail();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function cancelPasswordEmail();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getContactSignUpNotification();
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* Bool silent,
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setContactSignUpNotification(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* boolean compare_sound,
|
||||
* InputNotifyPeer peer,
|
||||
* ]
|
||||
*
|
||||
* @return Updates
|
||||
*/
|
||||
public function getNotifyExceptions(array $params);
|
||||
}
|
||||
|
||||
interface users
|
||||
@ -571,6 +614,15 @@ interface users
|
||||
|
||||
interface contacts
|
||||
{
|
||||
/**
|
||||
* @param array params [
|
||||
* int hash,
|
||||
* ]
|
||||
*
|
||||
* @return Vector_of_int
|
||||
*/
|
||||
public function getContactIDs(array $params);
|
||||
|
||||
/**
|
||||
* @return Vector_of_ContactStatus
|
||||
*/
|
||||
@ -612,6 +664,15 @@ interface contacts
|
||||
*/
|
||||
public function deleteContacts(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string phones,
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteByPhones(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputUser id,
|
||||
@ -640,20 +701,6 @@ interface contacts
|
||||
*/
|
||||
public function getBlocked(array $params);
|
||||
|
||||
/**
|
||||
* @return Vector_of_int
|
||||
*/
|
||||
public function exportCard();
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* int export_card,
|
||||
* ]
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function importCard(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string q,
|
||||
@ -1337,6 +1384,7 @@ interface messages
|
||||
* boolean silent,
|
||||
* boolean background,
|
||||
* boolean clear_draft,
|
||||
* boolean hide_via,
|
||||
* InputPeer peer,
|
||||
* int reply_to_msg_id,
|
||||
* long query_id,
|
||||
@ -1360,14 +1408,12 @@ interface messages
|
||||
/**
|
||||
* @param array params [
|
||||
* boolean no_webpage,
|
||||
* boolean stop_geo_live,
|
||||
* InputPeer peer,
|
||||
* int id,
|
||||
* string message,
|
||||
* InputMedia media,
|
||||
* ReplyMarkup reply_markup,
|
||||
* MessageEntity entities,
|
||||
* InputGeoPoint geo_point,
|
||||
* ]
|
||||
*
|
||||
* @return Updates
|
||||
@ -1377,13 +1423,11 @@ interface messages
|
||||
/**
|
||||
* @param array params [
|
||||
* boolean no_webpage,
|
||||
* boolean stop_geo_live,
|
||||
* InputBotInlineMessageID id,
|
||||
* string message,
|
||||
* InputMedia media,
|
||||
* ReplyMarkup reply_markup,
|
||||
* MessageEntity entities,
|
||||
* InputGeoPoint geo_point,
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
@ -1771,6 +1815,61 @@ interface messages
|
||||
* @return Vector_of_DialogPeer
|
||||
*/
|
||||
public function getDialogUnreadMarks();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function clearAllDrafts();
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* boolean silent,
|
||||
* InputPeer peer,
|
||||
* int id,
|
||||
* ]
|
||||
*
|
||||
* @return Updates
|
||||
*/
|
||||
public function updatePinnedMessage(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputPeer peer,
|
||||
* int msg_id,
|
||||
* bytes options,
|
||||
* ]
|
||||
*
|
||||
* @return Updates
|
||||
*/
|
||||
public function sendVote(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputPeer peer,
|
||||
* int msg_id,
|
||||
* ]
|
||||
*
|
||||
* @return Updates
|
||||
*/
|
||||
public function getPollResults(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputPeer peer,
|
||||
* ]
|
||||
*
|
||||
* @return ChatOnlines
|
||||
*/
|
||||
public function getOnlines(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputPeer peer,
|
||||
* ]
|
||||
*
|
||||
* @return StatsURL
|
||||
*/
|
||||
public function getStatsURL(array $params);
|
||||
}
|
||||
|
||||
interface updates
|
||||
@ -1949,19 +2048,14 @@ interface help
|
||||
*/
|
||||
public function getNearestDc();
|
||||
|
||||
/**
|
||||
* @return help_AppUpdate
|
||||
*/
|
||||
public function getAppUpdate();
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputAppEvent events,
|
||||
* string source,
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
* @return help_AppUpdate
|
||||
*/
|
||||
public function saveAppLog(array $params);
|
||||
public function getAppUpdate(array $params);
|
||||
|
||||
/**
|
||||
* @return help_InviteText
|
||||
@ -2033,6 +2127,54 @@ interface help
|
||||
* @return help_DeepLinkInfo
|
||||
*/
|
||||
public function getDeepLinkInfo(array $params);
|
||||
|
||||
/**
|
||||
* @return JSONValue
|
||||
*/
|
||||
public function getAppConfig();
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputAppEvent events,
|
||||
* ]
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveAppLog(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* int hash,
|
||||
* ]
|
||||
*
|
||||
* @return help_PassportConfig
|
||||
*/
|
||||
public function getPassportConfig(array $params);
|
||||
|
||||
/**
|
||||
* @return help_SupportName
|
||||
*/
|
||||
public function getSupportName();
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputUser user_id,
|
||||
* ]
|
||||
*
|
||||
* @return help_UserInfo
|
||||
*/
|
||||
public function getUserInfo(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* InputUser user_id,
|
||||
* string message,
|
||||
* MessageEntity entities,
|
||||
* ]
|
||||
*
|
||||
* @return help_UserInfo
|
||||
*/
|
||||
public function editUserInfo(array $params);
|
||||
}
|
||||
|
||||
interface channels
|
||||
@ -2279,17 +2421,6 @@ interface channels
|
||||
*/
|
||||
public function toggleSignatures(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* boolean silent,
|
||||
* InputChannel channel,
|
||||
* int id,
|
||||
* ]
|
||||
*
|
||||
* @return Updates
|
||||
*/
|
||||
public function updatePinnedMessage(array $params);
|
||||
|
||||
/**
|
||||
* @return messages_Chats
|
||||
*/
|
||||
@ -2586,6 +2717,7 @@ interface langpack
|
||||
{
|
||||
/**
|
||||
* @param array params [
|
||||
* string lang_pack,
|
||||
* string lang_code,
|
||||
* ]
|
||||
*
|
||||
@ -2595,6 +2727,7 @@ interface langpack
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string lang_pack,
|
||||
* string lang_code,
|
||||
* string keys,
|
||||
* ]
|
||||
@ -2605,6 +2738,7 @@ interface langpack
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string lang_code,
|
||||
* int from_version,
|
||||
* ]
|
||||
*
|
||||
@ -2613,7 +2747,21 @@ interface langpack
|
||||
public function getDifference(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string lang_pack,
|
||||
* ]
|
||||
*
|
||||
* @return Vector_of_LangPackLanguage
|
||||
*/
|
||||
public function getLanguages();
|
||||
public function getLanguages(array $params);
|
||||
|
||||
/**
|
||||
* @param array params [
|
||||
* string lang_pack,
|
||||
* string lang_code,
|
||||
* ]
|
||||
*
|
||||
* @return LangPackLanguage
|
||||
*/
|
||||
public function getLanguage(array $params);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,21 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Logger module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
/*
|
||||
* Logger class
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\ByteStream\ResourceOutputStream;
|
||||
|
||||
class Logger
|
||||
{
|
||||
const foreground = ['default' => 39, 'black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'light_gray' => 37, 'dark_gray' => 90, 'light_red' => 91, 'light_green' => 92, 'light_yellow' => 93, 'light_blue' => 94, 'light_magenta' => 95, 'light_cyan' => 96, 'white' => 97];
|
||||
@ -28,6 +36,7 @@ class Logger
|
||||
public $prefix = '';
|
||||
public $level = 3;
|
||||
public $colors = [];
|
||||
public $newline = "\n";
|
||||
|
||||
public static $default;
|
||||
public static $printed = false;
|
||||
@ -82,6 +91,23 @@ class Logger
|
||||
$this->colors[self::WARNING] = implode(';', [self::foreground['white'], self::set['dim'], self::background['red']]);
|
||||
$this->colors[self::ERROR] = implode(';', [self::foreground['white'], self::set['bold'], self::background['red']]);
|
||||
$this->colors[self::FATAL_ERROR] = implode(';', [self::foreground['red'], self::set['bold'], self::background['light_gray']]);
|
||||
$this->newline = PHP_EOL;
|
||||
|
||||
if ($this->mode === 3) {
|
||||
$this->stdout = new ResourceOutputStream(STDOUT);
|
||||
if (php_sapi_name() !== 'cli') $this->newline = '<br>'.$this->newline;
|
||||
} elseif ($this->mode === 2) {
|
||||
$this->stdout = new ResourceOutputStream(fopen($this->optional, 'a+'));
|
||||
} elseif ($this->mode === 1) {
|
||||
$result = @ini_get('error_log');
|
||||
if ($result === 'syslog') {
|
||||
$this->stdout = new ResourceOutputStream(STDERR);
|
||||
} elseif ($result) {
|
||||
$this->stdout = new ResourceOutputStream(fopen($result, 'a+'));
|
||||
} else {
|
||||
$this->stdout = new ResourceOutputStream(STDERR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function log($param, $level = self::NOTICE)
|
||||
@ -126,13 +152,10 @@ class Logger
|
||||
$param = str_pad($file.$prefix.': ', 16 + strlen($prefix))."\t".$param;
|
||||
switch ($this->mode) {
|
||||
case 1:
|
||||
error_log($param);
|
||||
$this->stdout->write($param.$this->newline);
|
||||
break;
|
||||
case 2:
|
||||
error_log($param.PHP_EOL, 3, $this->optional);
|
||||
break;
|
||||
case 3:
|
||||
echo Magic::$isatty ? "\33[".$this->colors[$level].'m'.$param."\33[0m".PHP_EOL : $param.PHP_EOL;
|
||||
default:
|
||||
$this->stdout->write(Magic::$isatty ? "\33[".$this->colors[$level].'m'.$param."\33[0m".$this->newline : $param.$this->newline);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
158
src/danog/MadelineProto/Loop/Connection/CheckLoop.php
Normal file
158
src/danog/MadelineProto/Loop/Connection/CheckLoop.php
Normal file
@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/**
|
||||
* RPC call status check loop.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Connection;
|
||||
|
||||
use Amp\Deferred;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* RPC call status check loop.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class CheckLoop extends ResumableSignalLoop
|
||||
{
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
$this->startedLoop();
|
||||
$API->logger->logger("Entered check loop in DC {$datacenter}", Logger::ULTRA_VERBOSE);
|
||||
|
||||
$try_count = 0;
|
||||
|
||||
$timeout = $API->settings['connection_settings'][isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all']['timeout'];
|
||||
while (true) {
|
||||
while (empty($connection->new_outgoing)) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger('Exiting check loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
$try_count = 0;
|
||||
}
|
||||
|
||||
if ($connection->hasPendingCalls()) {
|
||||
$last_recv = $connection->last_recv;
|
||||
if ($connection->temp_auth_key !== null) {
|
||||
$message_ids = array_values($connection->new_outgoing);
|
||||
$deferred = new Deferred();
|
||||
$deferred->promise()->onResolve(
|
||||
function ($e, $result) use ($message_ids, $API, $connection, $datacenter) {
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
$reply = [];
|
||||
foreach (str_split($result['info']) as $key => $chr) {
|
||||
$message_id = $message_ids[$key];
|
||||
if (!isset($connection->outgoing_messages[$message_id])) {
|
||||
$API->logger->logger('Already got response for and forgot about message ID '.($message_id));
|
||||
continue;
|
||||
}
|
||||
if (!isset($connection->new_outgoing[$message_id])) {
|
||||
$API->logger->logger('Already got response for '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id));
|
||||
continue;
|
||||
}
|
||||
$chr = ord($chr);
|
||||
switch ($chr & 7) {
|
||||
case 0:
|
||||
$API->logger->logger('Wrong message status 0 for '.$connection->outgoing_messages[$message_id]['_'], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
if ($connection->outgoing_messages[$message_id]['_'] === 'msgs_state_req') {
|
||||
break;
|
||||
}
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' not received by server, resending...', \danog\MadelineProto\Logger::ERROR);
|
||||
$API->method_recall('', ['message_id' => $message_id, 'datacenter' => $datacenter, 'postpone' => true]);
|
||||
break;
|
||||
case 4:
|
||||
if ($chr & 32) {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and is being processed, waiting...', \danog\MadelineProto\Logger::ERROR);
|
||||
} elseif ($chr & 64) {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and was already processed, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
} elseif ($chr & 128) {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server and was already sent, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
} else {
|
||||
$API->logger->logger('Message '.$connection->outgoing_messages[$message_id]['_'].' with message ID '.($message_id).' received by server, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($reply) {
|
||||
$API->object_call('msg_resend_ans_req', ['msg_ids' => $reply], ['datacenter' => $datacenter, 'postpone' => true]);
|
||||
}
|
||||
$connection->writer->resume();
|
||||
}
|
||||
);
|
||||
$list = '';
|
||||
foreach ($message_ids as $message_id) {
|
||||
$list .= $connection->outgoing_messages[$message_id]['_'].', ';
|
||||
}
|
||||
$API->logger->logger("Still missing $list on DC $datacenter, sending state request", \danog\MadelineProto\Logger::ERROR);
|
||||
yield $API->object_call_async('msgs_state_req', ['msg_ids' => $message_ids], ['datacenter' => $datacenter, 'promise' => $deferred]);
|
||||
} else {
|
||||
foreach ($connection->new_outgoing as $message_id) {
|
||||
if (isset($connection->outgoing_messages[$message_id]['sent'])
|
||||
&& $connection->outgoing_messages[$message_id]['sent'] + $timeout < time()
|
||||
&& $connection->outgoing_messages[$message_id]['unencrypted']
|
||||
) {
|
||||
$API->logger->logger('Still missing '.$connection->outgoing_messages[$message_id]['_'].' with message id '.($message_id)." on DC $datacenter, resending", \danog\MadelineProto\Logger::ERROR);
|
||||
$API->method_recall('', ['message_id' => $message_id, 'datacenter' => $datacenter, 'postpone' => true]);
|
||||
}
|
||||
}
|
||||
$connection->writer->resume();
|
||||
}
|
||||
//$t = time();
|
||||
if (yield $this->waitSignal($this->pause($timeout))) {
|
||||
$API->logger->logger('Exiting check loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
//var_dumP("after ".(time() - $t).", with timeout ".$timeout);
|
||||
|
||||
$try_count++;
|
||||
if ($connection->last_recv === $last_recv) {
|
||||
$API->logger->logger("Reconnecting and exiting check loop on DC $datacenter");
|
||||
$this->exitedLoop();
|
||||
yield $connection->reconnect();
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (yield $this->waitSignal($this->pause($timeout))) {
|
||||
$API->logger->logger('Exiting check loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
$try_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
src/danog/MadelineProto/Loop/Connection/HttpWaitLoop.php
Normal file
83
src/danog/MadelineProto/Loop/Connection/HttpWaitLoop.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* HttpWait loop.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Connection;
|
||||
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpsStream;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\HttpStream;
|
||||
|
||||
/**
|
||||
* HttpWait loop.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class HttpWaitLoop extends ResumableSignalLoop
|
||||
{
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
if (!in_array($connection->getCtx()->getStreamName(), [HttpStream::getName(), HttpsStream::getName()])) {
|
||||
yield new Success(0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->startedLoop();
|
||||
$API->logger->logger("Entered HTTP wait loop in DC {$datacenter}", Logger::ULTRA_VERBOSE);
|
||||
|
||||
$timeout = $API->settings['connection_settings'][isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all']['timeout'];
|
||||
while (true) {
|
||||
//var_dump("http loop DC $datacenter");
|
||||
if ($a = yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger('Exiting HTTP wait loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
if (!in_array($connection->getCtx()->getStreamName(), [HttpStream::getName(), HttpsStream::getName()])) {
|
||||
$this->exitedLoop();
|
||||
yield new Success(0);
|
||||
|
||||
return;
|
||||
}
|
||||
while ($connection->temp_auth_key === null) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger('Exiting HTTP wait loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
//if (time() - $connection->last_http_wait >= $timeout) {
|
||||
$API->logger->logger("DC $datacenter: request {$connection->http_req_count}, response {$connection->http_res_count}");
|
||||
if ($connection->http_req_count === $connection->http_res_count && (!empty($connection->pending_outgoing) || (!empty($connection->new_outgoing) && !$connection->hasPendingCalls()))) {
|
||||
yield $connection->sendMessage(['_' => 'http_wait', 'body' => ['max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 0], 'content_related' => true, 'unencrypted' => false, 'method' => false]);
|
||||
//var_dump('sent wait');
|
||||
}
|
||||
$API->logger->logger("DC $datacenter: request {$connection->http_req_count}, response {$connection->http_res_count}");
|
||||
|
||||
//($connection->last_http_wait + $timeout) - time()
|
||||
}
|
||||
}
|
||||
}
|
220
src/danog/MadelineProto/Loop/Connection/ReadLoop.php
Normal file
220
src/danog/MadelineProto/Loop/Connection/ReadLoop.php
Normal file
@ -0,0 +1,220 @@
|
||||
<?php
|
||||
/**
|
||||
* Socket read loop.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Connection;
|
||||
|
||||
use Amp\Loop;
|
||||
use Amp\Promise;
|
||||
use Amp\Websocket\ClosedException;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\Impl\SignalLoop;
|
||||
use danog\MadelineProto\MTProtoTools\Crypt;
|
||||
use danog\MadelineProto\NothingInTheSocketException;
|
||||
use danog\MadelineProto\Tools;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Socket read loop.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class ReadLoop extends SignalLoop
|
||||
{
|
||||
use Tools;
|
||||
use Crypt;
|
||||
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
$this->startedLoop();
|
||||
$API->logger->logger("Entered read loop in DC {$datacenter}", Logger::ULTRA_VERBOSE);
|
||||
$timeout = $API->settings['connection_settings'][isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all']['timeout'];
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
$error = yield $this->waitSignal($this->readMessage());
|
||||
} catch (NothingInTheSocketException $e) {
|
||||
if (isset($connection->old)) {
|
||||
$this->exitedLoop();
|
||||
$API->logger->logger("Exiting read loop in DC $datacenter");
|
||||
|
||||
return;
|
||||
}
|
||||
$API->logger->logger("Got nothing in the socket in DC {$datacenter}, reconnecting...", Logger::ERROR);
|
||||
yield $connection->reconnect();
|
||||
continue;
|
||||
} catch (ClosedException $e) {
|
||||
$API->logger->logger($e->getMessage(), Logger::FATAL_ERROR);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (is_int($error)) {
|
||||
$this->exitedLoop();
|
||||
yield $connection->reconnect();
|
||||
|
||||
if ($error === -404) {
|
||||
if ($connection->temp_auth_key !== null) {
|
||||
$API->logger->logger("WARNING: Resetting auth key in DC {$datacenter}...", \danog\MadelineProto\Logger::WARNING);
|
||||
$connection->temp_auth_key = null;
|
||||
$connection->session_id = null;
|
||||
foreach ($connection->new_outgoing as $message_id) {
|
||||
$connection->outgoing_messages[$message_id]['sent'] = 0;
|
||||
}
|
||||
$API->init_authorization();
|
||||
} else {
|
||||
//throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
} elseif ($error === -1) {
|
||||
$API->logger->logger("WARNING: Got quick ack from DC {$datacenter}", \danog\MadelineProto\Logger::WARNING);
|
||||
} elseif ($error === 0) {
|
||||
$API->logger->logger("Got NOOP from DC {$datacenter}", \danog\MadelineProto\Logger::WARNING);
|
||||
} else {
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$connection->http_res_count++;
|
||||
|
||||
try {
|
||||
$API->handle_messages($datacenter);
|
||||
} finally {
|
||||
$this->exitedLoop();
|
||||
}
|
||||
$this->startedLoop();
|
||||
// Loop::defer(function () use ($datacenter) {
|
||||
if ($this->API->is_http($datacenter)) {
|
||||
$this->API->datacenter->sockets[$datacenter]->waiter->resume();
|
||||
} // });
|
||||
}
|
||||
}
|
||||
|
||||
public function readMessage(): Promise
|
||||
{
|
||||
return call([$this, 'readMessageAsync']);
|
||||
}
|
||||
|
||||
public function readMessageAsync(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
try {
|
||||
$buffer = yield $connection->stream->getReadBuffer($payload_length);
|
||||
} catch (ClosedException $e) {
|
||||
$API->logger->logger($e->getReason());
|
||||
if (strpos($e->getReason(), ' ') === 0) {
|
||||
$payload = -substr($e->getReason(), 7);
|
||||
$API->logger->logger("Received $payload from DC ".$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($payload_length === 4) {
|
||||
$payload = $this->unpack_signed_int(yield $buffer->bufferRead(4));
|
||||
$API->logger->logger("Received $payload from DC ".$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return $payload;
|
||||
}
|
||||
$auth_key_id = yield $buffer->bufferRead(8);
|
||||
if ($auth_key_id === "\0\0\0\0\0\0\0\0") {
|
||||
$message_id = yield $buffer->bufferRead(8);
|
||||
if (!in_array($message_id, [1, 0])) {
|
||||
$connection->check_message_id($message_id, ['outgoing' => false, 'container' => false]);
|
||||
}
|
||||
$message_length = unpack('V', yield $buffer->bufferRead(4))[1];
|
||||
$message_data = yield $buffer->bufferRead($message_length);
|
||||
$left = $payload_length - $message_length - 4 - 8 - 8;
|
||||
if ($left) {
|
||||
$API->logger->logger('Padded unencrypted message', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if ($left < (-$message_length & 15)) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too small');
|
||||
}
|
||||
yield $buffer->bufferRead($left);
|
||||
}
|
||||
$connection->incoming_messages[$message_id] = [];
|
||||
} elseif ($auth_key_id === $connection->temp_auth_key['id']) {
|
||||
$message_key = yield $buffer->bufferRead(16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $connection->temp_auth_key['auth_key'], false);
|
||||
$encrypted_data = yield $buffer->bufferRead($payload_length - 24);
|
||||
$protocol_padding = strlen($encrypted_data) % 16;
|
||||
if ($protocol_padding) {
|
||||
$encrypted_data = substr($encrypted_data, 0, -$protocol_padding);
|
||||
}
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
/*
|
||||
$server_salt = substr($decrypted_data, 0, 8);
|
||||
if ($server_salt != $connection->temp_auth_key['server_salt']) {
|
||||
$API->logger->logger('WARNING: Server salt mismatch (my server salt '.$connection->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
*/
|
||||
$session_id = substr($decrypted_data, 8, 8);
|
||||
if ($session_id != $connection->session_id) {
|
||||
throw new \danog\MadelineProto\Exception('Session id mismatch.');
|
||||
}
|
||||
$message_id = substr($decrypted_data, 16, 8);
|
||||
$connection->check_message_id($message_id, ['outgoing' => false, 'container' => false]);
|
||||
$seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
|
||||
|
||||
$message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
|
||||
if ($message_data_length > strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
|
||||
}
|
||||
if (strlen($decrypted_data) - 32 - $message_data_length < 12) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too small');
|
||||
}
|
||||
if (strlen($decrypted_data) - 32 - $message_data_length > 1024) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too big');
|
||||
}
|
||||
if ($message_data_length < 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not positive');
|
||||
}
|
||||
if ($message_data_length % 4 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
|
||||
}
|
||||
$message_data = substr($decrypted_data, 32, $message_data_length);
|
||||
if ($message_key != substr(hash('sha256', substr($connection->temp_auth_key['auth_key'], 96, 32).$decrypted_data, true), 8, 16)) {
|
||||
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
|
||||
}
|
||||
$connection->incoming_messages[$message_id] = ['seq_no' => $seq_no];
|
||||
} else {
|
||||
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
|
||||
}
|
||||
$deserialized = $API->deserialize($message_data, ['type' => '', 'datacenter' => $datacenter]);
|
||||
$API->referenceDatabase->reset();
|
||||
|
||||
$connection->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$connection->incoming_messages[$message_id]['response'] = -1;
|
||||
$connection->new_incoming[$message_id] = $message_id;
|
||||
$connection->last_recv = time();
|
||||
$connection->last_http_wait = 0;
|
||||
|
||||
$API->logger->logger('Received payload from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
85
src/danog/MadelineProto/Loop/Connection/UpdateLoop.php
Normal file
85
src/danog/MadelineProto/Loop/Connection/UpdateLoop.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Update loop.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Connection;
|
||||
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
|
||||
|
||||
/**
|
||||
* Update loop.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class UpdateLoop extends ResumableSignalLoop
|
||||
{
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
if (!$this->API->settings['updates']['handle_updates']) {
|
||||
yield new Success(0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->startedLoop();
|
||||
$API->logger->logger("Entered updates loop in DC {$datacenter}", Logger::ULTRA_VERBOSE);
|
||||
|
||||
$timeout = $API->settings['updates']['getdifference_interval'];
|
||||
while (true) {
|
||||
while (!$this->API->settings['updates']['handle_updates'] || !$this->has_all_auth()) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger('Exiting update loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (time() - $API->last_getdifference > $timeout) {
|
||||
if (!$API->get_updates_difference()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (yield $this->waitSignal($this->pause(($API->last_getdifference + $timeout) - time()))) {
|
||||
$API->logger->logger('Exiting update loop');
|
||||
$this->exitedLoop();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function has_all_auth()
|
||||
{
|
||||
if ($this->API->isInitingAuthorization()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->API->datacenter->sockets as $dc) {
|
||||
if (!$dc->authorized || $dc->temp_auth_key === null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
330
src/danog/MadelineProto/Loop/Connection/WriteLoop.php
Normal file
330
src/danog/MadelineProto/Loop/Connection/WriteLoop.php
Normal file
@ -0,0 +1,330 @@
|
||||
<?php
|
||||
/**
|
||||
* Socket write loop.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Connection;
|
||||
|
||||
use Amp\Coroutine;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Connection;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\Impl\ResumableSignalLoop;
|
||||
use danog\MadelineProto\MTProtoTools\Crypt;
|
||||
use danog\MadelineProto\Tools;
|
||||
|
||||
/**
|
||||
* Socket write loop.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class WriteLoop extends ResumableSignalLoop
|
||||
{
|
||||
use Crypt;
|
||||
use Tools;
|
||||
|
||||
public function loop(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
$this->startedLoop();
|
||||
$API->logger->logger("Entered write loop in DC {$datacenter}", Logger::ULTRA_VERBOSE);
|
||||
|
||||
while (true) {
|
||||
if (empty($connection->pending_outgoing)) {
|
||||
if (yield $this->waitSignal($this->pause())) {
|
||||
$API->logger->logger('Exiting write loop');
|
||||
$this->exitedLoop();
|
||||
yield new Success(0);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if ($connection->temp_auth_key === null) {
|
||||
$res = $this->unencryptedWriteLoopAsync();
|
||||
} else {
|
||||
$res = $this->encryptedWriteLoopAsync();
|
||||
}
|
||||
if ($res instanceof \Generator) {
|
||||
yield new Coroutine($res);
|
||||
}
|
||||
} finally {
|
||||
$this->exitedLoop();
|
||||
}
|
||||
$this->startedLoop();
|
||||
|
||||
//$connection->waiter->resume();
|
||||
}
|
||||
}
|
||||
|
||||
public function unencryptedWriteLoopAsync(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
while ($connection->pending_outgoing) {
|
||||
$skipped_all = true;
|
||||
foreach ($connection->pending_outgoing as $k => $message) {
|
||||
if ($connection->temp_auth_key !== null) {
|
||||
return;
|
||||
}
|
||||
if (!$message['unencrypted']) {
|
||||
continue;
|
||||
}
|
||||
$skipped_all = false;
|
||||
|
||||
$body = $message['serialized_body'];
|
||||
|
||||
$API->logger->logger("Sending {$message['_']} as unencrypted message to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$message_id = isset($message['msg_id']) ? $message['msg_id'] : $connection->generate_message_id();
|
||||
$length = strlen($body);
|
||||
|
||||
$pad_length = -$length & 15;
|
||||
$pad_length += 16 * $this->random_int($modulus = 16);
|
||||
|
||||
$pad = $this->random($pad_length);
|
||||
$buffer = yield $connection->stream->getWriteBuffer(8 + 8 + 4 + $pad_length + $length);
|
||||
|
||||
yield $buffer->bufferWrite("\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int($length).$body.$pad);
|
||||
|
||||
//var_dump("plain ".bin2hex($message_id));
|
||||
$connection->http_req_count++;
|
||||
$connection->outgoing_messages[$message_id] = $message;
|
||||
$connection->outgoing_messages[$message_id]['sent'] = time();
|
||||
$connection->outgoing_messages[$message_id]['tries'] = 0;
|
||||
$connection->outgoing_messages[$message_id]['unencrypted'] = true;
|
||||
$connection->new_outgoing[$message_id] = $message_id;
|
||||
|
||||
unset($connection->pending_outgoing[$k]);
|
||||
|
||||
$API->logger->logger("Sent {$message['_']} as unencrypted message to DC {$datacenter}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$message['send_promise']->resolve(isset($message['promise']) ? $message['promise'] : true);
|
||||
}
|
||||
if ($skipped_all) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function encryptedWriteLoopAsync(): \Generator
|
||||
{
|
||||
$API = $this->API;
|
||||
$datacenter = $this->datacenter;
|
||||
$connection = $this->connection;
|
||||
|
||||
do {
|
||||
if ($connection->temp_auth_key === null) {
|
||||
return;
|
||||
}
|
||||
if ($this->API->is_http($datacenter) && empty($connection->pending_outgoing)) {
|
||||
return;
|
||||
}
|
||||
if (count($to_ack = $connection->ack_queue)) {
|
||||
$connection->pending_outgoing[$connection->pending_outgoing_key++] = ['_' => 'msgs_ack', 'serialized_body' => $this->API->serialize_object(['type' => 'msgs_ack'], ['msg_ids' => $connection->ack_queue], 'msgs_ack'), 'content_related' => false, 'unencrypted' => false, 'method' => false];
|
||||
$connection->pending_outgoing_key %= Connection::PENDING_MAX;
|
||||
}
|
||||
|
||||
$has_http_wait = false;
|
||||
$messages = [];
|
||||
$keys = [];
|
||||
|
||||
foreach ($connection->pending_outgoing as $message) {
|
||||
if ($message['_'] === 'http_wait') {
|
||||
$has_http_wait = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($API->is_http($datacenter) && !$has_http_wait) {
|
||||
$dc_config_number = isset($API->settings['connection_settings'][$datacenter]) ? $datacenter : 'all';
|
||||
|
||||
//$connection->pending_outgoing[$connection->pending_outgoing_key++] = ['_' => 'http_wait', 'serialized_body' => $this->API->serialize_object(['type' => ''], ['_' => 'http_wait', 'max_wait' => $API->settings['connection_settings'][$dc_config_number]['timeout'] * 1000 - 100, 'wait_after' => 0, 'max_delay' => 0], 'http_wait'), 'content_related' => true, 'unencrypted' => false, 'method' => true];
|
||||
$connection->pending_outgoing[$connection->pending_outgoing_key++] = ['_' => 'http_wait', 'serialized_body' => $this->API->serialize_object(['type' => ''], ['_' => 'http_wait', 'max_wait' => 30000, 'wait_after' => 0, 'max_delay' => 1], 'http_wait'), 'content_related' => true, 'unencrypted' => false, 'method' => true];
|
||||
$connection->pending_outgoing_key %= Connection::PENDING_MAX;
|
||||
|
||||
$has_http_wait = true;
|
||||
}
|
||||
|
||||
$total_length = 0;
|
||||
$count = 0;
|
||||
ksort($connection->pending_outgoing);
|
||||
foreach ($connection->pending_outgoing as $k => $message) {
|
||||
if ($message['unencrypted']) {
|
||||
continue;
|
||||
}
|
||||
if (isset($message['container'])) {
|
||||
unset($connection->pending_outgoing[$k]);
|
||||
continue;
|
||||
}
|
||||
$body = $message['serialized_body'];
|
||||
|
||||
$message_id = isset($message['msg_id']) ? $message['msg_id'] : $connection->generate_message_id($datacenter);
|
||||
|
||||
$API->logger->logger("Sending {$message['_']} as encrypted message to DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$MTmessage = ['_' => 'MTmessage', 'msg_id' => $message_id, 'body' => $body, 'seqno' => $connection->generate_out_seq_no($message['content_related'])];
|
||||
|
||||
if (isset($message['method']) && $message['method'] && $message['_'] !== 'http_wait') {
|
||||
if ((!isset($connection->temp_auth_key['connection_inited']) || $connection->temp_auth_key['connection_inited'] === false) && $message['_'] !== 'auth.bindTempAuthKey') {
|
||||
$API->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $message['_']), \danog\MadelineProto\Logger::NOTICE);
|
||||
$MTmessage['body'] = $API->serialize_method(
|
||||
'invokeWithLayer',
|
||||
[
|
||||
'layer' => $API->settings['tl_schema']['layer'],
|
||||
'query' => $API->serialize_method(
|
||||
'initConnection',
|
||||
[
|
||||
'api_id' => $API->settings['app_info']['api_id'],
|
||||
'api_hash' => $API->settings['app_info']['api_hash'],
|
||||
'device_model' => strpos($datacenter, 'cdn') === false ? $API->settings['app_info']['device_model'] : 'n/a',
|
||||
'system_version' => strpos($datacenter, 'cdn') === false ? $API->settings['app_info']['system_version'] : 'n/a',
|
||||
'app_version' => $API->settings['app_info']['app_version'],
|
||||
'system_lang_code' => $API->settings['app_info']['lang_code'],
|
||||
'lang_code' => $API->settings['app_info']['lang_code'],
|
||||
'lang_pack' => $API->settings['app_info']['lang_pack'],
|
||||
'query' => $MTmessage['body'],
|
||||
]
|
||||
),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if (isset($message['queue'])) {
|
||||
if (!isset($connection->call_queue[$message['queue']])) {
|
||||
$connection->call_queue[$message['queue']] = [];
|
||||
}
|
||||
$MTmessage['body'] = $API->serialize_method('invokeAfterMsgs', ['msg_ids' => $connection->call_queue[$message['queue']], 'query' => $MTmessage['body']]);
|
||||
|
||||
$connection->call_queue[$message['queue']][$message_id] = $message_id;
|
||||
if (count($connection->call_queue[$message['queue']]) > $API->settings['msg_array_limit']['call_queue']) {
|
||||
reset($connection->call_queue[$message['queue']]);
|
||||
$key = key($connection->call_queue[$message['queue']]);
|
||||
unset($connection->call_queue[$message['queue']][$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/* if ($API->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($MTmessage['body'])) > $API->settings['requests']['gzip_encode_if_gt']) {
|
||||
if (($g = strlen($gzipped = gzencode($MTmessage['body']))) < $l) {
|
||||
$MTmessage['body'] = $API->serialize_object(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data');
|
||||
$API->logger->logger('Using GZIP compression for ' . $message['_'] . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
unset($gzipped);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
$body_length = strlen($MTmessage['body']);
|
||||
if ($total_length && $total_length + $body_length + 32 > 655360) {
|
||||
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::NOTICE);
|
||||
break;
|
||||
}
|
||||
$count++;
|
||||
$total_length += $body_length + 32;
|
||||
|
||||
$MTmessage['bytes'] = $body_length;
|
||||
$messages[] = $MTmessage;
|
||||
$keys[$k] = $message_id;
|
||||
|
||||
if ($total_length && $total_length + 32 > 655360) {
|
||||
$API->logger->logger('Length overflow, postponing part of payload', \danog\MadelineProto\Logger::NOTICE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($messages) > 1) {
|
||||
$API->logger->logger("Wrapping in msg_container as encrypted message for DC {$datacenter}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$message_id = $connection->generate_message_id($datacenter);
|
||||
$connection->pending_outgoing[$connection->pending_outgoing_key] = ['_' => 'msg_container', 'container' => array_values($keys), 'content_related' => false, 'method' => false];
|
||||
|
||||
//var_dumP("container ".bin2hex($message_id));
|
||||
$keys[$connection->pending_outgoing_key++] = $message_id;
|
||||
$connection->pending_outgoing_key %= Connection::PENDING_MAX;
|
||||
|
||||
$message_data = $API->serialize_object(['type' => ''], ['_' => 'msg_container', 'messages' => $messages], 'container');
|
||||
|
||||
$message_data_length = strlen($message_data);
|
||||
$seq_no = $connection->generate_out_seq_no(false);
|
||||
} elseif (count($messages)) {
|
||||
$message = $messages[0];
|
||||
$message_data = $message['body'];
|
||||
$message_data_length = $message['bytes'];
|
||||
$message_id = $message['msg_id'];
|
||||
$seq_no = $message['seqno'];
|
||||
} else {
|
||||
$API->logger->logger('NO MESSAGE SENT', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
unset($messages);
|
||||
|
||||
$plaintext = $connection->temp_auth_key['server_salt'].$connection->session_id.$message_id.pack('VV', $seq_no, $message_data_length).$message_data;
|
||||
$padding = $this->posmod(-strlen($plaintext), 16);
|
||||
if ($padding < 12) {
|
||||
$padding += 16;
|
||||
}
|
||||
$padding = $this->random($padding);
|
||||
$message_key = substr(hash('sha256', substr($connection->temp_auth_key['auth_key'], 88, 32).$plaintext.$padding, true), 8, 16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $connection->temp_auth_key['auth_key']);
|
||||
$message = $connection->temp_auth_key['id'].$message_key.$this->ige_encrypt($plaintext.$padding, $aes_key, $aes_iv);
|
||||
|
||||
$buffer = yield $connection->stream->getWriteBuffer($len = strlen($message));
|
||||
|
||||
$t = microtime(true);
|
||||
yield $buffer->bufferWrite($message);
|
||||
|
||||
$connection->http_req_count++;
|
||||
|
||||
$API->logger->logger("Sent encrypted payload to DC {$datacenter}, speed ".((($len * 8) / (microtime(true) - $t)) / 1000000).' mbps!', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$sent = time();
|
||||
|
||||
if ($to_ack) {
|
||||
$connection->ack_queue = [];
|
||||
}
|
||||
|
||||
if ($has_http_wait) {
|
||||
$connection->last_http_wait = $sent;
|
||||
} elseif ($API->isAltervista()) {
|
||||
$connection->last_http_wait = PHP_INT_MAX;
|
||||
}
|
||||
|
||||
foreach ($keys as $key => $message_id) {
|
||||
$connection->outgoing_messages[$message_id] = &$connection->pending_outgoing[$key];
|
||||
if (isset($connection->outgoing_messages[$message_id]['promise'])) {
|
||||
$connection->new_outgoing[$message_id] = $message_id;
|
||||
$connection->outgoing_messages[$message_id]['sent'] = $sent;
|
||||
$connection->outgoing_messages[$message_id]['tries'] = 0;
|
||||
}
|
||||
if (isset($connection->outgoing_messages[$message_id]['send_promise'])) {
|
||||
$connection->outgoing_messages[$message_id]['send_promise']->resolve(isset($connection->outgoing_messages[$message_id]['promise']) ? $connection->outgoing_messages[$message_id]['promise'] : true);
|
||||
}
|
||||
//var_dumP("encrypted ".bin2hex($message_id)." ".$connection->outgoing_messages[$message_id]['_']);
|
||||
unset($connection->pending_outgoing[$key]);
|
||||
}
|
||||
|
||||
//if (!empty($connection->pending_outgoing)) $connection->select();
|
||||
} while (!empty($connection->pending_outgoing));
|
||||
|
||||
$connection->pending_outgoing_key = 0;
|
||||
}
|
||||
}
|
75
src/danog/MadelineProto/Loop/Impl/Loop.php
Normal file
75
src/danog/MadelineProto/Loop/Impl/Loop.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Impl;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\Loop\LoopInterface;
|
||||
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
* Wraps the asynchronous generator methods with asynchronous promise-based methods
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
abstract class Loop implements LoopInterface
|
||||
{
|
||||
use \danog\MadelineProto\Tools;
|
||||
|
||||
private $count = 0;
|
||||
|
||||
protected $API;
|
||||
protected $connection;
|
||||
protected $datacenter;
|
||||
|
||||
public function __construct($API, $datacenter)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->datacenter = $datacenter;
|
||||
$this->connection = $API->datacenter->sockets[$datacenter];
|
||||
}
|
||||
|
||||
public function start()
|
||||
{
|
||||
if ($this->count) {
|
||||
$this->API->logger->logger("NOT entering check loop in DC {$this->datacenter} with running count {$this->count}", Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
Promise\rethrow($this->call($this->loop()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function exitedLoop()
|
||||
{
|
||||
$this->count--;
|
||||
}
|
||||
|
||||
public function startedLoop()
|
||||
{
|
||||
$this->count++;
|
||||
}
|
||||
|
||||
public function isRunning()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
}
|
76
src/danog/MadelineProto/Loop/Impl/ResumableSignalLoop.php
Normal file
76
src/danog/MadelineProto/Loop/Impl/ResumableSignalLoop.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Impl;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Loop;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Loop\ResumableLoopInterface;
|
||||
|
||||
/**
|
||||
* Resumable signal loop helper trait.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
abstract class ResumableSignalLoop extends SignalLoop implements ResumableLoopInterface
|
||||
{
|
||||
private $resume;
|
||||
private $resumeWatcher;
|
||||
|
||||
public function pause($time = null): Promise
|
||||
{
|
||||
if (!is_null($time)) {
|
||||
if ($time <= 0) {
|
||||
return new Success(0);
|
||||
} else {
|
||||
$resume = microtime(true) + $time;
|
||||
if ($this->resumeWatcher) {
|
||||
Loop::cancel($this->resumeWatcher);
|
||||
$this->resumeWatcher = null;
|
||||
}
|
||||
$this->resumeWatcher = Loop::delay($time * 1000, [$this, 'resume'], $resume);
|
||||
//var_dump("resume {$this->resumeWatcher} ".get_class($this)." DC {$this->datacenter} after ", ($time * 1000), $resume);
|
||||
}
|
||||
}
|
||||
$this->resume = new Deferred();
|
||||
|
||||
return $this->resume->promise();
|
||||
}
|
||||
|
||||
public function resume($watcherId = null, $expected = 0)
|
||||
{
|
||||
if ($this->resumeWatcher) {
|
||||
$storedWatcherId = $this->resumeWatcher;
|
||||
Loop::cancel($storedWatcherId);
|
||||
$this->resumeWatcher = null;
|
||||
if ($watcherId && $storedWatcherId !== $watcherId) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ($expected) {
|
||||
//var_dump("=======", "resume $watcherId ".get_class($this)." DC {$this->datacenter} diff ".(microtime(true) - $expected).": expected $expected, actual ".microtime(true));
|
||||
}
|
||||
if ($this->resume) {
|
||||
$resume = $this->resume;
|
||||
$this->resume = null;
|
||||
$resume->resolve();
|
||||
}
|
||||
}
|
||||
}
|
62
src/danog/MadelineProto/Loop/Impl/SignalLoop.php
Normal file
62
src/danog/MadelineProto/Loop/Impl/SignalLoop.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
/**
|
||||
* Loop helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop\Impl;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Loop\SignalLoopInterface;
|
||||
|
||||
/**
|
||||
* Signal loop helper trait.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
abstract class SignalLoop extends Loop implements SignalLoopInterface
|
||||
{
|
||||
private $signalDeferred;
|
||||
|
||||
public function signal($what)
|
||||
{
|
||||
if ($this->signalDeferred) {
|
||||
$deferred = $this->signalDeferred;
|
||||
$this->signalDeferred = null;
|
||||
if ($what instanceof \Exception || $what instanceof \Throwable) {
|
||||
$deferred->fail($what);
|
||||
} else {
|
||||
$deferred->resolve($what);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function waitSignal(Promise $promise): Promise
|
||||
{
|
||||
$this->signalDeferred = new Deferred();
|
||||
$dpromise = $this->signalDeferred->promise();
|
||||
|
||||
$promise->onResolve(function () use ($promise) {
|
||||
if ($this->signalDeferred !== null) {
|
||||
$deferred = $this->signalDeferred;
|
||||
$this->signalDeferred = null;
|
||||
$deferred->resolve($promise);
|
||||
}
|
||||
});
|
||||
|
||||
return $dpromise;
|
||||
}
|
||||
}
|
41
src/danog/MadelineProto/Loop/LoopInterface.php
Normal file
41
src/danog/MadelineProto/Loop/LoopInterface.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* Loop interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop;
|
||||
|
||||
/**
|
||||
* Loop interface.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface LoopInterface
|
||||
{
|
||||
/**
|
||||
* Start the loop.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start();
|
||||
|
||||
/**
|
||||
* The actual loop.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loop(): \Generator;
|
||||
}
|
45
src/danog/MadelineProto/Loop/ResumableLoopInterface.php
Normal file
45
src/danog/MadelineProto/Loop/ResumableLoopInterface.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Resumable loop interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop;
|
||||
|
||||
use Amp\Promise;
|
||||
|
||||
/**
|
||||
* Resumable loop interface.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface ResumableLoopInterface extends LoopInterface
|
||||
{
|
||||
/**
|
||||
* Pause the loop.
|
||||
*
|
||||
* @param int $time For how long to pause the loop
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function pause($time = null): Promise;
|
||||
|
||||
/**
|
||||
* Resume the loop.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resume();
|
||||
}
|
47
src/danog/MadelineProto/Loop/SignalLoopInterface.php
Normal file
47
src/danog/MadelineProto/Loop/SignalLoopInterface.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Signal loop interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Loop;
|
||||
|
||||
use Amp\Promise;
|
||||
|
||||
/**
|
||||
* Signal loop interface.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface SignalLoopInterface extends LoopInterface
|
||||
{
|
||||
/**
|
||||
* Resolve the promise or return|throw the signal.
|
||||
*
|
||||
* @param Promise $promise The origin promise
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function waitSignal(Promise $promise): Promise;
|
||||
|
||||
/**
|
||||
* Send a signal to the the loop.
|
||||
*
|
||||
* @param Exception|any $data Signal to send
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function signal($data);
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Lua module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -27,6 +33,8 @@ class Lua
|
||||
}
|
||||
$this->MadelineProto = $MadelineProto;
|
||||
$this->MadelineProto->settings['updates']['handle_updates'] = true;
|
||||
$this->MadelineProto->API->datacenter->sockets[$this->MadelineProto->settings['connection_settings']['default_dc']]->startUpdateLoop();
|
||||
|
||||
$this->script = $script;
|
||||
$this->__wakeup();
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* AckHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
@ -26,24 +32,47 @@ trait AckHandler
|
||||
|
||||
return false;
|
||||
}
|
||||
//$this->logger->logger("Ack-ed ".$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_']." with message ID $message_id on DC $datacenter");
|
||||
/*
|
||||
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body'])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id]);
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['ack'] = true;
|
||||
public function got_response_for_outgoing_message_id($message_id, $datacenter)
|
||||
{
|
||||
// The server acknowledges that it received my message
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) {
|
||||
$this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body'])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['body']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function ack_incoming_message_id($message_id, $datacenter)
|
||||
{
|
||||
// I let the server know that I received its message
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) {
|
||||
$this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of incomgoing messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
|
||||
//throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
|
||||
}
|
||||
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") {
|
||||
// || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
|
||||
return;
|
||||
$this->logger->logger("WARNING: Couldn't find message id ".$message_id.' in the array of incoming messages. Maybe try to increase its size?', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
/*if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") {
|
||||
// || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
|
||||
return;
|
||||
}*/
|
||||
$this->datacenter->sockets[$datacenter]->ack_queue[$message_id] = $message_id;
|
||||
//$this->object_call('msgs_ack', ['msg_ids' => [$message_id]], ['datacenter' => $datacenter]);
|
||||
|
||||
return true;
|
||||
//$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* AuthKeyHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
@ -21,7 +27,10 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
trait AuthKeyHandler
|
||||
{
|
||||
public function create_auth_key($expires_in, $datacenter)
|
||||
private $init_auth_dcs = [];
|
||||
private $pending_auth = false;
|
||||
|
||||
public function create_auth_key_async($expires_in, $datacenter): \Generator
|
||||
{
|
||||
$req_pq = strpos($datacenter, 'cdn') ? 'req_pq' : 'req_pq_multi';
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
@ -34,18 +43,18 @@ trait AuthKeyHandler
|
||||
* @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
|
||||
* 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
|
||||
* 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 = $this->random(16);
|
||||
$ResPQ = $this->method_call($req_pq, ['nonce' => $nonce], ['datacenter' => $datacenter]);
|
||||
$ResPQ = yield $this->method_call_async_read($req_pq, ['nonce' => $nonce], ['datacenter' => $datacenter]);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
@ -135,7 +144,7 @@ trait AuthKeyHandler
|
||||
}
|
||||
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: ".$p->multiply($q));
|
||||
throw new \danog\MadelineProto\SecurityException("Couldn't compute p and q, install prime.madelineproto.xyz to fix. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: ".$p->multiply($q));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,7 +160,7 @@ trait AuthKeyHandler
|
||||
$p_bytes = $p->toBytes();
|
||||
$q_bytes = $q->toBytes();
|
||||
$new_nonce = $this->random(32);
|
||||
$data_unserialized = ['pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in];
|
||||
$data_unserialized = ['pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in, 'dc' => preg_replace('|_.*|', '', $datacenter)];
|
||||
$p_q_inner_data = $this->serialize_object(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -167,22 +176,22 @@ trait AuthKeyHandler
|
||||
* 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
|
||||
* 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 with server_DH_params_fail
|
||||
* string $encrypted_answer : Return this value if server responds with server_DH_params_ok
|
||||
* 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 with server_DH_params_fail
|
||||
* string $encrypted_answer : Return this value if server responds with 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' => $key->fp, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
|
||||
$server_dh_params = yield $this->method_call_async_read('req_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'p' => $p_bytes, 'q' => $q_bytes, 'public_key_fingerprint' => $key->fp, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
@ -223,12 +232,12 @@ trait AuthKeyHandler
|
||||
* ***********************************************************************
|
||||
* 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
|
||||
* 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->deserialize($answer, ['type' => '']);
|
||||
@ -280,10 +289,10 @@ trait AuthKeyHandler
|
||||
* 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
|
||||
* 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->serialize_object(['type' => 'client_DH_inner_data'], ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'retry_id' => $retry_id, 'g_b' => $g_b_str], 'client_DH_inner_data');
|
||||
@ -300,19 +309,19 @@ trait AuthKeyHandler
|
||||
* 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
|
||||
* 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 with dh_gen_ok
|
||||
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_retry
|
||||
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_fail
|
||||
* 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 with dh_gen_ok
|
||||
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_retry
|
||||
* int128 $new_nonce_hash2 : Return this value if server responds with 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], ['datacenter' => $datacenter]);
|
||||
$Set_client_DH_params_answer = yield $this->method_call_async_read('set_client_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Generate auth_key
|
||||
@ -380,13 +389,7 @@ trait AuthKeyHandler
|
||||
$this->logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...', \danog\MadelineProto\Logger::WARNING);
|
||||
$req_pq = $req_pq === 'req_pq_multi' ? 'req_pq' : 'req_pq_multi';
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->rpc === 'RPC_CALL_FAIL') {
|
||||
throw $e;
|
||||
}
|
||||
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
|
||||
} finally {
|
||||
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming = [];
|
||||
}
|
||||
}
|
||||
if (strpos($datacenter, 'cdn') === false) {
|
||||
@ -403,11 +406,11 @@ trait AuthKeyHandler
|
||||
*/
|
||||
$this->logger->logger('Executing g_a check (1/2)...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
if ($g_a->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g_a->compare($p->subtract(\danog\MadelineProto\Magic::$one)) >= 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('g_a is invalid (1 < g_a < dh_prime - 1 is false).');
|
||||
throw new \danog\MadelineProto\SecurityException('g_a is invalid (1 < g_a < p - 1 is false).');
|
||||
}
|
||||
$this->logger->logger('Executing g_a check (2/2)...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
if ($g_a->compare(\danog\MadelineProto\Magic::$twoe1984) < 0 || $g_a->compare($p->subtract(\danog\MadelineProto\Magic::$twoe1984)) >= 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < gA < dh_prime - 2^1984 is false).');
|
||||
throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < g_a < p - 2^1984 is false).');
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -434,9 +437,9 @@ trait AuthKeyHandler
|
||||
/*
|
||||
$this->logger->logger('Executing p/g checks (2/3)...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
if (!$p->subtract(\danog\MadelineProto\Magic::$one)->divide(\danog\MadelineProto\Magic::$two)[0]->isPrime()) {
|
||||
throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime ((p - 1) / 2 isn't a prime).");
|
||||
throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime ((p - 1) / 2 isn't a prime).");
|
||||
}
|
||||
*/
|
||||
*/
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of p
|
||||
@ -480,7 +483,7 @@ trait AuthKeyHandler
|
||||
return $this->dh_config = $dh_config;
|
||||
}
|
||||
|
||||
public function bind_temp_auth_key($expires_in, $datacenter)
|
||||
public function bind_temp_auth_key_async($expires_in, $datacenter)
|
||||
{
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
@ -491,14 +494,14 @@ trait AuthKeyHandler
|
||||
$perm_auth_key_id = $this->datacenter->sockets[$datacenter]->auth_key['id'];
|
||||
$temp_session_id = $this->datacenter->sockets[$datacenter]->session_id;
|
||||
$message_data = $this->serialize_object(['type' => 'bind_auth_key_inner'], ['nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bind_temp_auth_key_inner');
|
||||
$message_id = $this->generate_message_id($datacenter);
|
||||
$message_id = $this->datacenter->sockets[$datacenter]->generate_message_id();
|
||||
$seq_no = 0;
|
||||
$encrypted_data = $this->random(16).$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
|
||||
$message_key = substr(sha1($encrypted_data, true), -16);
|
||||
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
|
||||
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);
|
||||
$encrypted_message = $this->datacenter->sockets[$datacenter]->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['message_id' => $message_id, 'datacenter' => $datacenter]);
|
||||
$res = yield $this->method_call_async_read('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['msg_id' => $message_id, 'datacenter' => $datacenter]);
|
||||
if ($res === true) {
|
||||
$this->logger->logger('Successfully binded temporary and permanent authorization keys, DC '.$datacenter, \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
@ -510,9 +513,6 @@ trait AuthKeyHandler
|
||||
$this->logger->logger('An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
|
||||
} finally {
|
||||
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming = [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,70 +522,130 @@ trait AuthKeyHandler
|
||||
// Creates authorization keys
|
||||
public function init_authorization()
|
||||
{
|
||||
return $this->wait($this->init_authorization_async());
|
||||
}
|
||||
|
||||
public function init_authorization_async()
|
||||
{
|
||||
if ($this->pending_auth) {
|
||||
return;
|
||||
}
|
||||
$initing = $this->initing_authorization;
|
||||
|
||||
$this->initing_authorization = true;
|
||||
$this->updates_state['sync_loading'] = true;
|
||||
$this->postpone_updates = true;
|
||||
|
||||
try {
|
||||
$dcs = [];
|
||||
$postpone = [];
|
||||
foreach ($this->datacenter->sockets as $id => $socket) {
|
||||
if ($socket->session_id === null) {
|
||||
$socket->session_id = $this->random(8);
|
||||
$socket->session_in_seq_no = 0;
|
||||
$socket->session_out_seq_no = 0;
|
||||
if (strpos($id, 'media') !== false) {
|
||||
$oid = intval($id);
|
||||
if (isset($dcs[$oid])) {
|
||||
$postpone[$id] = $socket;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$cdn = strpos($id, 'cdn');
|
||||
$media = strpos($id, 'media');
|
||||
if (isset($this->init_auth_dcs[$id])) {
|
||||
$this->pending_auth = true;
|
||||
continue;
|
||||
}
|
||||
$dcs[$id] = function () use ($id, $socket) {
|
||||
return $this->init_authorization_socket($id, $socket);
|
||||
};
|
||||
}
|
||||
yield array_shift($dcs)();
|
||||
foreach ($dcs as &$dc) {
|
||||
$dc = $dc();
|
||||
}
|
||||
yield $dcs;
|
||||
|
||||
if ($socket->temp_auth_key === null || $socket->auth_key === null) {
|
||||
$dc_config_number = isset($this->settings['connection_settings'][$id]) ? $id : 'all';
|
||||
if ($socket->auth_key === null && !$cdn && !$media) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_perm_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->auth_key = $this->create_auth_key(-1, $id);
|
||||
$socket->authorized = false;
|
||||
} elseif ($socket->auth_key === null && $media) {
|
||||
$socket->auth_key = $this->datacenter->sockets[intval($id)]->auth_key;
|
||||
$socket->authorized = &$this->datacenter->sockets[intval($id)]->authorized;
|
||||
}
|
||||
if ($media) {
|
||||
$socket->authorized = &$this->datacenter->sockets[intval($id)]->authorized;
|
||||
}
|
||||
if ($this->settings['connection_settings'][$dc_config_number]['pfs']) {
|
||||
if (!$cdn) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->temp_auth_key = null;
|
||||
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
$this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
$config = $this->method_call('help.getConfig', [], ['datacenter' => $id]);
|
||||
$this->sync_authorization($id);
|
||||
$this->get_config($config);
|
||||
} elseif ($socket->temp_auth_key === null) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
}
|
||||
} else {
|
||||
if (!$cdn) {
|
||||
$socket->temp_auth_key = $socket->auth_key;
|
||||
$config = $this->method_call('help.getConfig', [], ['datacenter' => $id]);
|
||||
$this->sync_authorization($id);
|
||||
$this->get_config($config);
|
||||
} elseif ($socket->temp_auth_key === null) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
}
|
||||
}
|
||||
} elseif (!$cdn) {
|
||||
$this->sync_authorization($id);
|
||||
}
|
||||
foreach ($postpone as $id => $socket) {
|
||||
yield $this->init_authorization_socket($id, $socket);
|
||||
}
|
||||
//foreach ($dcs as $dc) { yield $dc; }
|
||||
|
||||
if ($this->pending_auth && empty($this->init_auth_dcs)) {
|
||||
$this->pending_auth = false;
|
||||
yield $this->init_authorization_async();
|
||||
}
|
||||
} finally {
|
||||
$this->pending_auth = false;
|
||||
$this->postpone_updates = false;
|
||||
$this->initing_authorization = false;
|
||||
$this->initing_authorization = $initing;
|
||||
$this->updates_state['sync_loading'] = false;
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
}
|
||||
|
||||
public function sync_authorization($id)
|
||||
public function init_authorization_socket($id, $socket)
|
||||
{
|
||||
$this->init_auth_dcs[$id] = true;
|
||||
|
||||
try {
|
||||
if ($socket->session_id === null) {
|
||||
$socket->session_id = $this->random(8);
|
||||
$socket->session_in_seq_no = 0;
|
||||
$socket->session_out_seq_no = 0;
|
||||
}
|
||||
$cdn = strpos($id, 'cdn');
|
||||
$media = strpos($id, 'media');
|
||||
|
||||
if ($socket->temp_auth_key === null || $socket->auth_key === null) {
|
||||
$dc_config_number = isset($this->settings['connection_settings'][$id]) ? $id : 'all';
|
||||
if ($socket->auth_key === null && !$cdn && !$media) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_perm_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->auth_key = yield $this->create_auth_key_async(-1, $id);
|
||||
$socket->authorized = false;
|
||||
} elseif ($socket->auth_key === null && $media) {
|
||||
$socket->auth_key = $this->datacenter->sockets[intval($id)]->auth_key;
|
||||
$socket->authorized = &$this->datacenter->sockets[intval($id)]->authorized;
|
||||
}
|
||||
if ($media) {
|
||||
$socket->authorized = &$this->datacenter->sockets[intval($id)]->authorized;
|
||||
}
|
||||
if ($this->settings['connection_settings'][$dc_config_number]['pfs']) {
|
||||
if (!$cdn) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
//$authorized = $socket->authorized;
|
||||
//$socket->authorized = false;
|
||||
|
||||
$socket->temp_auth_key = null;
|
||||
$socket->temp_auth_key = yield $this->create_auth_key_async($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
yield $this->bind_temp_auth_key_async($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
|
||||
//$socket->authorized = $authorized;
|
||||
|
||||
$config = yield $this->method_call_async_read('help.getConfig', [], ['datacenter' => $id]);
|
||||
|
||||
yield $this->sync_authorization_async($id);
|
||||
yield $this->get_config_async($config);
|
||||
} elseif ($socket->temp_auth_key === null) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->temp_auth_key = yield $this->create_auth_key_async($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
}
|
||||
} else {
|
||||
if (!$cdn) {
|
||||
$socket->temp_auth_key = $socket->auth_key;
|
||||
$config = yield $this->method_call_async_read('help.getConfig', [], ['datacenter' => $id]);
|
||||
yield $this->sync_authorization_async($id);
|
||||
yield $this->get_config_async($config);
|
||||
} elseif ($socket->temp_auth_key === null) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->temp_auth_key = yield $this->create_auth_key_async($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
}
|
||||
}
|
||||
} elseif (!$cdn) {
|
||||
yield $this->sync_authorization_async($id);
|
||||
}
|
||||
} finally {
|
||||
unset($this->init_auth_dcs[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
public function sync_authorization_async($id)
|
||||
{
|
||||
if (!isset($this->datacenter->sockets[$id])) {
|
||||
return false;
|
||||
@ -599,8 +659,8 @@ trait AuthKeyHandler
|
||||
if ($authorized_socket->temp_auth_key !== null && $authorized_socket->auth_key !== null && $authorized_socket->authorized === true && $this->authorized === self::LOGGED_IN && $socket->authorized === false && strpos($authorized_dc_id, 'cdn') === false) {
|
||||
try {
|
||||
$this->logger->logger('Trying to copy authorization from dc '.$authorized_dc_id.' to dc '.$id);
|
||||
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => preg_replace('|_.*|', '', $id)], ['datacenter' => $authorized_dc_id]);
|
||||
$authorization = $this->method_call('auth.importAuthorization', $exported_authorization, ['datacenter' => $id]);
|
||||
$exported_authorization = yield $this->method_call_async_read('auth.exportAuthorization', ['dc_id' => preg_replace('|_.*|', '', $id)], ['datacenter' => $authorized_dc_id]);
|
||||
$authorization = yield $this->method_call_async_read('auth.importAuthorization', $exported_authorization, ['datacenter' => $id]);
|
||||
$socket->authorized = true;
|
||||
break;
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
|
@ -1,96 +1,109 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* CallHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Async\Parameters;
|
||||
use function Amp\call;
|
||||
use function Amp\Promise\all;
|
||||
|
||||
/**
|
||||
* Manages method and object calls.
|
||||
*/
|
||||
trait CallHandler
|
||||
{
|
||||
public $wrapper;
|
||||
|
||||
public function method_call($method, $args = [], $aargs = ['message_id' => null, 'heavy' => false])
|
||||
/*
|
||||
public function select()
|
||||
{
|
||||
if (!is_array($args)) {
|
||||
throw new \danog\MadelineProto\Exception("Arguments aren't an array.");
|
||||
}
|
||||
if (!is_array($aargs)) {
|
||||
throw new \danog\MadelineProto\Exception("Additonal arguments aren't an array.");
|
||||
}
|
||||
if (isset($args['id']['_']) && isset($args['id']['dc_id']) && $args['id']['_'] === 'inputBotInlineMessageID') {
|
||||
$aargs['datacenter'] = $args['id']['dc_id'];
|
||||
}
|
||||
if (!isset($aargs['datacenter'])) {
|
||||
throw new \danog\MadelineProto\Exception('No datacenter provided');
|
||||
}
|
||||
if (isset($aargs['apifactory']) && array_key_exists($method, self::DISALLOWED_METHODS)) {
|
||||
throw new \danog\MadelineProto\Exception(self::DISALLOWED_METHODS[$method], 0, null, 'MadelineProto', 1);
|
||||
}
|
||||
if ($this->wrapper instanceof \danog\MadelineProto\API && isset($this->wrapper->session) && !is_null($this->wrapper->session) && time() - $this->wrapper->serialized > $this->settings['serialization']['serialization_interval']) {
|
||||
$this->logger->logger("Didn't serialize in a while, doing that now...");
|
||||
$this->wrapper->serialize($this->wrapper->session);
|
||||
}
|
||||
if (isset($aargs['file']) && $aargs['file'] && isset($this->datacenter->sockets[$aargs['datacenter'].'_media'])) {
|
||||
\danog\MadelineProto\Logger::log('Using media DC');
|
||||
$aargs['datacenter'] .= '_media';
|
||||
}
|
||||
if (isset($args['message']) && is_string($args['message']) && $this->mb_strlen($args['message']) > $this->config['message_length_max']) {
|
||||
$arg_chunks = $this->split_to_chunks($args);
|
||||
$args = array_shift($arg_chunks);
|
||||
}
|
||||
$args = $this->botAPI_to_MTProto($args);
|
||||
if (isset($args['ping_id']) && is_int($args['ping_id'])) {
|
||||
$args['ping_id'] = $this->pack_signed_long($args['ping_id']);
|
||||
}
|
||||
if (isset($args['chat_id']) && in_array($method, ['messages.addChatUser', 'messages.deleteChatUser', 'messages.editChatAdmin', 'messages.editChatPhoto', 'messages.editChatTitle', 'messages.getFullChat', 'messages.exportChatInvite', 'messages.editChatAdmin', 'messages.migrateChat']) && (!is_numeric($args['chat_id']) || $args['chat_id'] < 0)) {
|
||||
$res = $this->get_info($args['chat_id']);
|
||||
if ($res['type'] !== 'chat') {
|
||||
throw new \danog\MadelineProto\Exception('chat_id is not a chat id (only normal groups allowed, not supergroups)!');
|
||||
$result = [];
|
||||
|
||||
try {
|
||||
/*
|
||||
if ($this->is_http($this->settings['connection_settings']['default_dc']) || $this->altervista) {
|
||||
$this->logger->logger("Initial HTTP short poll");
|
||||
$waiting = $this->datacenter->select(0.1);
|
||||
$result = $this->handle_select($waiting, $result);
|
||||
}/
|
||||
$tries = 10; // TODO add setting
|
||||
$this->logger->logger('Long poll');
|
||||
$t = microtime(true);
|
||||
$waiting = $this->datacenter->select();
|
||||
$t = microtime(true) - $t;
|
||||
$this->logger->logger("Long poll took $t");
|
||||
|
||||
$result = $this->handle_select($waiting, $result);
|
||||
|
||||
do {
|
||||
$this->logger->logger('Short poll');
|
||||
$waiting = $this->datacenter->select($this->is_http($this->settings['connection_settings']['default_dc']) || $this->altervista ? $this->settings['connection_settings']['all']['timeout'] / 10 : true);
|
||||
$result = $this->handle_select($waiting, $result);
|
||||
} while ($tries-- && $waiting);
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
$this->logger->logger('Nothing in the socket while selecting', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
foreach ($this->datacenter->sockets as $dc => $socket) {
|
||||
$this->close_and_reopen($dc);
|
||||
$this->send_messages($dc);
|
||||
}
|
||||
$args['chat_id'] = $res['chat_id'];
|
||||
}
|
||||
if (in_array($method, ['messages.setEncryptedTyping', 'messages.readEncryptedHistory', 'messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService', 'messages.receivedQueue'])) {
|
||||
$aargs['queue'] = 'secret';
|
||||
}
|
||||
if (isset($aargs['queue'])) {
|
||||
$queue = $aargs['queue'];
|
||||
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue])) {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue] = [];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public $did = [];
|
||||
|
||||
public function handle_select($waiting, $result)
|
||||
{
|
||||
foreach ($waiting as $dc) {
|
||||
$error = $this->recv_message($dc);
|
||||
if ($error !== true) {
|
||||
$this->close_and_reopen($dc);
|
||||
if ($error === -404) {
|
||||
if ($this->datacenter->sockets[$dc]->temp_auth_key !== null) {
|
||||
$this->logger->logger('WARNING: Resetting auth key...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->sockets[$dc]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
$result[$dc] = $this->handle_messages($dc) && (isset($result[$dc]) ? $result[$dc] : true);
|
||||
if (($this->is_http($dc) || $this->altervista) && $this->datacenter->sockets[$dc]->new_outgoing) {
|
||||
$this->send_messages($dc);
|
||||
}
|
||||
unset($aargs['queue']);
|
||||
}
|
||||
if (isset($aargs['serialized'])) {
|
||||
$serialized = $args['serialized'];
|
||||
} else {
|
||||
$serialized = $this->serialize_method($method, $args);
|
||||
}
|
||||
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] === false)) {
|
||||
$this->logger->logger(sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $method), \danog\MadelineProto\Logger::NOTICE);
|
||||
$serialized = $this->serialize_method('invokeWithLayer', ['layer' => $this->settings['tl_schema']['layer'], 'query' => $this->serialize_method('initConnection', ['api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash'], 'device_model' => strpos($aargs['datacenter'], 'cdn') === false ? $this->settings['app_info']['device_model'] : 'n/a', 'system_version' => strpos($aargs['datacenter'], 'cdn') === false ? $this->settings['app_info']['system_version'] : 'n/a', 'app_version' => $this->settings['app_info']['app_version'], 'system_lang_code' => $this->settings['app_info']['lang_code'], 'lang_code' => $this->settings['app_info']['lang_code'], 'lang_pack' => '', 'query' => $serialized])]);
|
||||
}
|
||||
$content_related = $this->content_related($method);
|
||||
$type = $this->methods->find_by_method($method)['type'];
|
||||
if (isset($queue)) {
|
||||
$serialized = $this->serialize_method('invokeAfterMsgs', ['msg_ids' => $this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue], 'query' => $serialized]);
|
||||
}
|
||||
if ($this->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($serialized)) > $this->settings['requests']['gzip_encode_if_gt'] && ($g = strlen($gzipped = gzencode($serialized))) < $l) {
|
||||
$serialized = $this->serialize_object(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data');
|
||||
$this->logger->logger('Using GZIP compression for '.$method.', saved '.($l - $g).' bytes of data, reduced call size by '.$g * 100 / $l.'%', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
$to_ack = [];
|
||||
$last_recv = $this->datacenter->sockets[$aargs['datacenter']]->last_recv;
|
||||
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
|
||||
|
||||
return $result;
|
||||
}
|
||||
public function iorun($updates)
|
||||
{
|
||||
do {
|
||||
if ($updates && time() - $this->last_getdifference > $this->settings['updates']['getdifference_interval']) {
|
||||
$this->get_updates_difference();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($canunset = !$this->updates_state['sync_loading']) {
|
||||
$this->updates_state['sync_loading'] = true;
|
||||
}
|
||||
@ -101,240 +114,395 @@ trait CallHandler
|
||||
$this->postpone_pwrchat = true;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->logger->logger('Calling method (try number '.$count.' for '.$method.')...', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
|
||||
if (isset($message_id)) {
|
||||
$this->logger->logger('Clearing old method call', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id])) {
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id])) {
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
|
||||
}
|
||||
}
|
||||
if (($this->is_http($this->settings['connection_settings']['default_dc']) || $this->altervista) && $updates) {
|
||||
$this->send_messages($this->settings['connection_settings']['default_dc']);
|
||||
}
|
||||
foreach ($this->datacenter->sockets as $id => $datacenter) {
|
||||
if ($datacenter->pending_outgoing) {
|
||||
$this->send_messages($id);
|
||||
}
|
||||
}
|
||||
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->object_queue[] = ['_' => $method, 'body' => $serialized, 'content_related' => $content_related, 'msg_id' => $message_id = isset($aargs['message_id']) ? $aargs['message_id'] : $this->generate_message_id($aargs['datacenter'])];
|
||||
if (count($to_ack = $this->datacenter->sockets[$aargs['datacenter']]->ack_queue)) {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->object_queue[] = ['_' => 'msgs_ack', 'body' => $this->serialize_object(['type' => 'msgs_ack'], ['msg_ids' => $this->datacenter->sockets[$aargs['datacenter']]->ack_queue], 'msgs_ack'), 'content_related' => false, 'msg_id' => $this->generate_message_id($aargs['datacenter'])];
|
||||
$this->logger->logger('Polling for ' . ($updates ? 'updates' : 'replies') . ': selecting', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$t = microtime(true);
|
||||
$only_updates = $this->select();
|
||||
$t = microtime(true) - $t;
|
||||
$this->logger->logger('Polling for ' . ($updates ? 'updates' : 'replies') . ': selecting took ' . $t, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$response_result = $this->has_pending_calls();
|
||||
|
||||
$repeat = 0;
|
||||
foreach ($this->datacenter->sockets as $id => $datacenter) {
|
||||
if ($updates) {
|
||||
if (isset($only_updates[$id])) {
|
||||
if ($only_updates[$id]) {
|
||||
$this->logger->logger("Polling for updates: got only updates for DC $id", \danog\MadelineProto\Logger::VERBOSE);
|
||||
} else {
|
||||
$this->logger->logger("Polling for updates: got also RPC replies for DC $id", \danog\MadelineProto\Logger::NOTICE);
|
||||
}
|
||||
if ($response_result[$id]) {
|
||||
$this->logger->logger("Polling for updates: still pending requests, resending for DC $id", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->send_messages($id);
|
||||
}
|
||||
} else {
|
||||
if ($response_result[$id] || $id === $this->settings['connection_settings']['default_dc']) {
|
||||
$this->logger->logger("Polling for updates: got nothing for DC $id", \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
if ($this->is_http($id) || $this->altervista) {
|
||||
$this->logger->logger("Polling for updates: closing and reopening DC $id since we're on HTTP, and we polled");
|
||||
$this->close_and_reopen($id);
|
||||
$datacenter->last_http_wait = 0;
|
||||
$repeat |= 1;
|
||||
$this->logger->logger("Polling for updates: and now repolling for DC $id");
|
||||
$this->send_messages($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->is_http($aargs['datacenter']) && $method !== 'http_wait') {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->object_queue[] = ['_' => 'http_wait', 'body' => $this->serialize_method('http_wait', ['max_wait' => 500, 'wait_after' => 150, 'max_delay' => 500]), 'content_related' => false, 'msg_id' => $this->generate_message_id($aargs['datacenter'])];
|
||||
}
|
||||
$this->send_messages($aargs['datacenter']);
|
||||
} else {
|
||||
$this->send_unencrypted_message($method, $serialized, $message_id = isset($aargs['message_id']) ? $aargs['message_id'] : $this->generate_message_id($aargs['datacenter']), $aargs['datacenter']);
|
||||
$aargs['message_id'] = $message_id;
|
||||
}
|
||||
if (isset($only_updates[$id])) {
|
||||
if ($only_updates[$id]) {
|
||||
$this->logger->logger("Polling for replies: got only updates for DC $id", \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
if (isset($queue)) {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue][] = $message_id;
|
||||
if (count($this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue]) > $this->settings['msg_array_limit']['call_queue']) {
|
||||
reset($this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue]);
|
||||
$key = key($this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue]);
|
||||
if ($key[0] === "\0") {
|
||||
$key = $key;
|
||||
}
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue][$key]);
|
||||
}
|
||||
}
|
||||
if ($method === 'http_wait' || isset($aargs['noResponse']) && $aargs['noResponse']) {
|
||||
return true;
|
||||
}
|
||||
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id] = ['msg_id' => $message_id, 'method' => $method, 'type' => $type];
|
||||
$res_count = 0;
|
||||
$server_answer = null;
|
||||
$update_count = 0;
|
||||
$only_updates = false;
|
||||
$response_tries = $this->settings['max_tries']['response'] + 1;
|
||||
if ($last_recv) {
|
||||
$additional = (int) floor((time() - $last_recv) / 10);
|
||||
if ($additional > $this->settings['max_tries']['response'] * 2) {
|
||||
$additional = $this->settings['max_tries']['response'] * 2;
|
||||
}
|
||||
$response_tries += $additional;
|
||||
}
|
||||
while ($server_answer === null && $res_count++ < $response_tries) {
|
||||
// Loop until we get a response, loop for a max of $this->settings['max_tries']['response'] times
|
||||
try {
|
||||
$this->logger->logger('Getting response (try number '.$res_count.' for '.$method.')...', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'])) {
|
||||
// Checks if I have received the response to the called method, if not continue looping
|
||||
if ($only_updates) {
|
||||
if ($update_count > 50) {
|
||||
$update_count = 0;
|
||||
} else {
|
||||
$res_count--;
|
||||
$update_count++;
|
||||
}
|
||||
if ($response_result[$id]) {
|
||||
$this->logger->logger("Polling for replies: still pending requests, repolling for DC $id", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->send_messages($id);
|
||||
$repeat |= 1;
|
||||
} else {
|
||||
$this->logger->logger("Polling for replies: got all RPC replies for DC $id", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
} else {
|
||||
$server_answer = $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'] = '';
|
||||
break;
|
||||
if ($response_result[$id]) {
|
||||
$this->logger->logger("Polling for replies: still pending requests, repolling for DC $id", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->send_messages($id);
|
||||
} else {
|
||||
$this->logger->logger("Polling for replies: got all RPC replies for DC $id", \danog\MadelineProto\Logger::NOTICE);
|
||||
}
|
||||
}
|
||||
if (($error = $this->recv_message($aargs['datacenter'])) !== true) {
|
||||
if ($error === -404) {
|
||||
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
|
||||
$this->logger->logger('WARNING: Resetting auth key...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
} else {
|
||||
if ($response_result[$id]) {
|
||||
$this->logger->logger("Polling for replies: got nothing for DC $id", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->logger->logger("Polling for replies: closing and reopening DC $id", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->close_and_reopen($id);
|
||||
$datacenter->last_http_wait = 0;
|
||||
$repeat |= 1;
|
||||
$this->logger->logger("Polling for replies: resending for DC $id", \danog\MadelineProto\Logger::WARNING);
|
||||
$this->send_messages($id);
|
||||
} else {
|
||||
$this->logger->logger("Polling for replies: got all RPC replies for DC $id", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->logger->logger('Running guzzle promise queue');
|
||||
\GuzzleHttp\Promise\queue()->run();
|
||||
|
||||
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
if ($repeat) {
|
||||
$this->logger->logger('Repeat iowait');
|
||||
}
|
||||
} while ($repeat);
|
||||
|
||||
if ($canunset) {
|
||||
$this->updates_state['sync_loading'] = false;
|
||||
}
|
||||
if ($canunsetpostponepwrchat) {
|
||||
$this->postpone_pwrchat = false;
|
||||
$this->handle_pending_pwrchat();
|
||||
}
|
||||
if ($canunsetpostponeupdates) {
|
||||
$this->postpone_updates = false;
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
public function has_pending_calls()
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->datacenter->sockets as $id => $socket) {
|
||||
$result[$id] = $this->has_pending_calls_dc($id);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function has_pending_calls_dc($datacenter)
|
||||
{
|
||||
//$result = 0;
|
||||
$dc_config_number = isset($this->settings['connection_settings'][$datacenter]) ? $datacenter : 'all';
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $message_id) {
|
||||
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['sent']) && ($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['sent'] + $this->settings['connection_settings'][$dc_config_number]['timeout'] < time()) && ($this->datacenter->sockets[$datacenter]->temp_auth_key === null) === (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['unencrypted']) && $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['unencrypted']) && $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'] !== 'msgs_state_req') {
|
||||
return true;
|
||||
//$result |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false; //(bool) $result;
|
||||
}
|
||||
|
||||
public function check_pending_calls()
|
||||
{
|
||||
foreach ($this->datacenter->sockets as $datacenter => $socket) {
|
||||
$this->check_pending_calls_dc($datacenter);
|
||||
}
|
||||
}
|
||||
|
||||
public function check_pending_calls_dc($datacenter)
|
||||
{
|
||||
if (!empty($this->datacenter->sockets[$datacenter]->new_outgoing)) {
|
||||
if ($this->has_pending_calls_dc($datacenter)) {
|
||||
if ($this->datacenter->sockets[$datacenter]->temp_auth_key !== null) {
|
||||
$message_ids = array_values($this->datacenter->sockets[$datacenter]->new_outgoing);
|
||||
$deferred = new \danog\MadelineProto\ImmediatePromise();
|
||||
$deferred->then(
|
||||
function ($result) use ($datacenter, $message_ids) {
|
||||
$reply = [];
|
||||
foreach (str_split($result['info']) as $key => $chr) {
|
||||
$message_id = $message_ids[$key];
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) {
|
||||
$this->logger->logger('Already got response for and forgot about message ID '.$this->unpack_signed_long($message_id));
|
||||
continue;
|
||||
}
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->new_outgoing[$message_id])) {
|
||||
$this->logger->logger('Already got response for '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message ID '.$this->unpack_signed_long($message_id));
|
||||
continue;
|
||||
}
|
||||
$chr = ord($chr);
|
||||
switch ($chr & 7) {
|
||||
case 0:
|
||||
$this->logger->logger('Wrong message status 0 for '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
$this->logger->logger('Message '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message ID '.$this->unpack_signed_long($message_id).' not received by server, resending...', \danog\MadelineProto\Logger::ERROR);
|
||||
$this->method_recall($message_id, $datacenter, false, true);
|
||||
break;
|
||||
case 4:
|
||||
if ($chr & 32) {
|
||||
$this->logger->logger('Message '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message ID '.$this->unpack_signed_long($message_id).' received by server and is being processed, waiting...', \danog\MadelineProto\Logger::ERROR);
|
||||
} elseif ($chr & 64) {
|
||||
$this->logger->logger('Message '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message ID '.$this->unpack_signed_long($message_id).' received by server and was already processed, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
} elseif ($chr & 128) {
|
||||
$this->logger->logger('Message '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message ID '.$this->unpack_signed_long($message_id).' received by server and was already sent, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
} else {
|
||||
$this->logger->logger('Message '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message ID '.$this->unpack_signed_long($message_id).' received by server, requesting reply...', \danog\MadelineProto\Logger::ERROR);
|
||||
$reply[] = $message_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
$only_updates = $this->handle_messages($aargs['datacenter']);
|
||||
// This method receives data from the socket, and parses stuff
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
|
||||
if (in_array($e->getMessage(), ['Resend query', 'I had to recreate the temporary authorization key', 'Got bad message notification']) || $e->getCode() === 404) {
|
||||
continue 2;
|
||||
}
|
||||
$this->logger->logger('An error getting response of method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->logger->logger('Full trace '.$e, \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
$last_error = 'Nothing in the socket';
|
||||
$this->logger->logger('An error getting response of method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...', \danog\MadelineProto\Logger::WARNING);
|
||||
$only_updates = false;
|
||||
if ($last_recv === $this->datacenter->sockets[$aargs['datacenter']]->last_recv) { // the socket is dead, resend request
|
||||
$this->close_and_reopen($aargs['datacenter']);
|
||||
if ($this->altervista) {
|
||||
continue 2;
|
||||
if ($reply) {
|
||||
$this->object_call('msg_resend_ans_req', ['msg_ids' => $reply], ['datacenter' => $datacenter, 'postpone' => true]);
|
||||
}
|
||||
$this->send_messages($datacenter);
|
||||
},
|
||||
function ($error) use ($datacenter) {
|
||||
throw $error;
|
||||
}
|
||||
);
|
||||
$this->logger->logger("Still missing something on DC $datacenter, sending state request", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->object_call('msgs_state_req', ['msg_ids' => $message_ids], ['datacenter' => $datacenter, 'promise' => $deferred]);
|
||||
} else {
|
||||
$dc_config_number = isset($this->settings['connection_settings'][$datacenter]) ? $datacenter : 'all';
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $message_id) {
|
||||
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['sent']) && $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['sent'] + $this->settings['connection_settings'][$dc_config_number]['timeout'] < time() && $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['unencrypted']) {
|
||||
$this->logger->logger('Still missing '.$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['_'].' with message id '.$this->unpack_signed_long($message_id)." on DC $datacenter, resending", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->method_recall($message_id, $datacenter, false, true);
|
||||
}
|
||||
//if ($this->datacenter->sockets[$aargs['datacenter']]->last_recv < time() - 1 && $this->is_http($aargs['datacenter'])) {
|
||||
// $this->close_and_reopen($aargs['datacenter']);
|
||||
// continue 2;
|
||||
//}
|
||||
continue; //2;
|
||||
}
|
||||
}
|
||||
if ($canunset) {
|
||||
$this->updates_state['sync_loading'] = false;
|
||||
}
|
||||
if ($canunsetpostponepwrchat) {
|
||||
$this->postpone_pwrchat = false;
|
||||
$this->handle_pending_pwrchat();
|
||||
}
|
||||
if ($canunsetpostponeupdates) {
|
||||
$this->postpone_updates = false;
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
foreach ($to_ack as $msg_id) {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$msg_id]['ack'] = true;
|
||||
if (isset($this->datacenter->sockets[$aargs['datacenter']]->ack_queue[$msg_id])) {
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->ack_queue[$msg_id]);
|
||||
}
|
||||
}
|
||||
if ($server_answer === null) {
|
||||
throw new \danog\MadelineProto\Exception("Couldn't get response");
|
||||
}
|
||||
if (!isset($server_answer['_'])) {
|
||||
return $server_answer;
|
||||
}
|
||||
switch ($server_answer['_']) {
|
||||
case 'rpc_error':
|
||||
$this->handle_rpc_error($server_answer, $aargs);
|
||||
break;
|
||||
case 'bad_server_salt':
|
||||
case 'bad_msg_notification':
|
||||
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$server_answer['error_code']], $server_answer['error_code']);
|
||||
case 'boolTrue':
|
||||
case 'boolFalse':
|
||||
$server_answer = $server_answer['_'] === 'boolTrue';
|
||||
break;
|
||||
}
|
||||
if (isset($aargs['botAPI']) && $aargs['botAPI']) {
|
||||
$server_answer = $this->MTProto_to_botAPI($server_answer, $args);
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
|
||||
if (strpos($e->getMessage(), 'Received request to switch to DC ') === 0) {
|
||||
if (($method === 'users.getUsers' && $args = ['id' => [['_' => 'inputUserSelf']]]) || $method === 'auth.exportAuthorization' || $method === 'updates.getDifference') {
|
||||
$this->settings['connection_settings']['default_dc'] = $this->authorized_dc = $this->datacenter->curdc;
|
||||
}
|
||||
$last_recv = $this->datacenter->sockets[$aargs['datacenter']]->last_recv;
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
}
|
||||
$this->close_and_reopen($aargs['datacenter']);
|
||||
if ($e->getMessage() === 'Re-executing query after server error...') {
|
||||
return $this->method_call($method, $args, $aargs);
|
||||
}
|
||||
continue;
|
||||
} catch (\RuntimeException $e) {
|
||||
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
|
||||
$this->logger->logger('An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->close_and_reopen($aargs['datacenter']);
|
||||
continue;
|
||||
} finally {
|
||||
if (isset($aargs['heavy']) && $aargs['heavy'] && isset($message_id)) {
|
||||
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['args'] = [];
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]);
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
|
||||
}
|
||||
if (isset($message_id) && $method === 'req_pq') {
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]);
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
|
||||
}
|
||||
if ($canunset) {
|
||||
$this->updates_state['sync_loading'] = false;
|
||||
}
|
||||
if ($canunsetpostponepwrchat) {
|
||||
$this->postpone_pwrchat = false;
|
||||
$this->handle_pending_pwrchat();
|
||||
}
|
||||
if ($canunsetpostponeupdates) {
|
||||
$this->postpone_updates = false;
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
}
|
||||
if ($server_answer === null) {
|
||||
if ($last_recv === $this->datacenter->sockets[$aargs['datacenter']]->last_recv && $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
|
||||
$this->logger->logger('WARNING: Resetting auth key...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
|
||||
return $this->method_call($method, $args, $aargs);
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
|
||||
}
|
||||
$this->logger->logger('Got response for method '.$method.' @ try '.$count.' (response try '.$res_count.')', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] === false)) {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] = true;
|
||||
}
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id] = [];
|
||||
if (isset($arg_chunks) && count($arg_chunks)) {
|
||||
$server_answer = [$server_answer];
|
||||
foreach ($arg_chunks as $args) {
|
||||
$server_answer[] = $this->method_call($method, $args, $aargs);
|
||||
}
|
||||
}
|
||||
|
||||
return $server_answer;
|
||||
}
|
||||
if ($method === 'req_pq') {
|
||||
throw new \danog\MadelineProto\RPCErrorException('RPC_CALL_FAIL');
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
|
||||
}
|
||||
|
||||
public function object_call($object, $args = [], $aargs = ['message_id' => null, 'heavy' => false])
|
||||
public function method_recall($watcherId, $args)
|
||||
{
|
||||
if (!is_array($args)) {
|
||||
throw new \danog\MadelineProto\Exception("Arguments aren't an array.");
|
||||
$message_id = $args['message_id'];
|
||||
$new_datacenter = $args['datacenter'];
|
||||
$old_datacenter = $new_datacenter;
|
||||
if (isset($args['old_datacenter'])) {
|
||||
$old_datacenter = $args['old_datacenter'];
|
||||
}
|
||||
if (!isset($aargs['datacenter'])) {
|
||||
throw new \danog\MadelineProto\Exception('No datacenter provided');
|
||||
$postpone = false;
|
||||
if (isset($args['postpone'])) {
|
||||
$postpone = $args['postpone'];
|
||||
}
|
||||
|
||||
if (isset($this->datacenter->sockets[$old_datacenter]->outgoing_messages[$message_id]['container'])) {
|
||||
$message_ids = $this->datacenter->sockets[$old_datacenter]->outgoing_messages[$message_id]['container'];
|
||||
} else {
|
||||
$message_ids = [$message_id];
|
||||
}
|
||||
|
||||
foreach ($message_ids as $message_id) {
|
||||
if (isset($this->datacenter->sockets[$old_datacenter]->outgoing_messages[$message_id]['body'])) {
|
||||
$this->datacenter->sockets[$new_datacenter]->sendMessage($this->datacenter->sockets[$old_datacenter]->outgoing_messages[$message_id], false);
|
||||
$this->ack_outgoing_message_id($message_id, $old_datacenter);
|
||||
$this->got_response_for_outgoing_message_id($message_id, $old_datacenter);
|
||||
}
|
||||
}
|
||||
if (!$postpone) {
|
||||
$this->datacenter->sockets[$new_datacenter]->writer->resume();
|
||||
}
|
||||
$serialized = $this->serialize_object(['type' => $object], $args, $object);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->object_queue[] = ['_' => $object, 'body' => $serialized, 'content_related' => $this->content_related($object), 'msg_id' => $this->generate_message_id($aargs['datacenter'])];
|
||||
}
|
||||
|
||||
public function method_call($method, $args = [], $aargs = ['msg_id' => null, 'heavy' => false])
|
||||
{
|
||||
$promise = $this->method_call_async_read($method, $args, $aargs);
|
||||
|
||||
return $this->wait($promise);
|
||||
}
|
||||
|
||||
public function method_call_async_read($method, $args = [], $aargs = ['msg_id' => null, 'heavy' => false]): Promise
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$this->method_call_async_write($method, $args, $aargs)->onResolve(function ($e, $read_deferred) use ($deferred) {
|
||||
if ($e) {
|
||||
$deferred->fail($e);
|
||||
} else {
|
||||
if (is_array($read_deferred)) {
|
||||
$read_deferred = array_map(function ($value) {
|
||||
return $value->promise();
|
||||
}, $read_deferred);
|
||||
$deferred->resolve(all($read_deferred));
|
||||
} else {
|
||||
$deferred->resolve($read_deferred->promise());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isset($aargs['noResponse']) && $aargs['noResponse'] ? new \Amp\Success(0) : $deferred->promise();
|
||||
}
|
||||
|
||||
public function method_call_async_write($method, $args = [], $aargs = ['msg_id' => null, 'heavy' => false]): Promise
|
||||
{
|
||||
return call([$this, 'method_call_async_write_generator'], $method, $args, $aargs);
|
||||
}
|
||||
|
||||
public function method_call_async_write_generator($method, $args = [], $aargs = ['msg_id' => null, 'heavy' => false]): \Generator
|
||||
{
|
||||
if (is_array($args) && isset($args['id']['_']) && isset($args['id']['dc_id']) && $args['id']['_'] === 'inputBotInlineMessageID') {
|
||||
$aargs['datacenter'] = $args['id']['dc_id'];
|
||||
}
|
||||
if ($this->wrapper instanceof \danog\MadelineProto\API && isset($this->wrapper->session) && !is_null($this->wrapper->session) && time() - $this->wrapper->serialized > $this->settings['serialization']['serialization_interval']) {
|
||||
$this->logger->logger("Didn't serialize in a while, doing that now...");
|
||||
$this->wrapper->serialize($this->wrapper->session);
|
||||
}
|
||||
if (isset($aargs['file']) && $aargs['file'] && isset($this->datacenter->sockets[$aargs['datacenter'].'_media'])) {
|
||||
\danog\MadelineProto\Logger::log('Using media DC');
|
||||
$aargs['datacenter'] .= '_media';
|
||||
}
|
||||
if (in_array($method, ['messages.setEncryptedTyping', 'messages.readEncryptedHistory', 'messages.sendEncrypted', 'messages.sendEncryptedFile', 'messages.sendEncryptedService', 'messages.receivedQueue'])) {
|
||||
$aargs['queue'] = 'secret';
|
||||
}
|
||||
|
||||
if (is_array($args)) {
|
||||
if (isset($args['message']) && is_string($args['message']) && $this->mb_strlen($args['message']) > $this->config['message_length_max']) {
|
||||
$arg_chunks = $this->split_to_chunks($args);
|
||||
$promises = [];
|
||||
$new_aargs = $aargs;
|
||||
$new_aargs['postpone'] = true;
|
||||
$new_aargs['queue'] = $method;
|
||||
|
||||
foreach ($arg_chunks as $args) {
|
||||
$promises[] = $this->method_call_async_write($method, $args, $new_aargs);
|
||||
}
|
||||
|
||||
if (!isset($aargs['postpone'])) {
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->writer->resume();
|
||||
}
|
||||
|
||||
return yield $promises;
|
||||
}
|
||||
$args = $this->botAPI_to_MTProto($args);
|
||||
if (isset($args['ping_id']) && is_int($args['ping_id'])) {
|
||||
$args['ping_id'] = $this->pack_signed_long($args['ping_id']);
|
||||
}
|
||||
}
|
||||
|
||||
$deferred = new Deferred();
|
||||
$message = ['_' => $method, 'type' => $this->methods->find_by_method($method)['type'], 'content_related' => $this->content_related($method), 'promise' => $deferred, 'method' => true, 'unencrypted' => $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key === null && strpos($method, '.') === false];
|
||||
|
||||
if (is_object($args) && $args instanceof Parameters) {
|
||||
$message['body'] = call([$args, 'fetchParameters']);
|
||||
} else {
|
||||
$message['body'] = $args;
|
||||
}
|
||||
|
||||
if (isset($aargs['msg_id'])) {
|
||||
$message['msg_id'] = $aargs['msg_id'];
|
||||
}
|
||||
if (isset($aargs['queue'])) {
|
||||
$message['queue'] = $aargs['queue'];
|
||||
}
|
||||
if (isset($aargs['file'])) {
|
||||
$message['file'] = $aargs['file'];
|
||||
}
|
||||
if (isset($aargs['botAPI'])) {
|
||||
$message['botAPI'] = $aargs['botAPI'];
|
||||
}
|
||||
if (($method === 'users.getUsers' && $args === ['id' => [['_' => 'inputUserSelf']]]) || $method === 'auth.exportAuthorization' || $method === 'updates.getDifference') {
|
||||
$message['user_related'] = true;
|
||||
}
|
||||
|
||||
$write_deferred = yield $this->datacenter->sockets[$aargs['datacenter']]->sendMessage($message, isset($aargs['postpone']) ? !$aargs['postpone'] : true);
|
||||
|
||||
$deferred = new Deferred();
|
||||
$write_promise = $write_deferred->promise();
|
||||
$write_promise->onResolve(
|
||||
function ($e, $result) use ($aargs, $deferred) {
|
||||
//$this->datacenter->sockets[$aargs['datacenter']]->checker->resume();
|
||||
if ($e) {
|
||||
return $deferred->fail($e);
|
||||
}
|
||||
$deferred->resolve($result);
|
||||
}
|
||||
);
|
||||
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->checker->resume();
|
||||
|
||||
return $deferred;
|
||||
}
|
||||
|
||||
public function object_call($object, $args = [], $aargs = ['msg_id' => null, 'heavy' => false])
|
||||
{
|
||||
return $this->wait($this->object_call_async($object, $args, $aargs));
|
||||
}
|
||||
|
||||
public function object_call_async($object, $args = [], $aargs = ['msg_id' => null, 'heavy' => false]): Promise
|
||||
{
|
||||
$message = ['_' => $object, 'body' => $args, 'content_related' => $this->content_related($object), 'unencrypted' => $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key === null, 'method' => false];
|
||||
if (isset($aargs['promise'])) {
|
||||
$message['promise'] = $aargs['promise'];
|
||||
}
|
||||
|
||||
return $this->datacenter->sockets[$aargs['datacenter']]->sendMessage($message, isset($aargs['postpone']) ? !$aargs['postpone'] : true);
|
||||
}
|
||||
|
||||
/*
|
||||
$message = [
|
||||
// only in outgoing messages
|
||||
'body' => 'serialized body', (optional if container)
|
||||
'content_related' => bool,
|
||||
'_' => 'predicate',
|
||||
'promise' => deferred promise that gets resolved when a response to the message is received (optional),
|
||||
'send_promise' => deferred promise that gets resolved when the message is sent (optional),
|
||||
'file' => bool (optional),
|
||||
'type' => 'type' (optional),
|
||||
'queue' => queue ID (optional),
|
||||
'container' => [message ids] (optional),
|
||||
|
||||
// only in incoming messages
|
||||
'content' => deserialized body,
|
||||
'seq_no' => number (optional),
|
||||
'from_container' => bool (optional),
|
||||
|
||||
// can be present in both
|
||||
'response' => message id (optional),
|
||||
'msg_id' => message id (optional),
|
||||
'sent' => timestamp,
|
||||
'tries' => number
|
||||
];
|
||||
*/
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Crypt module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
|
@ -1,24 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Files module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use danog\MadelineProto\Async\AsyncParameters;
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\Logger;
|
||||
use danog\MadelineProto\RPCErrorException;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Manages upload and download of files.
|
||||
*/
|
||||
trait Files
|
||||
{
|
||||
public function upload($file, $file_name = '', $cb = null, $encrypted = false, $datacenter = null)
|
||||
public function upload_async($file, $file_name = '', $cb = null, $encrypted = false, $datacenter = null): \Generator
|
||||
{
|
||||
if (is_object($file)) {
|
||||
if (!isset(class_implements($file)['danog\MadelineProto\FileCallbackInterface'])) {
|
||||
@ -34,7 +46,10 @@ trait Files
|
||||
if (empty($file_name)) {
|
||||
$file_name = basename($file);
|
||||
}
|
||||
$datacenter = is_null($datacenter) ? $this->datacenter->curdc : $datacenter;
|
||||
$datacenter = is_null($datacenter) ? $this->settings['connection_settings']['default_dc'] : $datacenter;
|
||||
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
$file_size = filesize($file);
|
||||
if ($file_size > 512 * 1024 * 3000) {
|
||||
throw new \danog\MadelineProto\Exception('Given file is too big!');
|
||||
@ -51,7 +66,12 @@ trait Files
|
||||
$constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($file_size > 10 * 1024 * 1024 ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
|
||||
$file_id = $this->random(8);
|
||||
$f = fopen($file, 'r');
|
||||
fseek($f, 0);
|
||||
|
||||
$seekable = stream_get_meta_data($f)['seekable'];
|
||||
if ($seekable) {
|
||||
fseek($f, 0);
|
||||
}
|
||||
$ige = null;
|
||||
if ($encrypted === true) {
|
||||
$key = $this->random(32);
|
||||
$iv = $this->random(32);
|
||||
@ -63,35 +83,69 @@ trait Files
|
||||
$ige->enableContinuousBuffer();
|
||||
}
|
||||
$ctx = hash_init('md5');
|
||||
while (ftell($f) !== $file_size) {
|
||||
$bytes = stream_get_contents($f, $part_size);
|
||||
if ($encrypted === true) {
|
||||
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
|
||||
}
|
||||
hash_update($ctx, $bytes);
|
||||
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'file' => true, 'datacenter' => &$datacenter])) {
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
|
||||
}
|
||||
$cb(ftell($f) * 100 / $file_size);
|
||||
$promises = [];
|
||||
$cur_part_num = 0;
|
||||
|
||||
while ($part_num < $part_total_num) {
|
||||
$t = microtime(true);
|
||||
$read_deferred = yield $this->method_call_async_write(
|
||||
$method,
|
||||
new AsyncParameters(
|
||||
static function () use ($file_id, $part_num, $part_total_num, $part_size, $f, $ctx, $ige, $seekable) {
|
||||
if ($seekable) {
|
||||
fseek($f, $part_num * $part_size);
|
||||
}
|
||||
$bytes = stream_get_contents($f, $part_size);
|
||||
if ($ige) {
|
||||
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
|
||||
}
|
||||
hash_update($ctx, $bytes);
|
||||
|
||||
return ['file_id' => $file_id, 'file_part' => $part_num, 'file_total_parts' => $part_total_num, 'bytes' => $bytes];
|
||||
},
|
||||
$seekable
|
||||
),
|
||||
['heavy' => true, 'file' => true, 'datacenter' => $datacenter]
|
||||
);
|
||||
$this->logger->logger('Speed for chunk: '.(($part_size * 8 / 1000000) / (microtime(true) - $t)));
|
||||
$part_num++;
|
||||
$promises[] = $read_deferred->promise();
|
||||
}
|
||||
fclose($f);
|
||||
$constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'md5_checksum' => hash_final($ctx), 'mime_type' => mime_content_type($file)];
|
||||
|
||||
$result = yield $promises;
|
||||
foreach ($result as $key => $result) {
|
||||
if (!$result) {
|
||||
throw new \danog\MadelineProto\Exception('Upload of part '.$key.' failed');
|
||||
}
|
||||
}
|
||||
|
||||
$constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'mime_type' => $this->get_mime_from_file($file)];
|
||||
if ($encrypted === true) {
|
||||
$constructor['key_fingerprint'] = $fingerprint;
|
||||
$constructor['key'] = $key;
|
||||
$constructor['iv'] = $iv;
|
||||
$constructor['md5_checksum'] = '';
|
||||
}
|
||||
|
||||
fclose($f);
|
||||
|
||||
return $constructor;
|
||||
}
|
||||
|
||||
public function upload($file, $file_name = '', $cb = null, $encrypted = false, $datacenter = null)
|
||||
{
|
||||
$t = microtime(true);
|
||||
$res = $this->wait(call([$this, 'upload_async'], $file, $file_name, $cb, $encrypted, $datacenter));
|
||||
$this->logger->logger('Speed: '.((filesize($file) * 8) / (microtime(true) - $t) / 1000000));
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function upload_encrypted($file, $file_name = '', $cb = null)
|
||||
{
|
||||
return $this->upload($file, $file_name, $cb, true);
|
||||
}
|
||||
|
||||
public function gen_all_file($media)
|
||||
public function gen_all_file($media, $regenerate)
|
||||
{
|
||||
$res = [$this->constructors->find_by_predicate($media['_'])['type'] => $media];
|
||||
switch ($media['_']) {
|
||||
@ -100,7 +154,7 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['Photo'] = $media['photo'];
|
||||
$res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['photo']['id'], 'access_hash' => $media['photo']['access_hash']];
|
||||
$res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['photo']['id'], 'access_hash' => $media['photo']['access_hash'], 'file_reference' => $this->wait($this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION, $media['photo']))];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaPhoto', 'id' => $res['InputPhoto']];
|
||||
if (isset($media['ttl_seconds'])) {
|
||||
$res['InputMedia']['ttl_seconds'] = $media['ttl_seconds'];
|
||||
@ -111,7 +165,7 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['Document'] = $media['document'];
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['document']['id'], 'access_hash' => $media['document']['access_hash']];
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['document']['id'], 'access_hash' => $media['document']['access_hash'], 'file_reference' => $this->wait($this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT_LOCATION, $media['document']))];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
|
||||
if (isset($media['ttl_seconds'])) {
|
||||
$res['InputMedia']['ttl_seconds'] = $media['ttl_seconds'];
|
||||
@ -121,7 +175,7 @@ trait Files
|
||||
if (!isset($media['access_hash'])) {
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['id'], 'access_hash' => $media['access_hash']];
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['id'], 'access_hash' => $media['access_hash'], 'file_reference' => $this->wait($this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT_LOCATION, $media))];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
|
||||
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $media];
|
||||
break;
|
||||
@ -129,8 +183,8 @@ trait Files
|
||||
if (!isset($media['access_hash'])) {
|
||||
throw new \danog\MadelineProto\Exception('No access hash');
|
||||
}
|
||||
$res['InputDocument'] = ['_' => 'inputDocument', 'id' => $media['id'], 'access_hash' => $media['access_hash']];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaDocument', 'id' => $res['InputDocument']];
|
||||
$res['InputPhoto'] = ['_' => 'inputPhoto', 'id' => $media['id'], 'access_hash' => $media['access_hash'], 'file_reference' => $this->wait($this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION, $media))];
|
||||
$res['InputMedia'] = ['_' => 'inputMediaPhoto', 'id' => $res['InputPhoto']];
|
||||
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $media];
|
||||
break;
|
||||
default:
|
||||
@ -140,7 +194,7 @@ trait Files
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function get_file_info($constructor)
|
||||
public function get_file_info($constructor, $regenerate = false)
|
||||
{
|
||||
if (is_string($constructor)) {
|
||||
$constructor = $this->unpack_file_id($constructor)['MessageMedia'];
|
||||
@ -148,13 +202,13 @@ trait Files
|
||||
switch ($constructor['_']) {
|
||||
case 'updateNewMessage':
|
||||
case 'updateNewChannelMessage':
|
||||
$constructor = $constructor['message'];
|
||||
$constructor = $constructor['message'];
|
||||
|
||||
case 'message':
|
||||
$constructor = $constructor['media'];
|
||||
$constructor = $constructor['media'];
|
||||
}
|
||||
|
||||
return $this->gen_all_file($constructor);
|
||||
return $this->gen_all_file($constructor, $regenerate);
|
||||
}
|
||||
|
||||
public function get_download_info($message_media)
|
||||
@ -167,13 +221,16 @@ trait Files
|
||||
}
|
||||
$res = [];
|
||||
switch ($message_media['_']) {
|
||||
// Updates
|
||||
case 'updateNewMessage':
|
||||
case 'updateNewChannelMessage':
|
||||
$message_media = $message_media['message'];
|
||||
$message_media = $message_media['message'];
|
||||
case 'message':
|
||||
return $this->get_download_info($message_media['media']);
|
||||
case 'updateNewEncryptedMessage':
|
||||
$message_media = $message_media['message'];
|
||||
$message_media = $message_media['message'];
|
||||
|
||||
// Secret media
|
||||
case 'encryptedMessage':
|
||||
if ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaExternalDocument') {
|
||||
return $this->get_download_info($message_media['decrypted_message']['media']);
|
||||
@ -218,13 +275,22 @@ trait Files
|
||||
}
|
||||
}
|
||||
if (!isset($res['ext'])) {
|
||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($res['mime']));
|
||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime(isset($res['mime']) ? $res['mime'] : 'image/jpeg'));
|
||||
}
|
||||
if (!isset($res['mime'])) {
|
||||
$res['mime'] = $this->get_mime_from_extension($res['ext'], 'image/jpeg');
|
||||
}
|
||||
if (!isset($res['name'])) {
|
||||
$res['name'] = $message_media['file']['access_hash'];
|
||||
}
|
||||
|
||||
return $res;
|
||||
// Wallpapers
|
||||
case 'wallPaper':
|
||||
$photo = end($message_media['sizes']);
|
||||
|
||||
return array_merge($res, $this->get_download_info($photo));
|
||||
// Photos
|
||||
case 'photo':
|
||||
case 'messageMediaPhoto':
|
||||
if ($message_media['_'] == 'photo') {
|
||||
@ -234,32 +300,45 @@ trait Files
|
||||
$res['MessageMedia'] = $message_media;
|
||||
$photo = end($message_media['photo']['sizes']);
|
||||
}
|
||||
$res['name'] = $photo['location']['volume_id'].'_'.$photo['location']['local_id'];
|
||||
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']];
|
||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
|
||||
$res['mime'] = 'image/jpeg';
|
||||
if (isset($photo['location']['size'])) {
|
||||
$res['size'] = $photo['location']['size'];
|
||||
}
|
||||
if (isset($photo['location']['bytes'])) {
|
||||
$res['size'] = strlen($photo['location']['bytes']);
|
||||
|
||||
return array_merge($res, $this->get_download_info($photo));
|
||||
|
||||
case 'userProfilePhoto':
|
||||
case 'chatPhoto':
|
||||
return array_merge($res, $this->get_download_info($message_media['photo_big']));
|
||||
|
||||
case 'photoCachedSize':
|
||||
$res['size'] = strlen($message_media['bytes']);
|
||||
$res['data'] = $message_media['bytes'];
|
||||
|
||||
if ($message_media['location']['_'] === 'fileLocationUnavailable') {
|
||||
$res['name'] = $message_media['volume_id'].'_'.$message_media['local_id'];
|
||||
$res['mime'] = $this->get_mime_from_buffer($res['data']);
|
||||
$res['ext'] = $this->get_extension_from_mime($res['mime']);
|
||||
} else {
|
||||
$res = array_merge($res, $this->get_download_info($message_media['location']));
|
||||
}
|
||||
|
||||
return $res;
|
||||
case 'photoSize':
|
||||
case 'photoCachedSize':
|
||||
$res['name'] = $message_media['location']['volume_id'].'_'.$message_media['location']['local_id'];
|
||||
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['location']['volume_id'], 'local_id' => $message_media['location']['local_id'], 'secret' => $message_media['location']['secret'], 'dc_id' => $message_media['location']['dc_id']];
|
||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
|
||||
$res['mime'] = 'image/jpeg';
|
||||
if (isset($photo['location']['size'])) {
|
||||
$res['size'] = $photo['location']['size'];
|
||||
}
|
||||
if (isset($photo['location']['bytes'])) {
|
||||
$res['size'] = strlen($photo['location']['bytes']);
|
||||
$res = $this->get_download_info($message_media['location']);
|
||||
if (isset($message_media['size'])) {
|
||||
$res['size'] = $message_media['size'];
|
||||
}
|
||||
|
||||
return $res;
|
||||
|
||||
case 'fileLocationUnavailable':
|
||||
throw new \danog\MadelineProto\Exception('File location unavailable');
|
||||
case 'fileLocation':
|
||||
$res['name'] = $message_media['volume_id'].'_'.$message_media['local_id'];
|
||||
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['volume_id'], 'local_id' => $message_media['local_id'], 'secret' => $message_media['secret'], 'dc_id' => $message_media['dc_id'], 'file_reference' => $this->wait($this->referenceDatabase->getReference(ReferenceDatabase::PHOTO_LOCATION_LOCATION, $message_media))];
|
||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
|
||||
$res['mime'] = $this->get_mime_from_extension($res['ext'], 'image/jpeg');
|
||||
|
||||
return $res;
|
||||
|
||||
// Documents
|
||||
case 'decryptedMessageMediaExternalDocument':
|
||||
case 'document':
|
||||
$message_media = ['_' => 'messageMediaDocument', 'ttl_seconds' => 0, 'document' => $message_media];
|
||||
@ -286,7 +365,7 @@ trait Files
|
||||
$res['name'] .= ' - '.$audio['performer'];
|
||||
}
|
||||
}
|
||||
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id']];
|
||||
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id'], 'file_reference' => $this->wait($this->referenceDatabase->getReference(ReferenceDatabase::DOCUMENT_LOCATION_LOCATION, $message_media['document']))];
|
||||
if (!isset($res['ext'])) {
|
||||
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
|
||||
}
|
||||
@ -330,11 +409,12 @@ trait Files
|
||||
$file = realpath($file);
|
||||
$message_media = $this->get_download_info($message_media);
|
||||
$stream = fopen($file, 'r+b');
|
||||
$size = fstat($stream)['size'];
|
||||
$this->logger->logger('Waiting for lock of file to download...');
|
||||
flock($stream, LOCK_EX);
|
||||
|
||||
try {
|
||||
$this->download_to_stream($message_media, $stream, $cb, filesize($file), -1);
|
||||
$this->download_to_stream($message_media, $stream, $cb, $size, -1);
|
||||
} finally {
|
||||
flock($stream, LOCK_UN);
|
||||
fclose($stream);
|
||||
@ -372,7 +452,10 @@ trait Files
|
||||
$size = $end - $offset;
|
||||
$part_size = $this->settings['download']['part_size'];
|
||||
$percent = 0;
|
||||
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
|
||||
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->settings['connection_settings']['default_dc'];
|
||||
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
if (isset($message_media['key'])) {
|
||||
$digest = hash('md5', $message_media['key'].$message_media['iv'], true);
|
||||
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
|
||||
@ -396,7 +479,16 @@ trait Files
|
||||
$res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'file' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'file' => true, 'datacenter' => &$datacenter]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if (strpos($e->rpc, 'FLOOD_WAIT_') === 0) {
|
||||
//if (isset($message_media['MessageMedia']) && !$this->get_self()['bot']) $this->method_call('messages.sendMedia', ['peer' => '@danogentili', 'media' => $message_media['MessageMedia'], 'message' => 'This is broken'], ['datacenter' => $this->datacenter->curdc]);
|
||||
if (isset($message_media['MessageMedia']) && !$this->authorization['user']['bot'] && $this->settings['download']['report_broken_media']) {
|
||||
try {
|
||||
$this->method_call('messages.sendMedia', ['peer' => 'support', 'media' => $message_media['MessageMedia'], 'message' => "I can't download this file, could you please help?"], ['datacenter' => $this->datacenter->curdc]);
|
||||
} catch (RPCErrorException $e) {
|
||||
$this->logger->logger('An error occurred while reporting the broken file: '.$e->rpc, Logger::FATAL_ERROR);
|
||||
} catch (Exception $e) {
|
||||
$this->logger->logger('An error occurred while reporting the broken file: '.$e->getMessage(), Logger::FATAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('The media server where this file is hosted is offline/overloaded, please try again later. Send the media to the telegram devs or to @danogentili to fix this.');
|
||||
}
|
||||
switch ($e->rpc) {
|
||||
|
@ -1,161 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
||||
*/
|
||||
trait MessageHandler
|
||||
{
|
||||
public function send_unencrypted_message($type, $message_data, $message_id, $datacenter)
|
||||
{
|
||||
$this->logger->logger("Sending $type as unencrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$message_data = "\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int(strlen($message_data)).$message_data;
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['response' => -1];
|
||||
$this->datacenter->sockets[$datacenter]->send_message($message_data);
|
||||
}
|
||||
|
||||
public function send_messages($datacenter)
|
||||
{
|
||||
//$has_ack = false;
|
||||
|
||||
if (count($this->datacenter->sockets[$datacenter]->object_queue) > 1) {
|
||||
$messages = [];
|
||||
$this->logger->logger("Sending msg_container as encrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
foreach ($this->datacenter->sockets[$datacenter]->object_queue as $message) {
|
||||
$message['seqno'] = $this->generate_out_seq_no($datacenter, $message['content_related']);
|
||||
$message['bytes'] = strlen($message['body']);
|
||||
//$has_ack = $has_ack || $message['_'] === 'msgs_ack';
|
||||
$this->logger->logger("Inside of msg_container, sending {$message['_']} as encrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$message['_'] = 'MTmessage';
|
||||
$messages[] = $message;
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'response' => -1]; //, 'content' => $this->deserialize($message['body'], ['type' => '', 'datacenter' => $datacenter])];
|
||||
}
|
||||
$message_data = $this->serialize_object(['type' => ''], ['_' => 'msg_container', 'messages' => $messages], 'lol');
|
||||
$message_id = $this->generate_message_id($datacenter);
|
||||
$seq_no = $this->generate_out_seq_no($datacenter, false);
|
||||
} elseif (count($this->datacenter->sockets[$datacenter]->object_queue)) {
|
||||
$message = array_shift($this->datacenter->sockets[$datacenter]->object_queue);
|
||||
$this->logger->logger("Sending {$message['_']} as encrypted message to DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$message_data = $message['body'];
|
||||
$message_id = $message['msg_id'];
|
||||
$seq_no = $this->generate_out_seq_no($datacenter, $message['content_related']);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
$plaintext = $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].$this->datacenter->sockets[$datacenter]->session_id.$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
|
||||
$padding = $this->posmod(-strlen($plaintext), 16);
|
||||
if ($padding < 12) {
|
||||
$padding += 16;
|
||||
}
|
||||
$padding = $this->random($padding);
|
||||
$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 88, 32).$plaintext.$padding, true), 8, 16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key']);
|
||||
$message = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'].$message_key.$this->ige_encrypt($plaintext.$padding, $aes_key, $aes_iv);
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['seq_no' => $seq_no, 'response' => -1];
|
||||
$this->datacenter->sockets[$datacenter]->send_message($message);
|
||||
$this->datacenter->sockets[$datacenter]->object_queue = [];
|
||||
|
||||
/*if ($has_ack) {
|
||||
foreach ($this->datacenter->sockets[$datacenter]->ack_queue as $msg_id) {
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['ack'] = true;
|
||||
}
|
||||
$this->datacenter->sockets[$datacenter]->ack_queue = [];
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading connection and receiving message from server.
|
||||
*/
|
||||
public function recv_message($datacenter)
|
||||
{
|
||||
if ($this->datacenter->sockets[$datacenter]->must_open) {
|
||||
$this->logger->logger('Trying to read from closed socket, sending initial ping');
|
||||
if ($this->is_http($datacenter)) {
|
||||
$this->method_call('http_wait', ['max_wait' => 500, 'wait_after' => 150, 'max_delay' => 500], ['datacenter' => $datacenter]);
|
||||
} elseif (isset($this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) && $this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) {
|
||||
$this->method_call('ping', ['ping_id' => 0], ['datacenter' => $datacenter]);
|
||||
} else {
|
||||
throw new \danog\MadelineProto\Exception('Resend query');
|
||||
}
|
||||
}
|
||||
$payload = $this->datacenter->sockets[$datacenter]->read_message();
|
||||
if (strlen($payload) === 4) {
|
||||
$payload = $this->unpack_signed_int($payload);
|
||||
$this->logger->logger("Received $payload from DC $datacenter", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return $payload;
|
||||
}
|
||||
$auth_key_id = substr($payload, 0, 8);
|
||||
if ($auth_key_id === "\0\0\0\0\0\0\0\0") {
|
||||
$message_id = substr($payload, 8, 8);
|
||||
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
|
||||
$message_length = unpack('V', substr($payload, 16, 4))[1];
|
||||
$message_data = substr($payload, 20, $message_length);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = [];
|
||||
} elseif ($auth_key_id === $this->datacenter->sockets[$datacenter]->temp_auth_key['id']) {
|
||||
$message_key = substr($payload, 8, 16);
|
||||
$encrypted_data = substr($payload, 24);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], false);
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
/*
|
||||
$server_salt = substr($decrypted_data, 0, 8);
|
||||
if ($server_salt != $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt']) {
|
||||
$this->logger->logger('WARNING: Server salt mismatch (my server salt '.$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
*/
|
||||
$session_id = substr($decrypted_data, 8, 8);
|
||||
if ($session_id != $this->datacenter->sockets[$datacenter]->session_id) {
|
||||
throw new \danog\MadelineProto\Exception('Session id mismatch.');
|
||||
}
|
||||
$message_id = substr($decrypted_data, 16, 8);
|
||||
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
|
||||
$seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
|
||||
// Dunno how to handle any incorrect sequence numbers
|
||||
$message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
|
||||
if ($message_data_length > strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
|
||||
}
|
||||
if (strlen($decrypted_data) - 32 - $message_data_length < 12) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too small');
|
||||
}
|
||||
if (strlen($decrypted_data) - 32 - $message_data_length > 1024) {
|
||||
throw new \danog\MadelineProto\SecurityException('padding is too big');
|
||||
}
|
||||
if ($message_data_length < 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not positive');
|
||||
}
|
||||
if ($message_data_length % 4 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
|
||||
}
|
||||
$message_data = substr($decrypted_data, 32, $message_data_length);
|
||||
if ($message_key != substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 96, 32).$decrypted_data, true), 8, 16)) {
|
||||
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
|
||||
}
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
|
||||
} else {
|
||||
$this->close_and_reopen($datacenter);
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
|
||||
}
|
||||
$deserialized = $this->deserialize($message_data, ['type' => '', 'datacenter' => $datacenter]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1;
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id;
|
||||
$this->datacenter->sockets[$datacenter]->last_recv = time();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages message ids.
|
||||
*/
|
||||
trait MsgIdHandler
|
||||
{
|
||||
public function check_message_id($new_message_id, $aargs)
|
||||
{
|
||||
if (!is_object($new_message_id)) {
|
||||
$new_message_id = new \phpseclib\Math\BigInteger(strrev($new_message_id), 256);
|
||||
}
|
||||
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32);
|
||||
if ($min_message_id->compare($new_message_id) > 0) {
|
||||
$this->logger->logger('Given message id ('.$new_message_id.') is too old compared to the min value ('.$min_message_id.').', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
$max_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta + 30))->bitwise_leftShift(32);
|
||||
if ($max_message_id->compare($new_message_id) < 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too new compared to the max value ('.$max_message_id.'). Consider syncing your date.');
|
||||
}
|
||||
if ($aargs['outgoing']) {
|
||||
if (!$new_message_id->divide(\danog\MadelineProto\Magic::$four)[1]->equals(\danog\MadelineProto\Magic::$zero)) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4. Consider syncing your date.');
|
||||
}
|
||||
if (!\danog\MadelineProto\Magic::$has_thread && $new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], false)) <= 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', 1);
|
||||
}
|
||||
if (count($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
|
||||
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
|
||||
$key = key($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$key]);
|
||||
}
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->max_outgoing_id = $new_message_id;
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[strrev($new_message_id->toBytes())] = [];
|
||||
} else {
|
||||
if (!$new_message_id->divide(\danog\MadelineProto\Magic::$four)[1]->equals(\danog\MadelineProto\Magic::$one) && !$new_message_id->divide(\danog\MadelineProto\Magic::$four)[1]->equals(\danog\MadelineProto\Magic::$three)) {
|
||||
throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3');
|
||||
}
|
||||
$key = $this->get_max_id($aargs['datacenter'], true);
|
||||
if ($aargs['container']) {
|
||||
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) >= 0) {
|
||||
$this->logger->logger('WARNING: Given message id ('.$new_message_id.') is bigger than or equal to the current limit ('.$key.'). Consider syncing your date.', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
} else {
|
||||
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) <= 0) {
|
||||
$this->logger->logger('WARNING: Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (count($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
|
||||
reset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
|
||||
$key = key($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
|
||||
if ($key[0] === "\0") {
|
||||
$key = $key;
|
||||
}
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$key]);
|
||||
}
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->max_incoming_id = $new_message_id;
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_message_id($datacenter)
|
||||
{
|
||||
$message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta))->bitwise_leftShift(32);
|
||||
if ($message_id->compare($key = $this->get_max_id($datacenter, false)) <= 0) {
|
||||
$message_id = $key->add(\danog\MadelineProto\Magic::$four);
|
||||
}
|
||||
$this->check_message_id($message_id, ['outgoing' => true, 'datacenter' => $datacenter, 'container' => false]);
|
||||
|
||||
return strrev($message_id->toBytes());
|
||||
}
|
||||
|
||||
public function get_max_id($datacenter, $incoming)
|
||||
{
|
||||
$incoming = $incoming ? 'incoming' : 'outgoing';
|
||||
if (isset($this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'}) && is_object($this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'})) {
|
||||
return $this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'};
|
||||
}
|
||||
|
||||
return \danog\MadelineProto\Magic::$zero;
|
||||
}
|
||||
}
|
202
src/danog/MadelineProto/MTProtoTools/PasswordCalculator.php
Normal file
202
src/danog/MadelineProto/MTProtoTools/PasswordCalculator.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* Password calculator module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\Magic;
|
||||
use danog\MadelineProto\SecurityException;
|
||||
use danog\MadelineProto\Tools;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Manages password calculation.
|
||||
*/
|
||||
class PasswordCalculator
|
||||
{
|
||||
use AuthKeyHandler;
|
||||
use Tools;
|
||||
private $new_algo;
|
||||
private $secure_random = '';
|
||||
|
||||
private $current_algo;
|
||||
private $srp_B;
|
||||
private $srp_BForHash;
|
||||
private $srp_id;
|
||||
|
||||
public function __construct($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function addInfo(array $object)
|
||||
{
|
||||
if ($object['_'] !== 'account.password') {
|
||||
throw new Exception('Wrong constructor');
|
||||
}
|
||||
if ($object['has_secure_values']) {
|
||||
throw new Exception('Cannot parse secure values');
|
||||
}
|
||||
if ($object['has_password']) {
|
||||
switch ($object['current_algo']['_']) {
|
||||
case 'passwordKdfAlgoUnknown':
|
||||
throw new Exception('Update your client to continue');
|
||||
case 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow':
|
||||
$object['current_algo']['g'] = new BigInteger($object['current_algo']['g']);
|
||||
$object['current_algo']['p'] = new BigInteger((string) $object['current_algo']['p'], 256);
|
||||
$this->check_p_g($object['current_algo']['p'], $object['current_algo']['g']);
|
||||
$object['current_algo']['gForHash'] = str_pad($object['current_algo']['g']->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
$object['current_algo']['pForHash'] = str_pad($object['current_algo']['p']->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown KDF algo {$object['current_algo']['_']}");
|
||||
}
|
||||
$this->current_algo = $object['current_algo'];
|
||||
$object['srp_B'] = new BigInteger((string) $object['srp_B'], 256);
|
||||
if ($object['srp_B']->compare(\danog\MadelineProto\Magic::$zero) < 0) {
|
||||
throw new SecurityException('srp_B < 0');
|
||||
}
|
||||
if ($object['srp_B']->compare($object['current_algo']['p']) > 0) {
|
||||
throw new SecurityException('srp_B > p');
|
||||
}
|
||||
$this->srp_B = $object['srp_B'];
|
||||
$this->srp_BForHash = str_pad($object['srp_B']->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
$this->srp_id = $object['srp_id'];
|
||||
} else {
|
||||
$this->current_algo = null;
|
||||
$this->srp_B = null;
|
||||
$this->srp_BForHash = null;
|
||||
$this->srp_id = null;
|
||||
}
|
||||
switch ($object['new_algo']['_']) {
|
||||
case 'passwordKdfAlgoUnknown':
|
||||
throw new Exception('Update your client to continue');
|
||||
case 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow':
|
||||
$object['new_algo']['g'] = new BigInteger($object['new_algo']['g']);
|
||||
$object['new_algo']['p'] = new BigInteger((string) $object['new_algo']['p'], 256);
|
||||
$this->check_p_g($object['new_algo']['p'], $object['new_algo']['g']);
|
||||
$object['new_algo']['gForHash'] = str_pad($object['new_algo']['g']->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
$object['new_algo']['pForHash'] = str_pad($object['new_algo']['p']->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unknown KDF algo {$object['new_algo']['_']}");
|
||||
}
|
||||
$this->new_algo = $object['new_algo'];
|
||||
$this->secure_random = $object['secure_random'];
|
||||
}
|
||||
|
||||
public function createSalt(string $prefix = ''): string
|
||||
{
|
||||
return $prefix.$this->random(32);
|
||||
}
|
||||
|
||||
public function hashSha256(string $data, string $salt): string
|
||||
{
|
||||
return hash('sha256', $salt.$data.$salt, true);
|
||||
}
|
||||
|
||||
public function hashPassword(string $password, string $client_salt, string $server_salt): string
|
||||
{
|
||||
$buf = $this->hashSha256($password, $client_salt);
|
||||
$buf = $this->hashSha256($buf, $server_salt);
|
||||
$hash = hash_pbkdf2('sha512', $buf, $client_salt, 100000, 0, true);
|
||||
|
||||
return $this->hashSha256($hash, $server_salt);
|
||||
}
|
||||
|
||||
public function getCheckPassword(string $password): array
|
||||
{
|
||||
if ($password === '') {
|
||||
return ['_' => 'inputCheckPasswordEmpty'];
|
||||
}
|
||||
$client_salt = $this->current_algo['salt1'];
|
||||
$server_salt = $this->current_algo['salt2'];
|
||||
$g = $this->current_algo['g'];
|
||||
$gForHash = $this->current_algo['gForHash'];
|
||||
$p = $this->current_algo['p'];
|
||||
$pForHash = $this->current_algo['pForHash'];
|
||||
$B = $this->srp_B;
|
||||
$BForHash = $this->srp_BForHash;
|
||||
$id = $this->srp_id;
|
||||
|
||||
$x = new BigInteger($this->hashPassword($password, $client_salt, $server_salt), 256);
|
||||
$g_x = $g->powMod($x, $p);
|
||||
|
||||
$k = new BigInteger(hash('sha256', $pForHash.$gForHash, true), 256);
|
||||
$kg_x = $k->multiply($g_x)->powMod(Magic::$one, $p);
|
||||
|
||||
$a = new BigInteger($this->random(2048 / 8), 256);
|
||||
$A = $g->powMod($a, $p);
|
||||
$this->check_G($A, $p);
|
||||
$AForHash = str_pad($A->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
|
||||
$b_kg_x = $B->powMod(Magic::$one, $p)->subtract($kg_x);
|
||||
|
||||
$u = new BigInteger(hash('sha256', $AForHash.$BForHash, true), 256);
|
||||
$ux = $u->multiply($x);
|
||||
$a_ux = $a->add($ux);
|
||||
|
||||
$S = $b_kg_x->powMod($a_ux, $p);
|
||||
|
||||
$SForHash = str_pad($S->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
$K = hash('sha256', $SForHash, true);
|
||||
|
||||
$h1 = hash('sha256', $pForHash, true);
|
||||
$h2 = hash('sha256', $gForHash, true);
|
||||
$h1 ^= $h2;
|
||||
|
||||
$M1 = hash('sha256', $h1.hash('sha256', $client_salt, true).hash('sha256', $server_salt, true).$AForHash.$BForHash.$K, true);
|
||||
|
||||
return ['_' => 'inputCheckPasswordSRP', 'srp_id' => $id, 'A' => $AForHash, 'M1' => $M1];
|
||||
}
|
||||
|
||||
public function getPassword(array $params): array
|
||||
{
|
||||
$return = ['password' => $this->getCheckPassword(isset($params['password']) ? $params['password'] : ''), 'new_settings' => ['_' => 'account.passwordInputSettings', 'new_algo' => ['_' => 'passwordKdfAlgoUnknown'], 'new_password_hash' => '', 'hint' => '']];
|
||||
$new_settings = &$return['new_settings'];
|
||||
|
||||
if (isset($params['new_password']) && $params['new_password'] !== '') {
|
||||
$client_salt = $this->createSalt($this->new_algo['salt1']);
|
||||
$server_salt = $this->new_algo['salt2'];
|
||||
$g = $this->new_algo['g'];
|
||||
$p = $this->new_algo['p'];
|
||||
$pForHash = $this->new_algo['pForHash'];
|
||||
|
||||
$x = new BigInteger($this->hashPassword($params['new_password'], $client_salt, $server_salt), 256);
|
||||
$v = $g->powMod($x, $p);
|
||||
$vForHash = str_pad($v->toBytes(), 256, chr(0), \STR_PAD_LEFT);
|
||||
|
||||
$new_settings['new_algo'] = [
|
||||
'_' => 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
|
||||
'salt1' => $client_salt,
|
||||
'salt2' => $server_salt,
|
||||
'g' => (int) $g->toString(),
|
||||
'p' => $pForHash,
|
||||
];
|
||||
$new_settings['new_password_hash'] = $vForHash;
|
||||
$new_settings['hint'] = $params['hint'];
|
||||
if (isset($params['email'])) {
|
||||
$new_settings['email'] = $params['email'];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
@ -1,18 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
/**
|
||||
* PeerHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Loop;
|
||||
|
||||
/**
|
||||
* Manages peers.
|
||||
*/
|
||||
@ -23,6 +31,11 @@ trait PeerHandler
|
||||
return -($id + pow(10, (int) floor(log($id, 10) + 3)));
|
||||
}
|
||||
|
||||
public function from_supergroup($id)
|
||||
{
|
||||
return -$id - pow(10, (int) floor(log(-$id, 10)));
|
||||
}
|
||||
|
||||
public function is_supergroup($id)
|
||||
{
|
||||
$log = log(-$id, 10);
|
||||
@ -55,116 +68,99 @@ trait PeerHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function add_support($support)
|
||||
{
|
||||
$this->supportUser = $support['user']['id'];
|
||||
}
|
||||
|
||||
public function add_users($users)
|
||||
{
|
||||
foreach ($users as $key => $user) {
|
||||
if (!isset($user['access_hash'])) {
|
||||
if (isset($user['username']) && !isset($this->chats[$user['id']])) {
|
||||
if ($this->postpone_pwrchat) {
|
||||
$this->pending_pwrchat[$user['username']] = [false, true];
|
||||
} else {
|
||||
try {
|
||||
$this->get_pwr_chat($user['username'], false, true);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
switch ($user['_']) {
|
||||
case 'user':
|
||||
if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) {
|
||||
$this->chats[$user['id']] = $user;
|
||||
foreach ($users as $user) {
|
||||
$this->add_user($user);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->postpone_pwrchat) {
|
||||
$this->pending_pwrchat[$user['id']] = [false, true];
|
||||
} else {
|
||||
try {
|
||||
$this->get_pwr_chat($user['id'], false, true);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
case 'userEmpty':
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception('Invalid user provided at key '.$key.': '.var_export($user, true));
|
||||
break;
|
||||
public function add_user($user)
|
||||
{
|
||||
if (!isset($user['access_hash'])) {
|
||||
if (isset($user['username']) && !isset($this->chats[$user['id']])) {
|
||||
$this->cache_pwr_chat($user['username'], false, true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
switch ($user['_']) {
|
||||
case 'user':
|
||||
if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) {
|
||||
$this->chats[$user['id']] = $user;
|
||||
$this->cache_pwr_chat($user['id'], false, true);
|
||||
}
|
||||
case 'userEmpty':
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception('Invalid user provided', $user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function add_chats($chats)
|
||||
{
|
||||
foreach ($chats as $key => $chat) {
|
||||
switch ($chat['_']) {
|
||||
case 'chat':
|
||||
case 'chatEmpty':
|
||||
case 'chatForbidden':
|
||||
if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) {
|
||||
$this->chats[-$chat['id']] = $chat;
|
||||
foreach ($chats as $chat) {
|
||||
$this->add_chat($chat);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->postpone_pwrchat) {
|
||||
$this->pending_pwrchat[-$chat['id']] = [$this->settings['peer']['full_fetch'], true];
|
||||
} else {
|
||||
try {
|
||||
$this->get_pwr_chat(-$chat['id'], $this->settings['peer']['full_fetch'], true);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
public function add_chat($chat)
|
||||
{
|
||||
switch ($chat['_']) {
|
||||
case 'chat':
|
||||
case 'chatEmpty':
|
||||
case 'chatForbidden':
|
||||
if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) {
|
||||
$this->chats[-$chat['id']] = $chat;
|
||||
$this->cache_pwr_chat(-$chat['id'], $this->settings['peer']['full_fetch'], true);
|
||||
}
|
||||
break;
|
||||
case 'channelEmpty':
|
||||
break;
|
||||
case 'channel':
|
||||
case 'channelForbidden':
|
||||
$bot_api_id = $this->to_supergroup($chat['id']);
|
||||
if (!isset($chat['access_hash'])) {
|
||||
if (isset($chat['username']) && !isset($this->chats[$bot_api_id])) {
|
||||
$this->cache_pwr_chat($chat['username'], $this->settings['peer']['full_fetch'], true);
|
||||
}
|
||||
case 'channelEmpty':
|
||||
break;
|
||||
case 'channel':
|
||||
case 'channelForbidden':
|
||||
$bot_api_id = $this->to_supergroup($chat['id']);
|
||||
if (!isset($chat['access_hash'])) {
|
||||
if (isset($chat['username']) && !isset($this->chats[$bot_api_id])) {
|
||||
if ($this->postpone_pwrchat) {
|
||||
$this->pending_pwrchat[$chat['username']] = [$this->settings['peer']['full_fetch'], true];
|
||||
} else {
|
||||
try {
|
||||
$this->get_pwr_chat($chat['username'], $this->settings['peer']['full_fetch'], true);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) {
|
||||
$this->chats[$bot_api_id] = $chat;
|
||||
|
||||
try {
|
||||
if ($this->settings['peer']['full_fetch'] && (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== $this->get_full_info($bot_api_id)['full']['participants_count'])) {
|
||||
if ($this->postpone_pwrchat) {
|
||||
$this->pending_pwrchat[$this->to_supergroup($chat['id'])] = [$this->settings['peer']['full_fetch'], true];
|
||||
} else {
|
||||
$this->get_pwr_chat($this->to_supergroup($chat['id']), $this->settings['peer']['full_fetch'], true);
|
||||
}
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) {
|
||||
$this->chats[$bot_api_id] = $chat;
|
||||
|
||||
if ($this->settings['peer']['full_fetch'] && (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== $this->get_full_info($bot_api_id)['full']['participants_count'])) {
|
||||
$this->cache_pwr_chat($bot_api_id, $this->settings['peer']['full_fetch'], true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception('Invalid chat provided at key '.$key.': '.var_export($chat, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception('Invalid chat provided at key '.$key.': '.var_export($chat, true));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function cache_pwr_chat($id, $full_fetch, $send)
|
||||
{
|
||||
if ($this->postpone_pwrchat) {
|
||||
$this->pending_pwrchat[$id] = [$full_fetch, $send];
|
||||
} else {
|
||||
Loop::defer(function () use ($id, $full_fetch, $send) {
|
||||
try {
|
||||
$this->get_pwr_chat($id, $full_fetch, $send);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,8 +351,22 @@ trait PeerHandler
|
||||
if (is_string($id)) {
|
||||
$id = \danog\MadelineProto\Magic::$bigint ? (float) $id : (int) $id;
|
||||
}
|
||||
if (!isset($this->chats[$id]) && $id < 0 && !$this->is_supergroup($id)) {
|
||||
$this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]);
|
||||
if (!isset($this->chats[$id])) {
|
||||
try {
|
||||
if ($id < 0) {
|
||||
if ($this->is_supergroup($id)) {
|
||||
$this->method_call('channels.getChannels', ['id' => [['access_hash' => 0, 'channel_id' => $this->from_supergroup($id), '_' => 'inputChannel']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
} else {
|
||||
$this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
} else {
|
||||
$this->method_call('users.getUsers', ['id' => [['access_hash' => 0, 'user_id' => $id, '_' => 'inputUser']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger($e->getMessage(), \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (isset($this->chats[$id])) {
|
||||
try {
|
||||
@ -396,6 +406,13 @@ trait PeerHandler
|
||||
if ($id === 'me') {
|
||||
return $this->get_info($this->authorization['user']['id']);
|
||||
}
|
||||
if ($id === 'support') {
|
||||
if (!$this->supportUser) {
|
||||
$this->method_call('help.getSupport', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]);
|
||||
}
|
||||
|
||||
return $this->get_info($this->supportUser);
|
||||
}
|
||||
foreach ($this->chats as $chat) {
|
||||
if (isset($chat['username']) && strtolower($chat['username']) === $id) {
|
||||
return $this->gen_all($chat);
|
||||
@ -462,10 +479,8 @@ trait PeerHandler
|
||||
break;
|
||||
case 'channelForbidden':
|
||||
throw new \danog\MadelineProto\RPCErrorException('CHAT_FORBIDDEN');
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception('Invalid constructor given '.var_export($constructor, true));
|
||||
break;
|
||||
}
|
||||
|
||||
return $res;
|
||||
@ -518,23 +533,13 @@ trait PeerHandler
|
||||
$res[$key] = $full['User'][$key];
|
||||
}
|
||||
}
|
||||
if (isset($full['full']['about'])) {
|
||||
$res['about'] = $full['full']['about'];
|
||||
}
|
||||
if (isset($full['full']['bot_info'])) {
|
||||
$res['bot_info'] = $full['full']['bot_info'];
|
||||
}
|
||||
if (isset($full['full']['phone_calls_available'])) {
|
||||
$res['phone_calls_available'] = $full['full']['phone_calls_available'];
|
||||
}
|
||||
if (isset($full['full']['phone_calls_private'])) {
|
||||
$res['phone_calls_private'] = $full['full']['phone_calls_private'];
|
||||
}
|
||||
if (isset($full['full']['common_chats_count'])) {
|
||||
$res['common_chats_count'] = $full['full']['common_chats_count'];
|
||||
foreach (['about', 'bot_info', 'phone_calls_available', 'phone_calls_private', 'common_chats_count', 'can_pin_message', 'pinned_msg_id', 'notify_settings'] as $key) {
|
||||
if (isset($full['full'][$key])) {
|
||||
$res[$key] = $full['full'][$key];
|
||||
}
|
||||
}
|
||||
if (isset($full['full']['profile_photo']['sizes'])) {
|
||||
$res['photo'] = $this->photosize_to_botapi(end($full['full']['profile_photo']['sizes']), []);
|
||||
$res['photo'] = $this->photosize_to_botapi(end($full['full']['profile_photo']['sizes']), $full['full']['profile_photo']);
|
||||
}
|
||||
/*$bio = '';
|
||||
if ($full['type'] === 'user' && isset($res['username']) && !isset($res['about']) && $fullfetch) {
|
||||
@ -554,11 +559,16 @@ trait PeerHandler
|
||||
$res[$key] = $full['Chat'][$key];
|
||||
}
|
||||
}
|
||||
foreach (['bot_info', 'pinned_msg_id', 'notify_settings'] as $key) {
|
||||
if (isset($full['full'][$key])) {
|
||||
$res[$key] = $full['full'][$key];
|
||||
}
|
||||
}
|
||||
if (isset($res['admins_enabled'])) {
|
||||
$res['all_members_are_administrators'] = $res['admins_enabled'];
|
||||
$res['all_members_are_administrators'] = !$res['admins_enabled'];
|
||||
}
|
||||
if (isset($full['full']['chat_photo']['sizes'])) {
|
||||
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), []);
|
||||
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), $full['full']['chat_photo']);
|
||||
}
|
||||
if (isset($full['full']['exported_invite']['link'])) {
|
||||
$res['invite'] = $full['full']['exported_invite']['link'];
|
||||
@ -574,13 +584,13 @@ trait PeerHandler
|
||||
$res[$key] = $full['Chat'][$key];
|
||||
}
|
||||
}
|
||||
foreach (['can_set_stickers', 'stickerset', 'can_view_participants', 'can_set_username', 'participants_count', 'admins_count', 'kicked_count', 'banned_count', 'migrated_from_chat_id', 'migrated_from_max_id', 'pinned_msg_id', 'about', 'hidden_prehistory', 'available_min_id'] as $key) {
|
||||
foreach (['read_inbox_max_id', 'read_outbox_max_id', 'hidden_prehistory', 'bot_info', 'notify_settings', 'can_set_stickers', 'stickerset', 'can_view_participants', 'can_set_username', 'participants_count', 'admins_count', 'kicked_count', 'banned_count', 'migrated_from_chat_id', 'migrated_from_max_id', 'pinned_msg_id', 'about', 'hidden_prehistory', 'available_min_id', 'can_view_stats', 'online_count'] as $key) {
|
||||
if (isset($full['full'][$key])) {
|
||||
$res[$key] = $full['full'][$key];
|
||||
}
|
||||
}
|
||||
if (isset($full['full']['chat_photo']['sizes'])) {
|
||||
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), []);
|
||||
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), $full['full']['chat_photo']);
|
||||
}
|
||||
if (isset($full['full']['exported_invite']['link'])) {
|
||||
$res['invite'] = $full['full']['exported_invite']['link'];
|
||||
@ -632,7 +642,7 @@ trait PeerHandler
|
||||
$res['participants'][$key] = $newres;
|
||||
}
|
||||
}
|
||||
if (!isset($res['participants']) && isset($res['can_view_participants']) && $res['can_view_participants'] && $fullfetch) {
|
||||
if (!isset($res['participants']) && $fullfetch && in_array($res['type'], ['supergroup', 'channel'])) {
|
||||
$total_count = (isset($res['participants_count']) ? $res['participants_count'] : 0) + (isset($res['admins_count']) ? $res['admins_count'] : 0) + (isset($res['kicked_count']) ? $res['kicked_count'] : 0) + (isset($res['banned_count']) ? $res['banned_count'] : 0);
|
||||
$res['participants'] = [];
|
||||
$limit = 200;
|
||||
|
593
src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php
Normal file
593
src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php
Normal file
@ -0,0 +1,593 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Files module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\TL\TLCallback;
|
||||
use danog\MadelineProto\Tools;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Manages upload and download of files.
|
||||
*/
|
||||
class ReferenceDatabase implements TLCallback
|
||||
{
|
||||
use Tools;
|
||||
// Reference from a document
|
||||
const DOCUMENT_LOCATION = 0;
|
||||
// Reference from a photo
|
||||
const PHOTO_LOCATION = 1;
|
||||
// Reference from a location (can only be photo location)
|
||||
const PHOTO_LOCATION_LOCATION = 2;
|
||||
// Reference from a location (can only be document location)
|
||||
const DOCUMENT_LOCATION_LOCATION = 0;
|
||||
|
||||
// Peer + photo ID
|
||||
const USER_PHOTO_ORIGIN = 0;
|
||||
// Peer (default photo ID)
|
||||
const PEER_PHOTO_ORIGIN = 1;
|
||||
// set ID
|
||||
const STICKER_SET_ID_ORIGIN = 2;
|
||||
// Peer + msg ID
|
||||
const MESSAGE_ORIGIN = 3;
|
||||
//
|
||||
const SAVED_GIFS_ORIGIN = 4;
|
||||
//
|
||||
const STICKER_SET_RECENT_ORIGIN = 5;
|
||||
//
|
||||
const STICKER_SET_FAVED_ORIGIN = 6;
|
||||
// emoticon
|
||||
const STICKER_SET_EMOTICON_ORIGIN = 8;
|
||||
//
|
||||
const WALLPAPER_ORIGIN = 9;
|
||||
|
||||
const LOCATION_CONTEXT = [
|
||||
'inputFileLocation' => self::PHOTO_LOCATION_LOCATION,
|
||||
'inputDocumentFileLocation' => self::DOCUMENT_LOCATION_LOCATION,
|
||||
'inputPhoto' => self::PHOTO_LOCATION,
|
||||
'inputDocument' => self::DOCUMENT_LOCATION,
|
||||
];
|
||||
const METHOD_CONTEXT = [
|
||||
'photos.updateProfilePhoto' => self::USER_PHOTO_ORIGIN,
|
||||
'photos.getUserPhotos' => self::USER_PHOTO_ORIGIN,
|
||||
'photos.uploadProfilePhoto' => self::USER_PHOTO_ORIGIN,
|
||||
'messages.getStickers' => self::STICKER_SET_EMOTICON_ORIGIN,
|
||||
];
|
||||
const CONSTRUCTOR_CONTEXT = [
|
||||
'message' => self::MESSAGE_ORIGIN,
|
||||
'messageService' => self::MESSAGE_ORIGIN,
|
||||
|
||||
'chatFull' => self::PEER_PHOTO_ORIGIN,
|
||||
'channelFull' => self::PEER_PHOTO_ORIGIN,
|
||||
'chat' => self::PEER_PHOTO_ORIGIN,
|
||||
'channel' => self::PEER_PHOTO_ORIGIN,
|
||||
|
||||
'updateUserPhoto' => self::USER_PHOTO_ORIGIN,
|
||||
'user' => self::USER_PHOTO_ORIGIN,
|
||||
'userFull' => self::USER_PHOTO_ORIGIN,
|
||||
|
||||
'message' => self::MESSAGE_ORIGIN,
|
||||
'messageService' => self::MESSAGE_ORIGIN,
|
||||
|
||||
'wallPaper' => self::WALLPAPER_ORIGIN,
|
||||
|
||||
'messages.savedGifs' => self::SAVED_GIFS_ORIGIN,
|
||||
|
||||
'messages.recentStickers' => self::STICKER_SET_RECENT_ORIGIN,
|
||||
'messages.favedStickers' => self::STICKER_SET_FAVED_ORIGIN,
|
||||
'messages.stickerSet' => self::STICKER_SET_ID_ORIGIN,
|
||||
'document' => self::STICKER_SET_ID_ORIGIN,
|
||||
];
|
||||
/**
|
||||
* References indexed by location.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $db = [];
|
||||
private $cache = [];
|
||||
private $cacheContexts = [];
|
||||
private $refreshed = [];
|
||||
private $API;
|
||||
private $refresh = false;
|
||||
private $refreshCount = 0;
|
||||
|
||||
public function __construct($API)
|
||||
{
|
||||
$this->API = $API;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function __sleep()
|
||||
{
|
||||
return ['db', 'API'];
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
}
|
||||
|
||||
public function getMethodCallbacks(): array
|
||||
{
|
||||
return array_fill_keys(array_keys(self::METHOD_CONTEXT), [[$this, 'addOriginMethod']]);
|
||||
}
|
||||
|
||||
public function getMethodBeforeCallbacks(): array
|
||||
{
|
||||
return array_fill_keys(array_keys(self::METHOD_CONTEXT), [[$this, 'addOriginMethodContext']]);
|
||||
}
|
||||
|
||||
public function getConstructorCallbacks(): array
|
||||
{
|
||||
return array_merge(
|
||||
array_fill_keys(['document', 'photo', 'fileLocation'], [[$this, 'addReference']]),
|
||||
array_fill_keys(array_keys(self::CONSTRUCTOR_CONTEXT), [[$this, 'addOrigin']]),
|
||||
['document' => [[$this, 'addReference'], [$this, 'addOrigin']]]
|
||||
);
|
||||
}
|
||||
|
||||
public function getConstructorBeforeCallbacks(): array
|
||||
{
|
||||
return array_fill_keys(array_keys(self::CONSTRUCTOR_CONTEXT), [[$this, 'addOriginContext']]);
|
||||
}
|
||||
|
||||
public function getConstructorSerializeCallbacks(): array
|
||||
{
|
||||
return array_fill_keys(array_keys(self::LOCATION_CONTEXT), [$this, 'populateReferenceSync']);
|
||||
}
|
||||
|
||||
public function getTypeMismatchCallbacks(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
if ($this->cacheContexts) {
|
||||
$this->API->logger->logger('Found '.count($this->cacheContexts).' pending contexts', \danog\MadelineProto\Logger::ERROR);
|
||||
$this->cacheContexts = [];
|
||||
}
|
||||
if ($this->cache) {
|
||||
$this->API->logger->logger('Found pending locations', \danog\MadelineProto\Logger::ERROR);
|
||||
$this->cache = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function addReference(array $location)
|
||||
{
|
||||
if (!$this->cacheContexts) {
|
||||
$this->API->logger->logger('Trying to add reference out of context, report the following message to @danogentili!', \danog\MadelineProto\Logger::ERROR);
|
||||
$frames = [];
|
||||
$previous = '';
|
||||
foreach (debug_backtrace(0) as $k => $frame) {
|
||||
if (isset($frame['function']) && $frame['function'] === 'deserialize') {
|
||||
if (isset($frame['args'][1]['subtype'])) {
|
||||
if ($frame['args'][1]['subtype'] === $previous) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames[] = $frame['args'][1]['subtype'];
|
||||
$previous = $frame['args'][1]['subtype'];
|
||||
} elseif (isset($frame['args'][1]['type'])) {
|
||||
if ($frame['args'][1]['type'] === '') {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($frame['args'][1]['type'] === $previous) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$frames[] = $frame['args'][1]['type'];
|
||||
$previous = $frame['args'][1]['type'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$frames = array_reverse($frames);
|
||||
$tl_trace = array_shift($frames);
|
||||
foreach ($frames as $frame) {
|
||||
$tl_trace .= "['".$frame."']";
|
||||
}
|
||||
$this->API->logger->logger($tl_trace, \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
return false;
|
||||
}
|
||||
$key = count($this->cacheContexts) - 1;
|
||||
switch ($location['_']) {
|
||||
case 'document':
|
||||
$locationType = self::DOCUMENT_LOCATION;
|
||||
break;
|
||||
case 'photo':
|
||||
$locationType = self::PHOTO_LOCATION;
|
||||
break;
|
||||
case 'fileLocation':
|
||||
$locationType = self::PHOTO_LOCATION_LOCATION;
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown location type provided: '.$location['_']);
|
||||
}
|
||||
$this->API->logger->logger("Caching reference from location of type $locationType from {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->cache[$key] = [];
|
||||
}
|
||||
$this->cache[$key][$this->serializeLocation($locationType, $location)] = (string) $location['file_reference'];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function addOriginContext(string $type)
|
||||
{
|
||||
if (!isset(self::CONSTRUCTOR_CONTEXT[$type])) {
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: $type");
|
||||
}
|
||||
$originContext = self::CONSTRUCTOR_CONTEXT[$type];
|
||||
$this->API->logger->logger("Adding origin context $originContext for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->cacheContexts[] = $originContext;
|
||||
}
|
||||
|
||||
public function addOrigin(array $data = [])
|
||||
{
|
||||
$key = count($this->cacheContexts) - 1;
|
||||
if ($key === -1) {
|
||||
throw new \danog\MadelineProto\Exception('Trying to add origin with no origin context set');
|
||||
}
|
||||
$originType = array_pop($this->cacheContexts);
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->API->logger->logger("Removing origin context $originType for {$data['_']}, nothing in the reference cache!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return;
|
||||
}
|
||||
$cache = $this->cache[$key];
|
||||
unset($this->cache[$key]);
|
||||
$origin = [];
|
||||
switch ($data['_']) {
|
||||
case 'message':
|
||||
case 'messageService':
|
||||
$origin['peer'] = $data;
|
||||
$origin['msg_id'] = $data['id'];
|
||||
break;
|
||||
case 'messages.savedGifs':
|
||||
case 'messages.recentStickers':
|
||||
case 'messages.favedStickers':
|
||||
case 'wallPaper':
|
||||
break;
|
||||
case 'user':
|
||||
$origin['max_id'] = $data['photo']['photo_id'];
|
||||
$origin['offset'] = -1;
|
||||
$origin['limit'] = 1;
|
||||
$origin['user_id'] = $data['id'];
|
||||
break;
|
||||
case 'updateUserPhoto':
|
||||
$origin['max_id'] = $data['photo']['photo_id'];
|
||||
$origin['offset'] = -1;
|
||||
$origin['limit'] = 1;
|
||||
$origin['user_id'] = $data['user_id'];
|
||||
break;
|
||||
case 'userFull':
|
||||
$origin['max_id'] = $data['profile_photo']['id'];
|
||||
$origin['offset'] = -1;
|
||||
$origin['limit'] = 1;
|
||||
$origin['user_id'] = $data['user']['id'];
|
||||
break;
|
||||
case 'chatFull':
|
||||
case 'chat':
|
||||
$origin['peer'] = -$data['id'];
|
||||
break;
|
||||
case 'channelFull':
|
||||
case 'channel':
|
||||
$origin['peer'] = $this->API->to_supergroup($data['id']);
|
||||
break;
|
||||
case 'document':
|
||||
foreach ($data['attributes'] as $attribute) {
|
||||
if ($attribute['_'] === 'documentAttributeSticker' && $attribute['stickerset']['_'] !== 'inputStickerSetEmpty') {
|
||||
$origin['stickerset'] = $attribute['stickerset'];
|
||||
}
|
||||
}
|
||||
if (!isset($origin['stickerset'])) {
|
||||
$key = count($this->cacheContexts) - 1;
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->cache[$key] = [];
|
||||
}
|
||||
|
||||
foreach ($cache as $location => $reference) {
|
||||
$this->cache[$key][$location] = $reference;
|
||||
}
|
||||
$this->API->logger->logger("Skipped origin $originType ({$data['_']}) for ".count($cache).' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'messages.stickerSet':
|
||||
$origin['stickerset'] = ['_' => 'inputStickerSetID', 'id' => $data['set']['id'], 'access_hash' => $data['set']['access_hash']];
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: {$data['_']}");
|
||||
}
|
||||
foreach ($cache as $location => $reference) {
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
}
|
||||
$this->API->logger->logger("Added origin $originType ({$data['_']}) to ".count($cache).' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
|
||||
public function addOriginMethodContext(string $type)
|
||||
{
|
||||
if (!isset(self::METHOD_CONTEXT[$type])) {
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: {$type}");
|
||||
}
|
||||
$originContext = self::METHOD_CONTEXT[$type];
|
||||
$this->API->logger->logger("Adding origin context $originContext for {$type}!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->cacheContexts[] = $originContext;
|
||||
}
|
||||
|
||||
public function addOriginMethod(array $data, array $res)
|
||||
{
|
||||
$key = count($this->cacheContexts) - 1;
|
||||
if ($key === -1) {
|
||||
throw new \danog\MadelineProto\Exception('Trying to add origin with no origin context set');
|
||||
}
|
||||
$originType = array_pop($this->cacheContexts);
|
||||
if (!isset($this->cache[$key])) {
|
||||
$this->API->logger->logger("Removing origin context $originType for {$data['_']}, nothing in the reference cache!", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return;
|
||||
}
|
||||
$cache = $this->cache[$key];
|
||||
unset($this->cache[$key]);
|
||||
$origin = [];
|
||||
$body = $data['body'];
|
||||
switch ($data['_']) {
|
||||
case 'photos.updateProfilePhoto':
|
||||
$origin['max_id'] = $res['photo_id'];
|
||||
$origin['offset'] = -1;
|
||||
$origin['limit'] = 1;
|
||||
$origin['user_id'] = $this->API->authorization['user']['id'];
|
||||
break;
|
||||
case 'photos.uploadProfilePhoto':
|
||||
$origin['max_id'] = $res['photo']['id'];
|
||||
$origin['offset'] = -1;
|
||||
$origin['limit'] = 1;
|
||||
$origin['user_id'] = $this->API->authorization['user']['id'];
|
||||
break;
|
||||
case 'photos.getUserPhotos':
|
||||
$origin['user_id'] = $body['user_id'];
|
||||
$origin['offset'] = -1;
|
||||
$origin['limit'] = 1;
|
||||
$count = 0;
|
||||
foreach ($res['photos'] as $photo) {
|
||||
$origin['max_id'] = $photo['id'];
|
||||
|
||||
$location = $this->serializeLocation(self::PHOTO_LOCATION, $photo);
|
||||
if (isset($cache[$location])) {
|
||||
$reference = $cache[$location];
|
||||
unset($cache[$location]);
|
||||
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
$count++;
|
||||
}
|
||||
|
||||
if (isset($photo['sizes'])) {
|
||||
foreach ($photo['sizes'] as $size) {
|
||||
if (isset($size['location'])) {
|
||||
$location = $this->serializeLocation(self::PHOTO_LOCATION_LOCATION, $size['location']);
|
||||
if (isset($cache[$location])) {
|
||||
$reference = $cache[$location];
|
||||
unset($cache[$location]);
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->API->logger->logger("Added origin $originType ({$data['_']}) to $count references", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return;
|
||||
case 'messages.getStickers':
|
||||
$origin['emoticon'] = $body['emoticon'];
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type provided: {$data['_']}");
|
||||
}
|
||||
foreach ($cache as $location => $reference) {
|
||||
$this->storeReference($location, $reference, $originType, $origin);
|
||||
}
|
||||
$this->API->logger->logger("Added origin $originType ({$data['_']}) to ".count($cache).' references', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
|
||||
public function storeReference(string $location, string $reference, int $originType, array $origin)
|
||||
{
|
||||
if (!isset($this->db[$location])) {
|
||||
$this->db[$location] = ['origins' => []];
|
||||
}
|
||||
$this->db[$location]['reference'] = $reference;
|
||||
$this->db[$location]['origins'][$originType] = $origin;
|
||||
|
||||
if ($this->refresh) {
|
||||
$this->refreshed[$location] = true;
|
||||
}
|
||||
|
||||
$key = count($this->cacheContexts) - 1;
|
||||
if ($key >= 0) {
|
||||
$this->cache[$key][$location] = $reference;
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshNext($refresh = false)
|
||||
{
|
||||
if ($this->refreshCount === 1 && !$refresh) {
|
||||
$this->refreshed = [];
|
||||
$this->refreshCount--;
|
||||
$this->refresh = false;
|
||||
} elseif ($this->refreshCount === 0 && $refresh) {
|
||||
$this->refreshed = [];
|
||||
$this->refreshCount++;
|
||||
$this->refresh = true;
|
||||
} elseif ($this->refreshCount === 0 && !$refresh) {
|
||||
} elseif ($refresh) {
|
||||
$this->refreshCount++;
|
||||
} elseif (!$refresh) {
|
||||
$this->refreshCount--;
|
||||
}
|
||||
}
|
||||
|
||||
public function refreshReference(int $locationType, array $location): Promise
|
||||
{
|
||||
return $this->refreshReferenceInternal($this->serializeLocation($locationType, $location));
|
||||
}
|
||||
|
||||
public function refreshReferenceInternal(string $location): Promise
|
||||
{
|
||||
if (isset($this->refreshed[$location])) {
|
||||
$this->API->logger->logger('Reference already refreshed!', \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
return new Success($this->db[$location]['reference']);
|
||||
}
|
||||
|
||||
return call([$this, 'refreshReferenceInternalGenerator'], $location);
|
||||
}
|
||||
|
||||
public function refreshReferenceInternalGenerator(string $location): \Generator
|
||||
{
|
||||
ksort($this->db[$location]['origins']);
|
||||
$count = 0;
|
||||
|
||||
foreach ($this->db[$location]['origins'] as $originType => &$origin) {
|
||||
$count++;
|
||||
$this->API->logger->logger("Try {$count} refreshing file reference with origin type {$originType}", \danog\MadelineProto\Logger::VERBOSE);
|
||||
switch ($originType) {
|
||||
// Peer + msg ID
|
||||
case self::MESSAGE_ORIGIN:
|
||||
if (is_array($origin['peer'])) {
|
||||
$origin['peer'] = $this->API->get_info($origin['peer'])['bot_api_id'];
|
||||
}
|
||||
if ($origin['peer'] < 0) {
|
||||
yield $this->API->method_call_async_read('channels.getMessages', ['channel' => $origin['peer'], 'id' => [$origin['msg_id']]], ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
}
|
||||
yield $this->API->method_call_async_read('messages.getMessages', ['id' => [$origin['msg_id']]], ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
// Peer + photo ID
|
||||
case self::PEER_PHOTO_ORIGIN:
|
||||
if (isset($this->API->full_chats[$origin['peer']]['last_update'])) {
|
||||
$this->API->full_chats[$origin['peer']]['last_update'] = 0;
|
||||
}
|
||||
$this->API->get_full_info($origin['peer']);
|
||||
yield new Success(0);
|
||||
break;
|
||||
// Peer (default photo ID)
|
||||
case self::USER_PHOTO_ORIGIN:
|
||||
yield $this->API->method_call_async_read('photos.getUserPhotos', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
case self::SAVED_GIFS_ORIGIN:
|
||||
yield $this->API->method_call_async_read('messages.getSavedGifs', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
case self::STICKER_SET_ID_ORIGIN:
|
||||
yield $this->API->method_call_async_read('messages.getStickerSet', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
case self::STICKER_SET_RECENT_ORIGIN:
|
||||
yield $this->API->method_call_async_read('messages.getRecentStickers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
case self::STICKER_SET_FAVED_ORIGIN:
|
||||
yield $this->API->method_call_async_read('messages.getFavedStickers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
case self::STICKER_SET_EMOTICON_ORIGIN:
|
||||
yield $this->API->method_call_async_read('messages.getStickers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
case self::WALLPAPER_ORIGIN:
|
||||
yield $this->API->method_call_async_read('account.getWallPapers', $origin, ['datacenter' => $this->API->settings['connection_settings']['default_dc']]);
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\Exception("Unknown origin type $originType");
|
||||
}
|
||||
if (isset($this->refreshed[$location])) {
|
||||
return $this->db[$location]['reference'];
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception('Did not refresh reference');
|
||||
}
|
||||
|
||||
public function populateReferenceSync(array $object): array
|
||||
{
|
||||
return $this->wait($this->populateReference($object));
|
||||
}
|
||||
|
||||
public function populateReference(array $object): Promise
|
||||
{
|
||||
$deferred = new Deferred();
|
||||
$this->getReference(self::LOCATION_CONTEXT[$object['_']], $object)->onResolve(function ($e, $res) use ($deferred, $object) {
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
$object['file_reference'] = $res;
|
||||
$deferred->resolve($object);
|
||||
});
|
||||
|
||||
return $deferred->promise();
|
||||
}
|
||||
|
||||
public function getReference(int $locationType, array $location): Promise
|
||||
{
|
||||
$locationString = $this->serializeLocation($locationType, $location);
|
||||
if (!isset($this->db[$locationString]['reference'])) {
|
||||
if (isset($location['file_reference'])) {
|
||||
$this->API->logger->logger("Using outdated file reference for location of type $locationType object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
return new Success($location['file_reference']);
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception("Could not find file reference for location of type $locationType object {$location['_']}");
|
||||
}
|
||||
$this->API->logger->logger("Getting file reference for location of type $locationType object {$location['_']}", \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
if ($this->refresh) {
|
||||
return $this->refreshReferenceInternal($locationString);
|
||||
}
|
||||
|
||||
return new Success($this->db[$locationString]['reference']);
|
||||
}
|
||||
|
||||
private function serializeLocation(int $locationType, array $location)
|
||||
{
|
||||
switch ($locationType) {
|
||||
case self::DOCUMENT_LOCATION:
|
||||
case self::DOCUMENT_LOCATION_LOCATION:
|
||||
case self::PHOTO_LOCATION:
|
||||
return $locationType.(is_int($location['id']) ? $this->pack_signed_long($location['id']) : $location['id']);
|
||||
case self::PHOTO_LOCATION_LOCATION:
|
||||
$dc_id = $this->pack_signed_int($location['dc_id']);
|
||||
$volume_id = is_int($location['volume_id']) ? $this->pack_signed_long($location['volume_id']) : $location['volume_id'];
|
||||
$local_id = $this->pack_signed_int($location['local_id']);
|
||||
|
||||
return $locationType.$dc_id.$volume_id.$local_id;
|
||||
}
|
||||
}
|
||||
|
||||
public function __debugInfo()
|
||||
{
|
||||
return ['ReferenceDatabase instance '.spl_object_hash($this)];
|
||||
}
|
||||
}
|
@ -1,18 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* ResponseHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Loop;
|
||||
|
||||
/**
|
||||
* Manages responses.
|
||||
*/
|
||||
@ -20,6 +28,7 @@ trait ResponseHandler
|
||||
{
|
||||
public function send_msgs_state_info($req_msg_id, $msg_ids, $datacenter)
|
||||
{
|
||||
// TODO REWRITE
|
||||
$info = '';
|
||||
foreach ($msg_ids as $msg_id) {
|
||||
$cur_info = 0;
|
||||
@ -43,19 +52,24 @@ trait ResponseHandler
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id;
|
||||
}
|
||||
|
||||
public function handle_messages($datacenter)
|
||||
public $n = 0;
|
||||
|
||||
public function handle_messages($datacenter, $actual_datacenter = null)
|
||||
{
|
||||
if ($actual_datacenter) {
|
||||
$datacenter = $actual_datacenter;
|
||||
}
|
||||
|
||||
//$n = $this->n++;
|
||||
$only_updates = true;
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
|
||||
$unset = false;
|
||||
//var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
|
||||
$this->logger->logger((isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['from_container']) ? 'Inside of container, received ' : 'Received ').$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].' from DC '.$datacenter, \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
//var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
|
||||
case 'msgs_ack':
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
$this->ack_outgoing_message_id($msg_id, $datacenter);
|
||||
@ -64,90 +78,67 @@ trait ResponseHandler
|
||||
break;
|
||||
case 'rpc_result':
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter);
|
||||
// Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter);
|
||||
// Acknowledge that the server received my request
|
||||
//$this->logger->logger($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
//$this->logger->logger($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
|
||||
$req_msg_id = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'];
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result'];
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
|
||||
$this->handle_response($req_msg_id, $current_msg_id, $datacenter);
|
||||
|
||||
$only_updates = false;
|
||||
break;
|
||||
case 'future_salts':
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter);
|
||||
// Acknowledge that the server received my request
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
|
||||
$this->handle_response($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $current_msg_id, $datacenter);
|
||||
break;
|
||||
case 'bad_server_salt':
|
||||
case 'bad_msg_notification':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id'], $datacenter);
|
||||
// Acknowledge that the server received my request
|
||||
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id']]);
|
||||
|
||||
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['error_code']) {
|
||||
case 48:
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['new_server_salt'];
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Got bad message notification');
|
||||
case 16:
|
||||
case 17:
|
||||
$this->logger->logger('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['error_code']], \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->sockets[$datacenter]->time_delta = (int) (new \phpseclib\Math\BigInteger(strrev($current_msg_id), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString();
|
||||
$this->logger->logger('Set time delta to '.$this->datacenter->sockets[$datacenter]->time_delta, \danog\MadelineProto\Logger::WARNING);
|
||||
$this->reset_session();
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Got bad message notification');
|
||||
}
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id']]['response'] = $current_msg_id;
|
||||
$this->handle_response($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id'], $current_msg_id, $datacenter);
|
||||
break;
|
||||
case 'pong':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]);
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id'], $datacenter);
|
||||
// Acknowledge that the server received my request
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]['response'] = $current_msg_id;
|
||||
$this->handle_response($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id'], $current_msg_id, $datacenter);
|
||||
break;
|
||||
case 'new_session_created':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['server_salt'];
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter);
|
||||
|
||||
// Acknowledge that I received the server's response
|
||||
if ($this->authorized === self::LOGGED_IN && !$this->initing_authorization && $this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key !== null) {
|
||||
$this->get_updates_difference();
|
||||
Loop::defer([$this, 'get_updates_difference']);
|
||||
}
|
||||
$unset = true;
|
||||
//foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $message_id) {
|
||||
// $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['sent'] = 0;
|
||||
//}
|
||||
// Loop::defer([$this->datacenter->sockets[$datacenter]->checker, 'resume']);
|
||||
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'msg_container':
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['messages'] as $message) {
|
||||
$this->check_message_id($message['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]);
|
||||
$this->datacenter->sockets[$datacenter]->check_message_id($message['msg_id'], ['outgoing' => false, 'container' => true]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body'], 'from_container' => true];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message['msg_id']] = $message['msg_id'];
|
||||
$this->handle_messages($datacenter);
|
||||
}
|
||||
$unset = true;
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'msg_copy':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter);
|
||||
// Acknowledge that I received the server's response
|
||||
@ -155,44 +146,42 @@ trait ResponseHandler
|
||||
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id'], $datacenter);
|
||||
// Acknowledge that I received the server's response
|
||||
} else {
|
||||
$this->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]);
|
||||
$this->datacenter->sockets[$datacenter]->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'container' => true]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id'];
|
||||
$this->handle_messages($datacenter);
|
||||
}
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'http_wait':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$this->logger->logger($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], \danog\MadelineProto\Logger::NOTICE);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
break;
|
||||
case 'msgs_state_info':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
$unset = true;
|
||||
$this->handle_response($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $current_msg_id, $datacenter);
|
||||
break;
|
||||
case 'msgs_state_req':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->send_msgs_state_info($current_msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter);
|
||||
break;
|
||||
case 'msgs_all_info':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
|
||||
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
|
||||
$status = 'Status for message id '.$msg_id.': ';
|
||||
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) !== 0) {
|
||||
$this->ack_outgoing_message_id($msg_id, $datacenter);
|
||||
$this->got_response_for_outgoing_message_id($msg_id, $datacenter);
|
||||
}
|
||||
foreach (self::MSGS_INFO_FLAGS as $flag => $description) {
|
||||
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & $flag) !== 0) {
|
||||
@ -203,7 +192,7 @@ trait ResponseHandler
|
||||
}
|
||||
break;
|
||||
case 'msg_detailed_info':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']])) {
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
|
||||
@ -214,7 +203,7 @@ trait ResponseHandler
|
||||
}
|
||||
}
|
||||
case 'msg_new_detailed_info':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
|
||||
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id'], $datacenter);
|
||||
@ -224,7 +213,7 @@ trait ResponseHandler
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
break;
|
||||
case 'msg_resend_req':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
$ok = true;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
@ -242,7 +231,7 @@ trait ResponseHandler
|
||||
}
|
||||
break;
|
||||
case 'msg_resend_ans_req':
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->send_msgs_state_info($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter);
|
||||
@ -253,45 +242,34 @@ trait ResponseHandler
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->check_in_seq_no($datacenter, $current_msg_id);
|
||||
$this->datacenter->sockets[$datacenter]->check_in_seq_no($current_msg_id);
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter);
|
||||
// Acknowledge that I received the server's response
|
||||
$response_type = $this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'])['type'];
|
||||
|
||||
switch ($response_type) {
|
||||
case 'Updates':
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users'])) {
|
||||
$this->add_users($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats'])) {
|
||||
$this->add_chats($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats']);
|
||||
}
|
||||
|
||||
if (strpos($datacenter, 'cdn') === false) {
|
||||
$this->handle_updates($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
Loop::defer([$this, 'handle_updates'], $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
}
|
||||
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users'])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats'])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats']);
|
||||
}
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
|
||||
$only_updates = true && $only_updates;
|
||||
break;
|
||||
default:
|
||||
$only_updates = false;
|
||||
$this->logger->logger('Trying to assign a response of type '.$response_type.' to its request...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $key => $expecting) {
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $key => $expecting_msg_id) {
|
||||
$expecting = $this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting_msg_id];
|
||||
|
||||
$this->logger->logger('Does the request of return type '.$expecting['type'].' match?', \danog\MadelineProto\Logger::VERBOSE);
|
||||
if ($response_type === $expecting['type']) {
|
||||
$this->logger->logger('Yes', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$key]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->handle_response($expecting_msg_id, $current_msg_id, $datacenter);
|
||||
break 2;
|
||||
}
|
||||
$this->logger->logger('No', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -302,128 +280,259 @@ trait ResponseHandler
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users'])) {
|
||||
$this->add_users($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats'])) {
|
||||
$this->add_chats($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['user'])) {
|
||||
$this->add_users([$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['user']]);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chat'])) {
|
||||
$this->add_chats([$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chat']]);
|
||||
}
|
||||
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['users'])) {
|
||||
$this->add_users($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['users']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['chats'])) {
|
||||
$this->add_chats($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['chats']);
|
||||
}
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['_'])) {
|
||||
switch ($this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['_'])['type']) {
|
||||
case 'Update':
|
||||
$this->handle_update($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($unset) {
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
|
||||
}
|
||||
}
|
||||
//$this->n--;
|
||||
|
||||
return $only_updates;
|
||||
}
|
||||
|
||||
public function handle_rpc_error($server_answer, &$aargs)
|
||||
public function handle_reject($datacenter, &$request, $data)
|
||||
{
|
||||
if (in_array($server_answer['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
|
||||
throw new \danog\MadelineProto\PTSException($server_answer['error_message']);
|
||||
if (isset($request['promise']) && is_object($request['promise'])) {
|
||||
Loop::defer(function () use (&$request, $data) {
|
||||
$request['promise']->fail($data);
|
||||
unset($request['promise']);
|
||||
});
|
||||
} elseif (isset($request['container'])) {
|
||||
foreach ($request['container'] as $message_id) {
|
||||
$this->handle_reject($datacenter, $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id], $data);
|
||||
}
|
||||
} else {
|
||||
$this->logger->logger('Rejecting: already got response for '.(isset($request['_']) ? $request['_'] : '-'));
|
||||
$this->logger->logger("Rejecting: $data");
|
||||
}
|
||||
switch ($server_answer['error_code']) {
|
||||
case 500:
|
||||
if ($server_answer['error_message'] === 'MSG_WAIT_FAILED') {
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Re-executing query after server error...');
|
||||
case 303:
|
||||
$this->datacenter->curdc = $aargs['datacenter'] = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
|
||||
public function handle_response($request_id, $response_id, $datacenter)
|
||||
{
|
||||
//var_dumP("Response ".bin2hex($request_id));
|
||||
$response = &$this->datacenter->sockets[$datacenter]->incoming_messages[$response_id]['content'];
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$response_id]['content']);
|
||||
$request = &$this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id];
|
||||
|
||||
if (isset($aargs['file']) && $aargs['file'] && isset($this->datacenter->sockets[$aargs['datacenter'].'_media'])) {
|
||||
\danog\MadelineProto\Logger::log('Using media DC');
|
||||
$aargs['datacenter'] .= '_media';
|
||||
}
|
||||
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->datacenter->sockets[$datacenter]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited'] === false)) {
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['connection_inited'] = true;
|
||||
}
|
||||
if (isset($response['_'])) {
|
||||
switch ($response['_']) {
|
||||
case 'rpc_error':
|
||||
if (in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\PTSException($response['error_message']));
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
|
||||
case 401:
|
||||
switch ($server_answer['error_message']) {
|
||||
case 'USER_DEACTIVATED':
|
||||
case 'SESSION_REVOKED':
|
||||
case 'SESSION_EXPIRED':
|
||||
$this->logger->logger($server_answer['error_message'], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
foreach ($this->datacenter->sockets as $socket) {
|
||||
$socket->temp_auth_key = null;
|
||||
$socket->auth_key = null;
|
||||
$socket->authorized = false;
|
||||
return;
|
||||
}
|
||||
if (strpos($response['error_message'], 'FILE_REFERENCE_') === 0) {
|
||||
$this->logger->logger("Got {$response['error_message']}, refreshing file reference and repeating method call...");
|
||||
|
||||
$request['refresh_references'] = true;
|
||||
if (isset($request['serialized_body'])) {
|
||||
unset($request['serialized_body']);
|
||||
}
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorization = null;
|
||||
$this->init_authorization();
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
case 'AUTH_KEY_UNREGISTERED':
|
||||
case 'AUTH_KEY_INVALID':
|
||||
if ($this->authorized !== self::LOGGED_IN) {
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
}
|
||||
$this->logger->logger('Auth key not registered, resetting temporary and permanent auth keys...', \danog\MadelineProto\Logger::ERROR);
|
||||
Loop::defer([$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->auth_key = null;
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->authorized = false;
|
||||
if ($this->authorized_dc === $aargs['datacenter'] && $this->authorized === self::LOGGED_IN) {
|
||||
$this->logger->logger('Permanent auth key was main authorized key, logging out...', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
foreach ($this->datacenter->sockets as $socket) {
|
||||
$socket->temp_auth_key = null;
|
||||
$socket->auth_key = null;
|
||||
$socket->authorized = false;
|
||||
return;
|
||||
}
|
||||
switch ($response['error_code']) {
|
||||
case 500:
|
||||
if ($response['error_message'] === 'MSG_WAIT_FAILED') {
|
||||
$this->datacenter->sockets[$datacenter]->call_queue[$request['queue']] = [];
|
||||
}
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorization = null;
|
||||
$this->init_authorization();
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
}
|
||||
$this->init_authorization();
|
||||
Loop::defer([$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
|
||||
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
case 'AUTH_KEY_PERM_EMPTY':
|
||||
if ($this->authorized !== self::LOGGED_IN) {
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
}
|
||||
$this->logger->logger('Temporary auth key not bound, resetting temporary auth key...', \danog\MadelineProto\Logger::ERROR);
|
||||
return;
|
||||
case 303:
|
||||
$old_datacenter = $datacenter;
|
||||
$this->datacenter->curdc = $datacenter = (int) preg_replace('/[^0-9]+/', '', $response['error_message']);
|
||||
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
// idk
|
||||
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
}
|
||||
case 420:
|
||||
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
|
||||
$limit = isset($aargs['FloodWaitLimit']) ? $aargs['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt'];
|
||||
if (is_numeric($seconds) && $seconds < $limit) {
|
||||
$this->logger->logger('Flood, waiting '.$seconds.' seconds...', \danog\MadelineProto\Logger::NOTICE);
|
||||
sleep($seconds);
|
||||
if (isset($request['file']) && $request['file'] && isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
||||
\danog\MadelineProto\Logger::log('Using media DC');
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\Exception('Re-executing query...');
|
||||
}
|
||||
if (isset($request['user_related']) && $request['user_related']) {
|
||||
$this->settings['connection_settings']['default_dc'] = $this->authorized_dc = $this->datacenter->curdc;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
Loop::defer([$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter, 'old_datacenter' => $old_datacenter]);
|
||||
|
||||
return;
|
||||
case 401:
|
||||
switch ($response['error_message']) {
|
||||
case 'USER_DEACTIVATED':
|
||||
case 'SESSION_REVOKED':
|
||||
case 'SESSION_EXPIRED':
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
|
||||
$this->logger->logger($response['error_message'], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
foreach ($this->datacenter->sockets as $socket) {
|
||||
$socket->temp_auth_key = null;
|
||||
$socket->session_id = null;
|
||||
$socket->auth_key = null;
|
||||
$socket->authorized = false;
|
||||
}
|
||||
|
||||
if ($response['error_message'] === 'USER_DEACTIVATED') {
|
||||
$this->logger->logger('!!!!!!! WARNING !!!!!!!', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger("Telegram's flood prevention system suspended this account.", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->logger->logger('To continue, manual verification is required.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$phone = isset($this->authorization['user']['phone']) ? '+'.$this->authorization['user']['phone'] : 'you are currently using';
|
||||
$this->logger->logger('Send an email to recover@telegram.org, asking to unban the phone number '.$phone.', and quickly describe what will you do with this phone number.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('Then login again.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('If you intentionally deleted this account, ignore this message.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorization = null;
|
||||
|
||||
Loop::defer(function () use ($datacenter, &$request, &$response) {
|
||||
$this->init_authorization();
|
||||
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code']));
|
||||
});
|
||||
|
||||
return;
|
||||
case 'AUTH_KEY_UNREGISTERED':
|
||||
case 'AUTH_KEY_INVALID':
|
||||
if ($this->authorized !== self::LOGGED_IN) {
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
|
||||
Loop::defer(function () use ($datacenter, &$request, &$response) {
|
||||
$this->init_authorization();
|
||||
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code']));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
$this->datacenter->sockets[$datacenter]->session_id = null;
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
$this->datacenter->sockets[$datacenter]->auth_key = null;
|
||||
$this->datacenter->sockets[$datacenter]->authorized = false;
|
||||
|
||||
$this->logger->logger('Auth key not registered, resetting temporary and permanent auth keys...', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
if ($this->authorized_dc === $datacenter && $this->authorized === self::LOGGED_IN) {
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
|
||||
$this->logger->logger('Permanent auth key was main authorized key, logging out...', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
foreach ($this->datacenter->sockets as $socket) {
|
||||
$socket->temp_auth_key = null;
|
||||
$socket->auth_key = null;
|
||||
$socket->authorized = false;
|
||||
}
|
||||
$this->logger->logger('!!!!!!! WARNING !!!!!!!', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger("Telegram's flood prevention system suspended this account.", \danog\MadelineProto\Logger::ERROR);
|
||||
$this->logger->logger('To continue, manual verification is required.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$phone = isset($this->authorization['user']['phone']) ? '+'.$this->authorization['user']['phone'] : 'you are currently using';
|
||||
$this->logger->logger('Send an email to recover@telegram.org, asking to unban the phone number '.$phone.', and quickly describe what will you do with this phone number.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('Then login again.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
$this->logger->logger('If you intentionally deleted this account, ignore this message.', \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
|
||||
$this->authorized = self::NOT_LOGGED_IN;
|
||||
$this->authorization = null;
|
||||
|
||||
Loop::defer(function () use ($datacenter, &$request, &$response) {
|
||||
$this->init_authorization();
|
||||
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code']));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
Loop::defer(function () use ($request_id, $datacenter) {
|
||||
$this->init_authorization();
|
||||
|
||||
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
});
|
||||
|
||||
return;
|
||||
case 'AUTH_KEY_PERM_EMPTY':
|
||||
$this->logger->logger('Temporary auth key not bound, resetting temporary auth key...', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
Loop::defer(function () use ($request_id, $datacenter) {
|
||||
$this->init_authorization();
|
||||
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code']));
|
||||
|
||||
return;
|
||||
case 420:
|
||||
$seconds = preg_replace('/[^0-9]+/', '', $response['error_message']);
|
||||
$limit = isset($aargs['FloodWaitLimit']) ? $aargs['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt'];
|
||||
if (is_numeric($seconds) && $seconds < $limit) {
|
||||
$this->logger->logger('Flood, waiting '.$seconds.' seconds before repeating async call...', \danog\MadelineProto\Logger::NOTICE);
|
||||
Loop::delay($seconds * 1000, [$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
|
||||
return;
|
||||
}
|
||||
// no break
|
||||
default:
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException($response['error_message'], $response['error_code']));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
case 'boolTrue':
|
||||
case 'boolFalse':
|
||||
$response = $response['_'] === 'boolTrue';
|
||||
break;
|
||||
case 'bad_server_salt':
|
||||
case 'bad_msg_notification':
|
||||
|
||||
$this->logger->logger('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$response['error_code']], \danog\MadelineProto\Logger::WARNING);
|
||||
switch ($response['error_code']) {
|
||||
case 48:
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $response['new_server_salt'];
|
||||
Loop::defer([$this, 'method_recall'], ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
|
||||
return;
|
||||
case 16:
|
||||
case 17:
|
||||
$this->datacenter->sockets[$datacenter]->time_delta = (int) (new \phpseclib\Math\BigInteger(strrev($response_id), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString();
|
||||
$this->logger->logger('Set time delta to '.$this->datacenter->sockets[$datacenter]->time_delta, \danog\MadelineProto\Logger::WARNING);
|
||||
$this->reset_session();
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
Loop::defer(function () use ($request_id, $datacenter) {
|
||||
$this->init_authorization();
|
||||
$this->method_recall('', ['message_id' => $request_id, 'datacenter' => $datacenter]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
$this->handle_reject($datacenter, $request, new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$response['error_code']], $response['error_code']));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($request['promise'])) {
|
||||
$this->logger->logger('Response: already got response for '.(isset($request['_']) ? $request['_'] : '-').' with message ID '.$this->unpack_signed_long($request_id));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($request['botAPI']) && $request['botAPI']) {
|
||||
$response = $this->MTProto_to_botAPI($response);
|
||||
}
|
||||
unset($request);
|
||||
$this->got_response_for_outgoing_message_id($request_id, $datacenter);
|
||||
Loop::defer(function () use ($request_id, $response, $datacenter) {
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise']->resolve($response);
|
||||
unset($this->datacenter->sockets[$datacenter]->outgoing_messages[$request_id]['promise']);
|
||||
});
|
||||
}
|
||||
|
||||
public function handle_pending_updates()
|
||||
@ -443,11 +552,15 @@ trait ResponseHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_updates($updates)
|
||||
public function handle_updates($updates, $actual_updates = null)
|
||||
{
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
}
|
||||
if ($actual_updates) {
|
||||
$updates = $actual_updates;
|
||||
}
|
||||
|
||||
if ($this->postpone_updates) {
|
||||
$this->logger->logger('Postpone update handling', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->pending_updates[] = $updates;
|
||||
@ -467,53 +580,59 @@ trait ResponseHandler
|
||||
}
|
||||
}
|
||||
switch ($updates['_']) {
|
||||
case 'updates':
|
||||
case 'updatesCombined':
|
||||
foreach ($updates['updates'] as $update) {
|
||||
$this->handle_update($update, $opts);
|
||||
}
|
||||
break;
|
||||
case 'updateShort':
|
||||
$this->handle_update($updates['update'], $opts);
|
||||
break;
|
||||
case 'updateShortMessage':
|
||||
case 'updateShortChatMessage':
|
||||
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
|
||||
$to_id = isset($updates['chat_id']) ? -$updates['chat_id'] : ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
|
||||
if (!$this->peer_isset($from_id) || !$this->peer_isset($to_id) || isset($updates['via_bot_id']) && !$this->peer_isset($updates['via_bot_id']) || isset($updates['entities']) && !$this->entities_peer_isset($updates['entities']) || isset($updates['fwd_from']) && !$this->fwd_peer_isset($updates['fwd_from'])) {
|
||||
$this->logger->logger('getDifference: good - getting user for updateShortMessage', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
$message = $updates;
|
||||
$message['_'] = 'message';
|
||||
$message['from_id'] = $from_id;
|
||||
case 'updates':
|
||||
case 'updatesCombined':
|
||||
foreach ($updates['updates'] as $update) {
|
||||
$this->handle_update($update, $opts);
|
||||
}
|
||||
break;
|
||||
case 'updateShort':
|
||||
$this->handle_update($updates['update'], $opts);
|
||||
break;
|
||||
case 'updateShortMessage':
|
||||
case 'updateShortChatMessage':
|
||||
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
|
||||
$to_id = isset($updates['chat_id']) ? -$updates['chat_id'] : ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
|
||||
if (!$this->peer_isset($from_id) || !$this->peer_isset($to_id) || isset($updates['via_bot_id']) && !$this->peer_isset($updates['via_bot_id']) || isset($updates['entities']) && !$this->entities_peer_isset($updates['entities']) || isset($updates['fwd_from']) && !$this->fwd_peer_isset($updates['fwd_from'])) {
|
||||
$this->logger->logger('getDifference: good - getting user for updateShortMessage', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
$message = $updates;
|
||||
$message['_'] = 'message';
|
||||
$message['from_id'] = $from_id;
|
||||
|
||||
try {
|
||||
$message['to_id'] = $this->get_info($to_id)['Peer'];
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger('Still did not get user in database, postponing update', \danog\MadelineProto\Logger::ERROR);
|
||||
//$this->pending_updates[] = $updates;
|
||||
try {
|
||||
$message['to_id'] = $this->get_info($to_id)['Peer'];
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger('Still did not get user in database, postponing update', \danog\MadelineProto\Logger::ERROR);
|
||||
//$this->pending_updates[] = $updates;
|
||||
break;
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger('Still did not get user in database, postponing update', \danog\MadelineProto\Logger::ERROR);
|
||||
//$this->pending_updates[] = $updates;
|
||||
break;
|
||||
}
|
||||
$update = ['_' => 'updateNewMessage', 'message' => $message, 'pts' => $updates['pts'], 'pts_count' => $updates['pts_count']];
|
||||
$this->handle_update($update, $opts);
|
||||
break;
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
$this->logger->logger('Still did not get user in database, postponing update', \danog\MadelineProto\Logger::ERROR);
|
||||
//$this->pending_updates[] = $updates;
|
||||
case 'updateShortSentMessage':
|
||||
//$this->set_update_state(['date' => $updates['date']]);
|
||||
break;
|
||||
}
|
||||
$update = ['_' => 'updateNewMessage', 'message' => $message, 'pts' => $updates['pts'], 'pts_count' => $updates['pts_count']];
|
||||
$this->handle_update($update, $opts);
|
||||
break;
|
||||
case 'updateShortSentMessage':
|
||||
//$this->set_update_state(['date' => $updates['date']]);
|
||||
break;
|
||||
case 'updatesTooLong':
|
||||
$this->get_updates_difference();
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: '.var_export($updates, true));
|
||||
break;
|
||||
}
|
||||
case 'updatesTooLong':
|
||||
$this->get_updates_difference();
|
||||
break;
|
||||
default:
|
||||
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: '.var_export($updates, true));
|
||||
break;
|
||||
}
|
||||
} finally {
|
||||
$this->postpone_updates = false;
|
||||
}
|
||||
if ($this->updates && $this->update_deferred) {
|
||||
$d = $this->update_deferred;
|
||||
$this->update_deferred = null;
|
||||
|
||||
$d->resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages message ids.
|
||||
*/
|
||||
trait SaltHandler
|
||||
{
|
||||
public function add_salts($salts)
|
||||
{
|
||||
foreach ($salts as $salt) {
|
||||
$this->add_salt($salt['valid_since'], $salt['valid_until'], $salt['salt']);
|
||||
}
|
||||
}
|
||||
|
||||
public function add_salt($valid_since, $valid_until, $salt)
|
||||
{
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt])) {
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_future_salts($salt)
|
||||
{
|
||||
$this->method_call('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
}
|
@ -1,81 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* SeqNoHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use danog\MadelineProto\MTProto;
|
||||
|
||||
/**
|
||||
* Manages sequence number.
|
||||
*/
|
||||
trait SeqNoHandler
|
||||
{
|
||||
public function generate_out_seq_no($datacenter, $content_related)
|
||||
{
|
||||
$in = $content_related ? 1 : 0;
|
||||
$value = $this->datacenter->sockets[$datacenter]->session_out_seq_no;
|
||||
$this->datacenter->sockets[$datacenter]->session_out_seq_no += $in;
|
||||
//$this->logger->logger("OUT $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_out_seq_no);
|
||||
return $value * 2 + $in;
|
||||
}
|
||||
|
||||
public function check_in_seq_no($datacenter, $current_msg_id)
|
||||
{
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) && ($seq_no = $this->generate_in_seq_no($datacenter, $this->content_related($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']))) !== $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) {
|
||||
//$this->logger->logger('SECURITY WARNING: Seqno mismatch (should be '.$seq_no.', is '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no'].', '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].')', \danog\MadelineProto\Logger::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_in_seq_no($datacenter, $content_related)
|
||||
{
|
||||
$in = $content_related ? 1 : 0;
|
||||
$value = $this->datacenter->sockets[$datacenter]->session_in_seq_no;
|
||||
$this->datacenter->sockets[$datacenter]->session_in_seq_no += $in;
|
||||
//$this->logger->logger("IN $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_in_seq_no);
|
||||
return $value * 2 + $in;
|
||||
}
|
||||
|
||||
public function content_related($method)
|
||||
{
|
||||
return isset($method['_']) ? !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',
|
||||
]) : true;
|
||||
$method = is_array($method) && isset($method['_']) ? $method['_'] : $method;
|
||||
|
||||
return is_string($method) ? !in_array($method, MTProto::NOT_CONTENT_RELATED) : true;
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* UpdateHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Deferred;
|
||||
use Amp\Delayed;
|
||||
use function Amp\Promise\any;
|
||||
|
||||
/**
|
||||
* Manages updates.
|
||||
*/
|
||||
@ -45,71 +55,35 @@ trait UpdateHandler
|
||||
return;
|
||||
}
|
||||
$this->updates[$this->updates_key++] = $update;
|
||||
//$this->logger->logger(['Stored ', $update);
|
||||
$this->logger->logger('Stored ');
|
||||
}
|
||||
|
||||
public function get_updates($params = [])
|
||||
public function get_updates_async($params = [])
|
||||
{
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
$this->settings['updates']['handle_updates'] = true;
|
||||
$this->datacenter->sockets[$this->settings['connection_settings']['default_dc']]->updater->start();
|
||||
}
|
||||
if (!$this->settings['updates']['run_callback']) {
|
||||
$this->settings['updates']['run_callback'] = true;
|
||||
}
|
||||
array_walk($this->calls, function ($controller, $id) {
|
||||
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
|
||||
$controller->discard();
|
||||
}
|
||||
});
|
||||
$time = microtime(true);
|
||||
|
||||
try {
|
||||
if (!$this->is_http($this->datacenter->curdc) && !$this->altervista) {
|
||||
try {
|
||||
$waiting = $this->datacenter->select();
|
||||
if (count($waiting)) {
|
||||
$tries = 10;
|
||||
while (count($waiting) && $tries--) {
|
||||
$dc = $waiting[0];
|
||||
if (($error = $this->recv_message($dc)) !== true) {
|
||||
if ($error === -404) {
|
||||
if ($this->datacenter->sockets[$dc]->temp_auth_key !== null) {
|
||||
$this->logger->logger('WARNING: Resetting auth key...', \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->sockets[$dc]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
$params = array_merge(self::DEFAULT_GETUPDATES_PARAMS, $params);
|
||||
|
||||
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
}
|
||||
}
|
||||
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
$only_updates = $this->handle_messages($dc);
|
||||
$waiting = $this->datacenter->select(true);
|
||||
}
|
||||
} else {
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
} else {
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
if (time() - $this->last_getdifference > $this->settings['updates']['getdifference_interval']) {
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->rpc !== 'RPC_CALL_FAIL') {
|
||||
throw $e;
|
||||
}
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->connect_to_all_dcs();
|
||||
if (empty($this->updates)) {
|
||||
$this->update_deferred = new Deferred();
|
||||
yield any([$this->update_deferred->promise(), new Delayed($params['timeout'] * 1000)]);
|
||||
}
|
||||
$default_params = ['offset' => 0, 'limit' => null, 'timeout' => 0];
|
||||
$params = array_merge($default_params, $params);
|
||||
$params['timeout'] = (int) ($params['timeout'] * 1000000 - (microtime(true) - $time));
|
||||
usleep($params['timeout'] > 0 ? $params['timeout'] : 0);
|
||||
|
||||
if (empty($this->updates)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($params['offset'] < 0) {
|
||||
$params['offset'] = array_reverse(array_keys((array) $this->updates))[abs($params['offset']) - 1];
|
||||
}
|
||||
@ -127,6 +101,10 @@ trait UpdateHandler
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->updates)) {
|
||||
$this->updates_key = 0;
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
@ -287,7 +265,7 @@ trait UpdateHandler
|
||||
return $this->updates_state;
|
||||
}
|
||||
|
||||
public function get_updates_difference()
|
||||
public function get_updates_difference($w = null)
|
||||
{
|
||||
if (!$this->settings['updates']['handle_updates']) {
|
||||
return;
|
||||
@ -315,6 +293,7 @@ trait UpdateHandler
|
||||
$this->postpone_updates = true;
|
||||
$this->updates_state['sync_loading'] = true;
|
||||
$this->last_getdifference = time();
|
||||
$this->datacenter->sockets[$this->settings['connection_settings']['default_dc']]->updater->resume();
|
||||
|
||||
try {
|
||||
switch ($difference['_']) {
|
||||
@ -348,6 +327,15 @@ trait UpdateHandler
|
||||
$this->updates_state['sync_loading'] = false;
|
||||
}
|
||||
$this->handle_pending_updates();
|
||||
|
||||
if ($this->updates && $this->update_deferred) {
|
||||
$d = $this->update_deferred;
|
||||
$this->update_deferred = null;
|
||||
|
||||
$d->resolve();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_updates_state()
|
||||
@ -558,7 +546,7 @@ trait UpdateHandler
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->calls[$update['phone_call']['id']]->discard(['_' => 'phoneCallDiscardReasonHangup'], [], $update['phone_call']['need_debug']);
|
||||
return $this->calls[$update['phone_call']['id']]->discard($update['phone_call']['reason'], [], $update['phone_call']['need_debug']);
|
||||
}
|
||||
}
|
||||
if ($update['_'] === 'updateNewEncryptedMessage' && !isset($update['message']['decrypted_message'])) {
|
||||
@ -629,7 +617,7 @@ trait UpdateHandler
|
||||
$this->logger->logger('Saving an update of type '.$update['_'].'...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict'] && isset($this->settings['pwr']['update_handler'])) {
|
||||
$this->pwr_update_handler($update);
|
||||
} else {
|
||||
} elseif ($this->settings['updates']['run_callback']) {
|
||||
$this->get_updates_update_handler($update);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,14 +1,20 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* MyTelegramOrgWrapper module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -18,6 +24,7 @@ namespace danog\MadelineProto;
|
||||
class MyTelegramOrgWrapper
|
||||
{
|
||||
private $logged = false;
|
||||
private $hash = '';
|
||||
|
||||
public function __construct($number)
|
||||
{
|
||||
@ -140,10 +147,11 @@ class MyTelegramOrgWrapper
|
||||
curl_close($ch);
|
||||
$title = explode('</title>', explode('<title>', $result)[1])[0];
|
||||
switch ($title) {
|
||||
case 'App configuration': return true;
|
||||
case 'Create new application': $this->creation_hash = explode('"/>', explode('<input type="hidden" name="hash" value="', $result)[1])[0];
|
||||
case 'App configuration':return true;
|
||||
case 'Create new application':
|
||||
$this->creation_hash = explode('"/>', explode('<input type="hidden" name="hash" value="', $result)[1])[0];
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new Exception($title);
|
||||
@ -180,13 +188,13 @@ return false;
|
||||
$cose = explode('<label for="app_id" class="col-md-4 text-right control-label">App api_id:</label>
|
||||
<div class="col-md-7">
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();"><strong>', $result);
|
||||
$asd = explode('</strong></span>', $cose['1']);
|
||||
$api_id = $asd['0'];
|
||||
$asd = explode('</strong></span>', $cose[1]);
|
||||
$api_id = $asd[0];
|
||||
$cose = explode('<label for="app_hash" class="col-md-4 text-right control-label">App api_hash:</label>
|
||||
<div class="col-md-7">
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();">', $result);
|
||||
$asd = explode('</span>', $cose['1']);
|
||||
$api_hash = $asd['0'];
|
||||
$asd = explode('</span>', $cose[1]);
|
||||
$api_hash = $asd[0];
|
||||
|
||||
return ['api_id' => (int) $api_id, 'api_hash' => $api_hash];
|
||||
}
|
||||
@ -199,6 +207,7 @@ return false;
|
||||
if ($this->has_app()) {
|
||||
throw new Exception('The app was already created!');
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, 'https://my.telegram.org/apps/create');
|
||||
@ -227,6 +236,10 @@ return false;
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($result) {
|
||||
throw new Exception($result);
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
|
||||
curl_setopt($ch, CURLOPT_URL, 'https://my.telegram.org/apps');
|
||||
@ -254,6 +267,13 @@ return false;
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$title = explode('</title>', explode('<title>', $result)[1])[0];
|
||||
if ($title === 'Create new application') {
|
||||
$this->creation_hash = explode('"/>', explode('<input type="hidden" name="hash" value="', $result)[1])[0];
|
||||
|
||||
throw new \danog\MadelineProto\Exception('App creation failed');
|
||||
}
|
||||
|
||||
$cose = explode('<label for="app_id" class="col-md-4 text-right control-label">App api_id:</label>
|
||||
<div class="col-md-7">
|
||||
<span class="form-control input-xlarge uneditable-input" onclick="this.select();"><strong>', $result);
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* NothingInTheSocketException module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* PTSException module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
36
src/danog/MadelineProto/PayloadStream.php
Normal file
36
src/danog/MadelineProto/PayloadStream.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* PayloadStream.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use Amp\ByteStream\InputStream;
|
||||
use Amp\Promise;
|
||||
|
||||
/**
|
||||
* PayloadStream.
|
||||
*
|
||||
* Represents an MTProto payload
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class PayloadStream implements InputStream
|
||||
{
|
||||
public function read(): Promise
|
||||
{
|
||||
}
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Proxy module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* RPCErrorException module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
@ -33,7 +39,11 @@ class RPCErrorException extends \Exception
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess(), $this->rpc, $this->file, $this->line.PHP_EOL.PHP_EOL).PHP_EOL.'Revision: '.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.$this->getTLTrace().PHP_EOL;
|
||||
$result = sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess(), $this->rpc, $this->file, $this->line.PHP_EOL.PHP_EOL).PHP_EOL.\danog\MadelineProto\Magic::$revision.PHP_EOL.$this->getTLTrace().PHP_EOL;
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
$result = str_replace(PHP_EOL, '<br>'.PHP_EOL, $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __construct($message = null, $code = 0, Exception $previous = null)
|
||||
|
@ -1,15 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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/>.
|
||||
*/
|
||||
/**
|
||||
* RSA module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* ResponseException module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* AuthKeyHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* MessageHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* ResponseHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* SeqNoHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* SecurityException module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Serialization module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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.
|
||||
The PWRTelegram API 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Server module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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.
|
||||
The PWRTelegram API 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Handler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Server;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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.
|
||||
The PWRTelegram API 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Proxy module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Server;
|
||||
|
||||
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
Copyright 2016-2018 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.
|
||||
The PWRTelegram API 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 MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Stream module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Server;
|
||||
|
||||
|
42
src/danog/MadelineProto/Stream/Async/Buffer.php
Normal file
42
src/danog/MadelineProto/Stream/Async/Buffer.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* Buffer helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\Async;
|
||||
|
||||
use Amp\Promise;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Buffer helper trait.
|
||||
*
|
||||
* Wraps the asynchronous generator methods with asynchronous promise-based methods
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
trait Buffer
|
||||
{
|
||||
public function bufferRead(int $length): Promise
|
||||
{
|
||||
return call([$this, 'bufferReadAsync'], $length);
|
||||
}
|
||||
|
||||
public function bufferWrite(string $data): Promise
|
||||
{
|
||||
return call([$this, 'bufferWriteAsync'], $data);
|
||||
}
|
||||
}
|
74
src/danog/MadelineProto/Stream/Async/BufferedStream.php
Normal file
74
src/danog/MadelineProto/Stream/Async/BufferedStream.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/**
|
||||
* Buffered stream helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\Async;
|
||||
|
||||
use Amp\Coroutine;
|
||||
use Amp\Failure;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Buffered stream helper trait.
|
||||
*
|
||||
* Wraps the asynchronous generator methods with asynchronous promise-based methods
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
trait BufferedStream
|
||||
{
|
||||
use Stream;
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of payload, as detected by this layer
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getReadBuffer(&$length): Promise
|
||||
{
|
||||
try {
|
||||
$result = $this->getReadBufferAsync($length);
|
||||
} catch (\Throwable $exception) {
|
||||
return new Failure($exception);
|
||||
}
|
||||
if ($result instanceof \Generator) {
|
||||
return new Coroutine($result);
|
||||
}
|
||||
if ($result instanceof Promise) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return new Success($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
* @param int $length Total length of data that is going to be piped in the buffer
|
||||
* @param string $append Data to append after entire buffer is written
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getWriteBuffer(int $length, string $append = ''): Promise
|
||||
{
|
||||
return call([$this, 'getWriteBufferAsync'], $length, $append);
|
||||
}
|
||||
}
|
49
src/danog/MadelineProto/Stream/Async/RawStream.php
Normal file
49
src/danog/MadelineProto/Stream/Async/RawStream.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
/**
|
||||
* Raw stream helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\Async;
|
||||
|
||||
use Amp\Promise;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Raw stream helper trait.
|
||||
*
|
||||
* Wraps the asynchronous generator methods with asynchronous promise-based methods
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
trait RawStream
|
||||
{
|
||||
use Stream;
|
||||
|
||||
public function read(): Promise
|
||||
{
|
||||
return call([$this, 'readAsync']);
|
||||
}
|
||||
|
||||
public function write(string $data): Promise
|
||||
{
|
||||
return call([$this, 'writeAsync'], $data);
|
||||
}
|
||||
|
||||
public function end(string $finalData = ''): Promise
|
||||
{
|
||||
return call([$this, 'endAsync'], $finalData);
|
||||
}
|
||||
}
|
38
src/danog/MadelineProto/Stream/Async/Stream.php
Normal file
38
src/danog/MadelineProto/Stream/Async/Stream.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Generic stream helper trait.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\Async;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Generic stream helper trait.
|
||||
*
|
||||
* Wraps the asynchronous generator methods with asynchronous promise-based methods
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
trait Stream
|
||||
{
|
||||
public function connect(ConnectionContext $ctx, string $header = ''): Promise
|
||||
{
|
||||
return call([$this, 'connectAsync'], $ctx, $header);
|
||||
}
|
||||
}
|
47
src/danog/MadelineProto/Stream/BufferInterface.php
Normal file
47
src/danog/MadelineProto/Stream/BufferInterface.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Buffer interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream;
|
||||
|
||||
use Amp\Promise;
|
||||
|
||||
/**
|
||||
* Buffer interface.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface BufferInterface
|
||||
{
|
||||
/**
|
||||
* Read data asynchronously.
|
||||
*
|
||||
* @param int $length How much data to read
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function bufferRead(int $length): Promise;
|
||||
|
||||
/**
|
||||
* Write data asynchronously.
|
||||
*
|
||||
* @param string $data Data to write
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function bufferWrite(string $data): Promise;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Buffered proxy stream interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream;
|
||||
|
||||
/**
|
||||
* Buffered proxy stream interface.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface BufferedProxyStreamInterface extends BufferedStreamInterface, ProxyStreamInterface
|
||||
{
|
||||
}
|
56
src/danog/MadelineProto/Stream/BufferedStreamInterface.php
Normal file
56
src/danog/MadelineProto/Stream/BufferedStreamInterface.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Buffered stream interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream;
|
||||
|
||||
use Amp\Promise;
|
||||
|
||||
/**
|
||||
* Buffered stream interface.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface BufferedStreamInterface extends StreamInterface
|
||||
{
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of payload, as detected by this layer
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getReadBuffer(&$length): Promise;
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
* @param int $length Total length of data that is going to be piped in the buffer
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getWriteBuffer(int $length, string $append = ''): Promise;
|
||||
|
||||
/**
|
||||
* Get stream name.
|
||||
*
|
||||
* Is supposed to return __CLASS__
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getName(): string;
|
||||
}
|
225
src/danog/MadelineProto/Stream/Common/BufferedRawStream.php
Normal file
225
src/danog/MadelineProto/Stream/Common/BufferedRawStream.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
/**
|
||||
* Buffered raw stream.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\Common;
|
||||
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Exception;
|
||||
use danog\MadelineProto\Stream\Async\RawStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use function Amp\call;
|
||||
use function Amp\Socket\connect;
|
||||
|
||||
/**
|
||||
* Buffered raw stream.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class BufferedRawStream implements \danog\MadelineProto\Stream\BufferedStreamInterface, \danog\MadelineProto\Stream\BufferInterface, \danog\MadelineProto\Stream\RawStreamInterface
|
||||
{
|
||||
use RawStream;
|
||||
|
||||
const MAX_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
protected $stream;
|
||||
protected $memory_stream;
|
||||
private $append = '';
|
||||
private $append_after = 0;
|
||||
|
||||
/**
|
||||
* Asynchronously connect to a TCP/TLS server.
|
||||
*
|
||||
* @param ConnectionContext $ctx Connection context
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function connectAsync(ConnectionContext $ctx, string $header = ''): \Generator
|
||||
{
|
||||
$this->stream = yield $ctx->getStream($header);
|
||||
$this->memory_stream = fopen('php://memory', 'r+');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Async chunked read.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function read(): Promise
|
||||
{
|
||||
return $this->stream->read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Async write.
|
||||
*
|
||||
* @param string $data Data to write
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function write(string $data): Promise
|
||||
{
|
||||
return $this->stream->write($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
if ($this->memory_stream) {
|
||||
fclose($this->memory_stream);
|
||||
$this->memory_stream = null;
|
||||
}
|
||||
if ($this->stream) {
|
||||
$this->stream->disconnect();
|
||||
$this->stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of payload, as detected by this layer
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getReadBuffer(&$length): Promise
|
||||
{
|
||||
$size = fstat($this->memory_stream)['size'];
|
||||
$offset = ftell($this->memory_stream);
|
||||
$length = $size - $offset;
|
||||
if ($length === 0 || $size > self::MAX_SIZE) {
|
||||
$new_memory_stream = fopen('php://memory', 'r+');
|
||||
if ($length) {
|
||||
fwrite($new_memory_stream, fread($this->memory_stream, $length));
|
||||
fseek($new_memory_stream, 0);
|
||||
}
|
||||
fclose($this->memory_stream);
|
||||
$this->memory_stream = $new_memory_stream;
|
||||
}
|
||||
|
||||
return new \Amp\Success($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
* @param int $length Total length of data that is going to be piped in the buffer
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getWriteBuffer(int $length, string $append = ''): Promise
|
||||
{
|
||||
if (strlen($append)) {
|
||||
$this->append = $append;
|
||||
$this->append_after = $length - strlen($append);
|
||||
}
|
||||
|
||||
return new \Amp\Success($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data asynchronously.
|
||||
*
|
||||
* @param int $length Amount of data to read
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function bufferRead(int $length): Promise
|
||||
{
|
||||
$size = fstat($this->memory_stream)['size'];
|
||||
$offset = ftell($this->memory_stream);
|
||||
$buffer_length = $size - $offset;
|
||||
if ($buffer_length >= $length) {
|
||||
return new Success(fread($this->memory_stream, $length));
|
||||
}
|
||||
|
||||
return call([$this, 'bufferReadAsync'], $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data asynchronously.
|
||||
*
|
||||
* @param int $length Amount of data to read
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function bufferReadAsync(int $length): \Generator
|
||||
{
|
||||
$size = fstat($this->memory_stream)['size'];
|
||||
$offset = ftell($this->memory_stream);
|
||||
$buffer_length = $size - $offset;
|
||||
if ($buffer_length < $length && $buffer_length) {
|
||||
fseek($this->memory_stream, $offset + $buffer_length);
|
||||
}
|
||||
|
||||
while ($buffer_length < $length) {
|
||||
$chunk = yield $this->read();
|
||||
if ($chunk === null) {
|
||||
$this->disconnect();
|
||||
|
||||
throw new \danog\MadelineProto\NothingInTheSocketException();
|
||||
}
|
||||
fwrite($this->memory_stream, $chunk);
|
||||
$buffer_length += strlen($chunk);
|
||||
}
|
||||
fseek($this->memory_stream, $offset);
|
||||
|
||||
return fread($this->memory_stream, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async write.
|
||||
*
|
||||
* @param string $data Data to write
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function bufferWrite(string $data): Promise
|
||||
{
|
||||
if ($this->append_after) {
|
||||
$this->append_after -= strlen($data);
|
||||
if ($this->append_after === 0) {
|
||||
$data .= $this->append;
|
||||
$this->append = '';
|
||||
} elseif ($this->append_after < 0) {
|
||||
$this->append_after = 0;
|
||||
$this->append = '';
|
||||
|
||||
throw new Exception('Tried to send too much out of frame data, cannot append');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->write($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get class name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
319
src/danog/MadelineProto/Stream/Common/HashedBufferedStream.php
Normal file
319
src/danog/MadelineProto/Stream/Common/HashedBufferedStream.php
Normal file
@ -0,0 +1,319 @@
|
||||
<?php
|
||||
/**
|
||||
* Hash stream wrapper.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\Common;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\Async\Stream;
|
||||
use danog\MadelineProto\Stream\BufferedProxyStreamInterface;
|
||||
use danog\MadelineProto\Stream\BufferInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Hash stream wrapper.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class HashedBufferedStream implements BufferedProxyStreamInterface, BufferInterface
|
||||
{
|
||||
use BufferedStream;
|
||||
private $hash_name;
|
||||
private $read_hash;
|
||||
private $write_hash;
|
||||
private $write_buffer;
|
||||
private $write_check_after = 0;
|
||||
private $write_check_pos = 0;
|
||||
private $read_buffer;
|
||||
private $read_check_after = 0;
|
||||
private $read_check_pos = 0;
|
||||
private $stream;
|
||||
private $rev = false;
|
||||
|
||||
/**
|
||||
* Enable read hashing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startReadHash()
|
||||
{
|
||||
$this->read_hash = hash_init($this->hash_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the read hash after N bytes are read.
|
||||
*
|
||||
* @param int $after The number of bytes to read before checking the hash
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkReadHash(int $after)
|
||||
{
|
||||
$this->read_check_after = $after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop read hashing and get final hash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReadHash(): string
|
||||
{
|
||||
$hash = hash_final($this->read_hash, true);
|
||||
if ($this->rev) {
|
||||
$hash = strrev($hash);
|
||||
}
|
||||
$this->read_hash = null;
|
||||
$this->read_check_after = 0;
|
||||
$this->read_check_pos = 0;
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are read hashing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasReadHash(): bool
|
||||
{
|
||||
return $this->read_hash !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable write hashing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function startWriteHash()
|
||||
{
|
||||
$this->write_hash = hash_init($this->hash_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the write hash after N bytes are read.
|
||||
*
|
||||
* @param int $after The number of bytes to read before writing the hash
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function checkWriteHash(int $after)
|
||||
{
|
||||
$this->write_check_after = $after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop write hashing and get final hash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWriteHash(): string
|
||||
{
|
||||
$hash = hash_final($this->write_hash, true);
|
||||
if ($this->rev) {
|
||||
$hash = strrev($hash);
|
||||
}
|
||||
$this->write_hash = null;
|
||||
$this->write_check_after = 0;
|
||||
$this->write_check_pos = 0;
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are write hashing.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasWriteHash(): bool
|
||||
{
|
||||
return $this->write_hash !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashes read data asynchronously.
|
||||
*
|
||||
* @param int $length Read and hash $length bytes
|
||||
*
|
||||
* @throws PendingReadError Thrown if another read operation is still pending.
|
||||
*
|
||||
* @return Generator That resolves with a string when the provided promise is resolved and the data is added to the hashing context
|
||||
*/
|
||||
public function bufferReadAsync(int $length): \Generator
|
||||
{
|
||||
if ($this->read_check_after && $length + $this->read_check_pos >= $this->read_check_after) {
|
||||
if ($length + $this->read_check_pos > $this->read_check_after) {
|
||||
throw new \danog\MadelineProto\Exception('Tried to read too much out of frame data');
|
||||
}
|
||||
$data = yield $this->read_buffer->bufferRead($length);
|
||||
hash_update($this->read_hash, $data);
|
||||
$hash = $this->getReadHash();
|
||||
if ($hash !== yield $this->read_buffer->bufferRead(strlen($hash))) {
|
||||
throw new \danog\MadelineProto\Exception('Hash mismatch');
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
$data = yield $this->read_buffer->bufferRead($length);
|
||||
hash_update($this->read_hash, $data);
|
||||
if ($this->read_check_after) {
|
||||
$this->read_check_pos += $length;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the hash algorithm.
|
||||
*
|
||||
* @param string $hash Algorithm name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setExtra($hash)
|
||||
{
|
||||
$rev = strpos($hash, '_rev');
|
||||
$this->rev = false;
|
||||
if ($rev !== false) {
|
||||
$hash = substr($hash, 0, $rev);
|
||||
$this->rev = true;
|
||||
}
|
||||
$this->hash_name = $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
* @param ConnectionContext $ctx The connection context
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function connectAsync(ConnectionContext $ctx): \Generator
|
||||
{
|
||||
$this->write_hash = null;
|
||||
$this->write_check_after = 0;
|
||||
$this->write_check_pos = 0;
|
||||
$this->read_hash = null;
|
||||
$this->read_check_after = 0;
|
||||
$this->read_check_pos = 0;
|
||||
|
||||
$this->stream = yield $ctx->getStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of payload, as detected by this layer
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function getReadBufferAsync(&$length): \Generator
|
||||
{
|
||||
if ($this->read_hash) {
|
||||
$this->read_buffer = yield $this->stream->getReadBuffer($length);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
return yield $this->stream->getReadBuffer($length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of data that is going to be written to the write buffer
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function getWriteBufferAsync(int $length, string $append = ''): \Generator
|
||||
{
|
||||
if ($this->write_hash) {
|
||||
$this->write_buffer = yield $this->stream->getWriteBuffer($length, $append);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
return yield $this->stream->getWriteBuffer($length, $append);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from the stream.
|
||||
*
|
||||
* @throws PendingReadError Thrown if another read operation is still pending.
|
||||
*
|
||||
* @return Promise Resolves with a string when new data is available or `null` if the stream has closed.
|
||||
*/
|
||||
public function bufferRead(int $length): Promise
|
||||
{
|
||||
if ($this->read_hash === null) {
|
||||
return $this->read_buffer->bufferRead($length);
|
||||
}
|
||||
|
||||
return call([$this, 'bufferReadAsync'], $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data to the stream.
|
||||
*
|
||||
* @param string $data Bytes to write.
|
||||
*
|
||||
* @throws ClosedException If the stream has already been closed.
|
||||
*
|
||||
* @return Promise Succeeds once the data has been successfully written to the stream.
|
||||
*/
|
||||
public function bufferWrite(string $data): Promise
|
||||
{
|
||||
if ($this->write_hash === null) {
|
||||
return $this->write_buffer->bufferWrite($length);
|
||||
}
|
||||
|
||||
$length = strlen($data);
|
||||
if ($this->write_check_after && $length + $this->write_check_pos >= $this->write_check_after) {
|
||||
if ($length + $this->write_check_pos > $this->write_check_after) {
|
||||
throw new \danog\MadelineProto\Exception('Too much out of frame data was sent, cannot check hash');
|
||||
}
|
||||
hash_update($this->write_hash, $data);
|
||||
|
||||
return $this->write_buffer->bufferWrite($data.$this->getWriteHash());
|
||||
}
|
||||
if ($this->write_check_after) {
|
||||
$this->write_check_pos += $length;
|
||||
}
|
||||
if ($this->write_hash) {
|
||||
hash_update($this->write_hash, $data);
|
||||
}
|
||||
|
||||
return $this->write_buffer->bufferWrite($data);
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
391
src/danog/MadelineProto/Stream/ConnectionContext.php
Normal file
391
src/danog/MadelineProto/Stream/ConnectionContext.php
Normal file
@ -0,0 +1,391 @@
|
||||
<?php
|
||||
/**
|
||||
* Connection context.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream;
|
||||
|
||||
use Amp\CancellationToken;
|
||||
use Amp\Promise;
|
||||
use Amp\Socket\ClientConnectContext;
|
||||
use Amp\Uri\Uri;
|
||||
use function Amp\call;
|
||||
|
||||
/**
|
||||
* Connection context class.
|
||||
*
|
||||
* Is responsible for maintaining state about a certain connection to a DC.
|
||||
* That includes the Stream chain that is required to use the connection, the connection URI, and other connection-related data.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class ConnectionContext
|
||||
{
|
||||
/**
|
||||
* Whether to use a secure socket.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $secure = false;
|
||||
/**
|
||||
* Whether to use test servers.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $test = false;
|
||||
/**
|
||||
* The connection URI.
|
||||
*
|
||||
* @var \Amp\Uri\Uri
|
||||
*/
|
||||
private $uri;
|
||||
/**
|
||||
* Socket context.
|
||||
*
|
||||
* @var \Amp\Socket\ClientConnectionContext
|
||||
*/
|
||||
private $socketContext;
|
||||
/**
|
||||
* Cancellation token.
|
||||
*
|
||||
* @var \Amp\CancellationToken
|
||||
*/
|
||||
private $cancellationToken;
|
||||
/**
|
||||
* The telegram DC ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $dc = 0;
|
||||
/**
|
||||
* Whether to use IPv6.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $ipv6 = false;
|
||||
/**
|
||||
* An array of arrays containing an array with the stream name and the extra parameter to pass to it.
|
||||
*
|
||||
* @var array<array<string, any>>
|
||||
*/
|
||||
private $nextStreams = [];
|
||||
/**
|
||||
* The current stream key.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $key = 0;
|
||||
|
||||
/**
|
||||
* Set the socket context.
|
||||
*
|
||||
* @param ClientConnectContext $socketContext
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setSocketContext(ClientConnectContext $socketContext): self
|
||||
{
|
||||
$this->socketContext = $socketContext;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the socket context.
|
||||
*
|
||||
* @return ClientConnectContext
|
||||
*/
|
||||
public function getSocketContext(): ClientConnectContext
|
||||
{
|
||||
return $this->socketContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection URI.
|
||||
*
|
||||
* @param string|\Amp\Uri\Uri $uri
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setUri($uri): self
|
||||
{
|
||||
$this->uri = $uri instanceof Uri ? $uri : new Uri($uri);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStringUri(): string
|
||||
{
|
||||
return (string) $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URI.
|
||||
*
|
||||
* @return \Amp\Uri\Uri
|
||||
*/
|
||||
public function getUri(): Uri
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cancellation token.
|
||||
*
|
||||
* @param CancellationToken $cancellationToken
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setCancellationToken($cancellationToken): self
|
||||
{
|
||||
$this->cancellationToken = $cancellationToken;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cancellation token.
|
||||
*
|
||||
* @return CancellationToken
|
||||
*/
|
||||
public function getCancellationToken()
|
||||
{
|
||||
return $this->cancellationToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the secure boolean.
|
||||
*
|
||||
* @param bool $secure
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setTest(bool $test): self
|
||||
{
|
||||
$this->test = $test;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use TLS with socket connections.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTest(): bool
|
||||
{
|
||||
return $this->test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the secure boolean.
|
||||
*
|
||||
* @param bool $secure
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function secure(bool $secure): self
|
||||
{
|
||||
$this->secure = $secure;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use TLS with socket connections.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSecure(): bool
|
||||
{
|
||||
return $this->secure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DC ID.
|
||||
*
|
||||
* @param string|int $dc
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setDc($dc): self
|
||||
{
|
||||
$this->dc = $dc;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the DC ID.
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function getDc()
|
||||
{
|
||||
return $this->dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int DC ID.
|
||||
*
|
||||
* @return string|int
|
||||
*/
|
||||
public function getIntDc()
|
||||
{
|
||||
$dc = intval($this->dc);
|
||||
if ($this->test) {
|
||||
$dc += 10000;
|
||||
}
|
||||
if (strpos($this->dc, '_media')) {
|
||||
$dc = -$dc;
|
||||
}
|
||||
|
||||
return $dc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use ipv6.
|
||||
*
|
||||
* @param bool $ipv6
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setIpv6(bool $ipv6): self
|
||||
{
|
||||
$this->ipv6 = $ipv6;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use ipv6.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getIpv6(): bool
|
||||
{
|
||||
return $this->ipv6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ipv6 boolean.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function getCtx(): self
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a stream to the stream chain.
|
||||
*
|
||||
* @param string $streamName
|
||||
* @param any $extra
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addStream(string $streamName, $extra = null): self
|
||||
{
|
||||
$this->nextStreams[] = [$streamName, $extra];
|
||||
$this->key = count($this->nextStreams) - 1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current stream name from the stream chain.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getStreamName(): string
|
||||
{
|
||||
return $this->nextStreams[$this->key][0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stream from the stream chain.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function getStream(string $buffer = ''): Promise
|
||||
{
|
||||
return call([$this, 'getStreamAsync'], $buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a stream from the stream chain.
|
||||
*
|
||||
* @internal Generator func
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function getStreamAsync(string $buffer = ''): \Generator
|
||||
{
|
||||
list($clazz, $extra) = $this->nextStreams[$this->key--];
|
||||
$obj = new $clazz();
|
||||
if ($obj instanceof ProxyStreamInterface) {
|
||||
$obj->setExtra($extra);
|
||||
}
|
||||
yield $obj->connect($this, $buffer);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a description "name" of the context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
$string = $this->getStringUri();
|
||||
if ($this->isSecure()) {
|
||||
$string .= ' (TLS)';
|
||||
}
|
||||
$string .= ' DC ';
|
||||
$string .= $this->getDc();
|
||||
$string .= ', via ';
|
||||
$string .= $this->getIpv6() ? 'ipv6' : 'ipv4';
|
||||
$string .= ' using ';
|
||||
foreach (array_reverse($this->nextStreams) as $k => $stream) {
|
||||
if ($k) {
|
||||
$string .= ' => ';
|
||||
}
|
||||
$string .= preg_replace('/.*\\\\/', '', $stream[0]);
|
||||
if ($stream[1]) {
|
||||
$string .= ' ('.json_encode($stream[1]).')';
|
||||
}
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a representation of the context.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
}
|
28
src/danog/MadelineProto/Stream/MTProtoBufferInterface.php
Normal file
28
src/danog/MadelineProto/Stream/MTProtoBufferInterface.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* MTProto buffer interface.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream;
|
||||
|
||||
/**
|
||||
* MTProto buffer interface, for reading transport MTProto header info.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
interface MTProtoBufferInterface
|
||||
{
|
||||
}
|
102
src/danog/MadelineProto/Stream/MTProtoTools/MsgIdHandler.php
Normal file
102
src/danog/MadelineProto/Stream/MTProtoTools/MsgIdHandler.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* MsgIdHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages message ids.
|
||||
*/
|
||||
trait MsgIdHandler
|
||||
{
|
||||
public function check_message_id($new_message_id, $aargs)
|
||||
{
|
||||
if (!is_object($new_message_id)) {
|
||||
$new_message_id = new \phpseclib\Math\BigInteger(strrev($new_message_id), 256);
|
||||
}
|
||||
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->time_delta - 300))->bitwise_leftShift(32);
|
||||
if ($min_message_id->compare($new_message_id) > 0) {
|
||||
$this->API->logger->logger('Given message id ('.$new_message_id.') is too old compared to the min value ('.$min_message_id.').', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
$max_message_id = (new \phpseclib\Math\BigInteger(time() + $this->time_delta + 30))->bitwise_leftShift(32);
|
||||
if ($max_message_id->compare($new_message_id) < 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too new compared to the max value ('.$max_message_id.'). Consider syncing your date.');
|
||||
}
|
||||
if ($aargs['outgoing']) {
|
||||
if (!$new_message_id->divide(\danog\MadelineProto\Magic::$four)[1]->equals(\danog\MadelineProto\Magic::$zero)) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4. Consider syncing your date.');
|
||||
}
|
||||
if (!\danog\MadelineProto\Magic::$has_thread && $new_message_id->compare($key = $this->get_max_id($incoming = false)) <= 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', 1);
|
||||
}
|
||||
if (count($this->outgoing_messages) > $this->API->settings['msg_array_limit']['outgoing']) {
|
||||
reset($this->outgoing_messages);
|
||||
$key = key($this->outgoing_messages);
|
||||
if (!isset($this->outgoing_messages[$key]['promise'])) {
|
||||
unset($this->outgoing_messages[$key]);
|
||||
}
|
||||
}
|
||||
$this->max_outgoing_id = $new_message_id;
|
||||
$this->outgoing_messages[strrev($new_message_id->toBytes())] = [];
|
||||
} else {
|
||||
if (!$new_message_id->divide(\danog\MadelineProto\Magic::$four)[1]->equals(\danog\MadelineProto\Magic::$one) && !$new_message_id->divide(\danog\MadelineProto\Magic::$four)[1]->equals(\danog\MadelineProto\Magic::$three)) {
|
||||
throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3');
|
||||
}
|
||||
$key = $this->get_max_id($incoming = true);
|
||||
if ($aargs['container']) {
|
||||
if ($new_message_id->compare($key = $this->get_max_id($incoming = true)) >= 0) {
|
||||
$this->API->logger->logger('WARNING: Given message id ('.$new_message_id.') is bigger than or equal to the current limit ('.$key.'). Consider syncing your date.', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
} else {
|
||||
if ($new_message_id->compare($key = $this->get_max_id($incoming = true)) <= 0) {
|
||||
$this->API->logger->logger('WARNING: Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (count($this->incoming_messages) > $this->API->settings['msg_array_limit']['incoming']) {
|
||||
reset($this->incoming_messages);
|
||||
$key = key($this->incoming_messages);
|
||||
if (!isset($this->incoming_messages[$key]['promise'])) {
|
||||
unset($this->incoming_messages[$key]);
|
||||
}
|
||||
}
|
||||
$this->max_incoming_id = $new_message_id;
|
||||
$this->incoming_messages[strrev($new_message_id->toBytes())] = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_message_id()
|
||||
{
|
||||
$message_id = (new \phpseclib\Math\BigInteger(time() + $this->time_delta))->bitwise_leftShift(32);
|
||||
if ($message_id->compare($key = $this->get_max_id($incoming = false)) <= 0) {
|
||||
$message_id = $key->add(\danog\MadelineProto\Magic::$four);
|
||||
}
|
||||
$this->check_message_id($message_id, ['outgoing' => true, 'container' => false]);
|
||||
|
||||
return strrev($message_id->toBytes());
|
||||
}
|
||||
|
||||
public function get_max_id($incoming)
|
||||
{
|
||||
$incoming = $incoming ? 'incoming' : 'outgoing';
|
||||
if (isset($this->{'max_'.$incoming.'_id'}) && is_object($this->{'max_'.$incoming.'_id'})) {
|
||||
return $this->{'max_'.$incoming.'_id'};
|
||||
}
|
||||
|
||||
return \danog\MadelineProto\Magic::$zero;
|
||||
}
|
||||
}
|
45
src/danog/MadelineProto/Stream/MTProtoTools/SaltHandler.php
Normal file
45
src/danog/MadelineProto/Stream/MTProtoTools/SaltHandler.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SaltHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages message ids.
|
||||
*/
|
||||
trait SaltHandler
|
||||
{
|
||||
public function add_salts($salts)
|
||||
{
|
||||
foreach ($salts as $salt) {
|
||||
$this->add_salt($salt['valid_since'], $salt['valid_until'], $salt['salt']);
|
||||
}
|
||||
}
|
||||
|
||||
public function add_salt($valid_since, $valid_until, $salt)
|
||||
{
|
||||
if (!isset($this->temp_auth_key['salts'][$salt])) {
|
||||
$this->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
|
||||
}
|
||||
}
|
||||
|
||||
public function handle_future_salts($salt)
|
||||
{
|
||||
$this->method_call('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
}
|
56
src/danog/MadelineProto/Stream/MTProtoTools/SeqNoHandler.php
Normal file
56
src/danog/MadelineProto/Stream/MTProtoTools/SeqNoHandler.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SeqNoHandler module.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\MTProtoTools;
|
||||
|
||||
/**
|
||||
* Manages sequence number.
|
||||
*/
|
||||
trait SeqNoHandler
|
||||
{
|
||||
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
|
||||
|
||||
public function generate_out_seq_no($content_related)
|
||||
{
|
||||
$in = $content_related ? 1 : 0;
|
||||
$value = $this->session_out_seq_no;
|
||||
$this->session_out_seq_no += $in;
|
||||
//$this->API->logger->logger("OUT: $value + $in = ".$this->session_out_seq_no);
|
||||
return $value * 2 + $in;
|
||||
}
|
||||
|
||||
public function check_in_seq_no($current_msg_id)
|
||||
{
|
||||
$type = isset($this->incoming_messages[$current_msg_id]['content']['_']) ? $this->incoming_messages[$current_msg_id]['content']['_'] : '-';
|
||||
if (isset($this->incoming_messages[$current_msg_id]['seq_no']) && ($seq_no = $this->generate_in_seq_no($this->content_related($this->incoming_messages[$current_msg_id]['content']))) !== $this->incoming_messages[$current_msg_id]['seq_no']) {
|
||||
$this->API->logger->logger('SECURITY WARNING: Seqno mismatch (should be '.$seq_no.', is '.$this->incoming_messages[$current_msg_id]['seq_no'].', '.$type.')', \danog\MadelineProto\Logger::ERROR);
|
||||
} elseif (isset($seq_no)) {
|
||||
$this->API->logger->logger('Seqno OK (should be '.$seq_no.', is '.$this->incoming_messages[$current_msg_id]['seq_no'].', '.$type.')', \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_in_seq_no($content_related)
|
||||
{
|
||||
$in = $content_related ? 1 : 0;
|
||||
$value = $this->session_in_seq_no;
|
||||
$this->session_in_seq_no += $in;
|
||||
//$this->API->logger->logger("IN: $value + $in = ".$this->session_in_seq_no);
|
||||
return $value * 2 + $in;
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Abridged stream wrapper.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\MTProtoTransport;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
|
||||
/**
|
||||
* Abridged stream wrapper.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class AbridgedStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
use BufferedStream;
|
||||
|
||||
private $stream;
|
||||
private $ctx;
|
||||
|
||||
/**
|
||||
* Connect to stream.
|
||||
*
|
||||
* @param ConnectionContext $ctx The connection context
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function connectAsync(ConnectionContext $ctx, string $header = ''): \Generator
|
||||
{
|
||||
$this->ctx = $ctx->getCtx();
|
||||
$this->stream = yield $ctx->getStream(chr(239).$header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of data that is going to be written to the write buffer
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function getWriteBufferAsync(int $length, string $append = ''): \Generator
|
||||
{
|
||||
$length >>= 2;
|
||||
if ($length < 127) {
|
||||
$message = chr($length);
|
||||
} else {
|
||||
$message = chr(127).substr(pack('V', $length), 0, 3);
|
||||
}
|
||||
$buffer = yield $this->stream->getWriteBuffer(strlen($message) + $length, $append);
|
||||
yield $buffer->bufferWrite($message);
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of payload, as detected by this layer
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function getReadBufferAsync(&$length): \Generator
|
||||
{
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$length = ord(yield $buffer->bufferRead(1));
|
||||
if ($length >= 127) {
|
||||
$length = unpack('V', (yield $buffer->bufferRead(3))."\0")[1];
|
||||
}
|
||||
$length <<= 2;
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
114
src/danog/MadelineProto/Stream/MTProtoTransport/FullStream.php
Normal file
114
src/danog/MadelineProto/Stream/MTProtoTransport/FullStream.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* TCP full stream wrapper.
|
||||
*
|
||||
* 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 MadelineProto.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
* @copyright 2016-2018 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Stream\MTProtoTransport;
|
||||
|
||||
use Amp\Promise;
|
||||
use danog\MadelineProto\Stream\Async\BufferedStream;
|
||||
use danog\MadelineProto\Stream\BufferedStreamInterface;
|
||||
use danog\MadelineProto\Stream\Common\HashedBufferedStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoBufferInterface;
|
||||
|
||||
/**
|
||||
* TCP full stream wrapper.
|
||||
*
|
||||
* Manages obfuscated2 encryption/decryption
|
||||
*
|
||||
* @author Daniil Gentili <daniil@daniil.it>
|
||||
*/
|
||||
class FullStream implements BufferedStreamInterface, MTProtoBufferInterface
|
||||
{
|
||||
use BufferedStream;
|
||||
private $stream;
|
||||
private $in_seq_no = -1;
|
||||
private $out_seq_no = -1;
|
||||
|
||||
/**
|
||||
* Stream to use as data source.
|
||||
*
|
||||
* @param ConnectionContext $ctx
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function connect(ConnectionContext $ctx, string $header = ''): Promise
|
||||
{
|
||||
$this->in_seq_no = -1;
|
||||
$this->out_seq_no = -1;
|
||||
$this->stream = new HashedBufferedStream();
|
||||
$this->stream->setExtra('crc32b_rev');
|
||||
|
||||
return $this->stream->connect($ctx, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* Async close.
|
||||
*
|
||||
* @return Promise
|
||||
*/
|
||||
public function disconnect()
|
||||
{
|
||||
return $this->stream->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get write buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of data that is going to be written to the write buffer
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function getWriteBufferAsync(int $length, string $append = ''): \Generator
|
||||
{
|
||||
$this->stream->startWriteHash();
|
||||
$this->stream->checkWriteHash($length + 8);
|
||||
$buffer = yield $this->stream->getWriteBuffer($length + 12, $append);
|
||||
$this->out_seq_no++;
|
||||
$buffer->bufferWrite(pack('VV', $length + 12, $this->out_seq_no));
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get read buffer asynchronously.
|
||||
*
|
||||
* @param int $length Length of payload, as detected by this layer
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public function getReadBufferAsync(&$length): \Generator
|
||||
{
|
||||
$this->stream->startReadHash();
|
||||
$buffer = yield $this->stream->getReadBuffer($l);
|
||||
$read_length = unpack('V', yield $buffer->bufferRead(4))[1];
|
||||
$length = $read_length - 12;
|
||||
$this->stream->checkReadHash($read_length - 8);
|
||||
$this->in_seq_no++;
|
||||
$in_seq_no = unpack('V', yield $buffer->bufferRead(4))[1];
|
||||
if ($in_seq_no != $this->in_seq_no) {
|
||||
throw new Exception('Incoming seq_no mismatch');
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return __CLASS__;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user