From 118cf244b1fdc21ba7aaa3d8d930f1a4c6adde20 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 3 Oct 2020 15:36:03 +0200 Subject: [PATCH] More type improvements --- .../MadelineProto/AbstractAPIFactory.php | 2 +- .../MadelineProto/AnnotationsBuilder.php | 1 + src/danog/MadelineProto/DoHConnector.php | 5 +- src/danog/MadelineProto/DocsBuilder.php | 12 ++- .../MadelineProto/DocsBuilder/Methods.php | 26 +++--- .../MadelineProto/Ipc/Wrapper/InputStream.php | 1 + src/danog/MadelineProto/Ipc/Wrapper/Obj.php | 2 +- .../Ipc/Wrapper/OutputStream.php | 2 + .../MadelineProto/Loop/Update/FeedLoop.php | 5 ++ .../MTProtoTools/AuthKeyHandler.php | 89 +------------------ .../MadelineProto/MTProtoTools/Crypt.php | 84 +++++++++++++++++ .../MadelineProto/MTProtoTools/FilesLogic.php | 6 +- .../MTProtoTools/PasswordCalculator.php | 8 +- .../MTProtoTools/PeerHandler.php | 16 +++- .../MTProtoTools/ReferenceDatabase.php | 1 + .../MadelineProto/MyTelegramOrgWrapper.php | 5 ++ .../SecretChats/AuthKeyHandler.php | 17 ++-- src/danog/MadelineProto/SessionPaths.php | 2 + .../MadelineProto/Stream/Async/RawStream.php | 8 +- src/danog/MadelineProto/TL/Conversion/TD.php | 2 +- src/danog/MadelineProto/Tools.php | 2 +- .../MadelineProto/VoIP/AuthKeyHandler.php | 9 +- 22 files changed, 178 insertions(+), 127 deletions(-) diff --git a/src/danog/MadelineProto/AbstractAPIFactory.php b/src/danog/MadelineProto/AbstractAPIFactory.php index 7b075375..abe4f191 100644 --- a/src/danog/MadelineProto/AbstractAPIFactory.php +++ b/src/danog/MadelineProto/AbstractAPIFactory.php @@ -51,7 +51,7 @@ abstract class AbstractAPIFactory extends AsyncConstruct /** * Method list. * - * @var string[] + * @var array */ protected array $methods = []; diff --git a/src/danog/MadelineProto/AnnotationsBuilder.php b/src/danog/MadelineProto/AnnotationsBuilder.php index 56b344d4..f03cc7cc 100644 --- a/src/danog/MadelineProto/AnnotationsBuilder.php +++ b/src/danog/MadelineProto/AnnotationsBuilder.php @@ -56,6 +56,7 @@ class AnnotationsBuilder $this->reflectionClasses = $reflectionClasses; $this->logger = $logger; $this->namespace = $namespace; + /** @psalm-suppress InvalidArgument */ $this->TL = new TL(new class($logger) { public function __construct($logger) { diff --git a/src/danog/MadelineProto/DoHConnector.php b/src/danog/MadelineProto/DoHConnector.php index 97c20d43..fdc4dd41 100644 --- a/src/danog/MadelineProto/DoHConnector.php +++ b/src/danog/MadelineProto/DoHConnector.php @@ -157,8 +157,9 @@ class DoHConnector implements Connector // This is reached if either all URIs failed or the maximum number of attempts is reached. /** @noinspection PhpUndefinedVariableInspection */ - - throw $e; + if ($e) { + throw $e; + } })()); } } diff --git a/src/danog/MadelineProto/DocsBuilder.php b/src/danog/MadelineProto/DocsBuilder.php index c49c680e..3230419a 100644 --- a/src/danog/MadelineProto/DocsBuilder.php +++ b/src/danog/MadelineProto/DocsBuilder.php @@ -39,10 +39,16 @@ class DocsBuilder use \danog\MadelineProto\DocsBuilder\Methods; use \danog\MadelineProto\DocsBuilder\Constructors; public $td = false; - public function __construct($logger, $settings) + protected array $settings; + protected string $index; + protected Logger $logger; + protected TL $TL; + protected array $tdDescriptions; + public function __construct(Logger $logger, array $settings) { $this->logger = $logger; \set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); + /** @psalm-suppress InvalidArgument */ $this->TL = new TL(new class($logger) { public function __construct($logger) { @@ -119,8 +125,8 @@ class DocsBuilder } } } - if (isset($this->td_descriptions['types'][$otype])) { - $header = "{$this->td_descriptions['types'][$otype]}\n\n$header"; + if (isset($this->tdDescriptions['types'][$otype])) { + $header = "{$this->tdDescriptions['types'][$otype]}\n\n$header"; } $header = \sprintf( $this->templates['Type'], diff --git a/src/danog/MadelineProto/DocsBuilder/Methods.php b/src/danog/MadelineProto/DocsBuilder/Methods.php index c69bd4b5..80de7fe0 100644 --- a/src/danog/MadelineProto/DocsBuilder/Methods.php +++ b/src/danog/MadelineProto/DocsBuilder/Methods.php @@ -84,22 +84,22 @@ trait Methods $param[$type_or_subtype] = '['.StrTools::markdownEscape($param[$type_or_subtype]).'](../'.$type_or_bare_type.'/'.$param[$type_or_subtype].'.md)'; $params .= "'".$param['name']."' => ".(isset($param['subtype']) ? '\\['.$param[$type_or_subtype].'\\]' : $param[$type_or_subtype]).', '; } - if (!isset($this->td_descriptions['methods'][$method])) { + if (!isset($this->tdDescriptions['methods'][$method])) { $this->addToLang('method_'.$method); if (\danog\MadelineProto\Lang::$lang['en']['method_'.$method] !== '') { - $this->td_descriptions['methods'][$method]['description'] = \danog\MadelineProto\Lang::$lang['en']['method_'.$method]; + $this->tdDescriptions['methods'][$method]['description'] = \danog\MadelineProto\Lang::$lang['en']['method_'.$method]; } } $md_method = '['.$phpMethod.']('.$method.'.md)'; $this->docs_methods[$method] = '$MadelineProto->'.$md_method.'(\\['.$params.'\\]) === [$'.StrTools::markdownEscape($type).'](../types/'.$php_type.'.md) '; - if (isset($this->td_descriptions['methods'][$method])) { - $desc = \Parsedown::instance()->line(\trim(\explode("\n", $this->td_descriptions['methods'][$method]['description'])[0], '.')); + if (isset($this->tdDescriptions['methods'][$method])) { + $desc = \Parsedown::instance()->line(\trim(\explode("\n", $this->tdDescriptions['methods'][$method]['description'])[0], '.')); $dom = new \DOMDocument(); $dom->loadHTML(\mb_convert_encoding($desc, 'HTML-ENTITIES', 'UTF-8')); $desc = $dom->textContent; - $this->human_docs_methods[$this->td_descriptions['methods'][$method]['description'].': '.$method] = '* '.$desc.': '.$method.' + $this->human_docs_methods[$this->tdDescriptions['methods'][$method]['description'].': '.$method] = '* '.$desc.': '.$method.' '; } @@ -112,7 +112,7 @@ trait Methods | Name | Type | Required | |----------|---------------|----------| '; - if (isset($this->td_descriptions['methods'][$method]) && !empty($data['params'])) { + if (isset($this->tdDescriptions['methods'][$method]) && !empty($data['params'])) { $table = '### Parameters: | Name | Type | Description | Required | @@ -164,13 +164,13 @@ trait Methods $human_ptype = 'File path or '.$ptype; } $type_or_bare_type = \ctype_upper(Tools::end(\explode('.', $param[$type_or_subtype]))[0]) || \in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53']) ? 'types' : 'constructors'; - if (!isset($this->td_descriptions['methods'][$method]['params'][$param['name']])) { - if (isset($this->td_descriptions['methods'][$method]['description'])) { - $this->td_descriptions['methods'][$method]['params'][$param['name']] = \danog\MadelineProto\Lang::$lang['en']['method_'.$method.'_param_'.$param['name'].'_type_'.$param['type']] ?? ''; + if (!isset($this->tdDescriptions['methods'][$method]['params'][$param['name']])) { + if (isset($this->tdDescriptions['methods'][$method]['description'])) { + $this->tdDescriptions['methods'][$method]['params'][$param['name']] = \danog\MadelineProto\Lang::$lang['en']['method_'.$method.'_param_'.$param['name'].'_type_'.$param['type']] ?? ''; } } - if (isset($this->td_descriptions['methods'][$method])) { - $table .= '|'.StrTools::markdownEscape($param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.StrTools::markdownEscape($human_ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.$this->td_descriptions['methods'][$method]['params'][$param['name']].' | '.(isset($param['pow']) || ($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type'] || ($id = $this->TL->getConstructors($this->td)->findByPredicate('input'.$param['type'].'Empty')) && $id['type'] === $param['type'] ? 'Optional' : 'Yes').'|'; + if (isset($this->tdDescriptions['methods'][$method])) { + $table .= '|'.StrTools::markdownEscape($param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.StrTools::markdownEscape($human_ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.$this->tdDescriptions['methods'][$method]['params'][$param['name']].' | '.(isset($param['pow']) || ($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type'] || ($id = $this->TL->getConstructors($this->td)->findByPredicate('input'.$param['type'].'Empty')) && $id['type'] === $param['type'] ? 'Optional' : 'Yes').'|'; } else { $table .= '|'.StrTools::markdownEscape($param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.StrTools::markdownEscape($human_ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || ($id = $this->TL->getConstructors($this->td)->findByPredicate(\lcfirst($param['type']).'Empty')) && $id['type'] === $param['type'] || ($id = $this->TL->getConstructors($this->td)->findByPredicate('input'.$param['type'].'Empty')) && $id['type'] === $param['type'] ? 'Optional' : 'Yes').'|'; } @@ -200,7 +200,7 @@ trait Methods $pwr_params = "parseMode - string\n"; } } - $description = isset($this->td_descriptions['methods'][$method]) ? $this->td_descriptions['methods'][$method]['description'] : $method.' parameters, return type and example'; + $description = isset($this->tdDescriptions['methods'][$method]) ? $this->tdDescriptions['methods'][$method]['description'] : $method.' parameters, return type and example'; $symFile = \str_replace('.', '_', $method); $redir = $symFile !== $method ? "\nredirect_from: /API_docs/methods/{$symFile}.html" : ''; $description = \rtrim(\explode("\n", $description)[0], ':'); @@ -208,7 +208,7 @@ trait Methods if ($this->td) { $header .= "YOU CANNOT USE THIS METHOD IN MADELINEPROTO\n\n\n\n\n"; } - $header .= isset($this->td_descriptions['methods'][$method]) ? $this->td_descriptions['methods'][$method]['description'].PHP_EOL.PHP_EOL : ''; + $header .= isset($this->tdDescriptions['methods'][$method]) ? $this->tdDescriptions['methods'][$method]['description'].PHP_EOL.PHP_EOL : ''; $table .= ' '; diff --git a/src/danog/MadelineProto/Ipc/Wrapper/InputStream.php b/src/danog/MadelineProto/Ipc/Wrapper/InputStream.php index b44f106b..29d24f7f 100644 --- a/src/danog/MadelineProto/Ipc/Wrapper/InputStream.php +++ b/src/danog/MadelineProto/Ipc/Wrapper/InputStream.php @@ -3,6 +3,7 @@ namespace danog\MadelineProto\Ipc\Wrapper; use Amp\ByteStream\InputStream as AmpInputStream; +use Amp\ByteStream\PendingReadError; use Amp\Promise; use danog\MadelineProto\Tools; diff --git a/src/danog/MadelineProto/Ipc/Wrapper/Obj.php b/src/danog/MadelineProto/Ipc/Wrapper/Obj.php index 3bf0a424..1b17d17c 100644 --- a/src/danog/MadelineProto/Ipc/Wrapper/Obj.php +++ b/src/danog/MadelineProto/Ipc/Wrapper/Obj.php @@ -36,7 +36,7 @@ class Obj * @param string $name * @param array $arguments * - * @return \Generator + * @return \Generator */ public function __call(string $name, array $arguments = []): \Generator { diff --git a/src/danog/MadelineProto/Ipc/Wrapper/OutputStream.php b/src/danog/MadelineProto/Ipc/Wrapper/OutputStream.php index 30a43808..cffce22a 100644 --- a/src/danog/MadelineProto/Ipc/Wrapper/OutputStream.php +++ b/src/danog/MadelineProto/Ipc/Wrapper/OutputStream.php @@ -2,7 +2,9 @@ namespace danog\MadelineProto\Ipc\Wrapper; +use Amp\ByteStream\ClosedException; use Amp\ByteStream\OutputStream as AmpOutputStream; +use Amp\ByteStream\StreamException; use Amp\Promise; use danog\MadelineProto\Tools; diff --git a/src/danog/MadelineProto/Loop/Update/FeedLoop.php b/src/danog/MadelineProto/Loop/Update/FeedLoop.php index a6845e66..ab336719 100644 --- a/src/danog/MadelineProto/Loop/Update/FeedLoop.php +++ b/src/danog/MadelineProto/Loop/Update/FeedLoop.php @@ -22,6 +22,7 @@ namespace danog\MadelineProto\Loop\Update; use danog\Loop\ResumableSignalLoop; use danog\MadelineProto\Loop\InternalLoop; use danog\MadelineProto\MTProto; +use danog\MadelineProto\MTProtoTools\UpdatesState; /** * Update feed loop. @@ -55,6 +56,10 @@ class FeedLoop extends ResumableSignalLoop * @var UpdateLoop */ private ?UpdateLoop $updater = null; + /** + * Update state + */ + private ?UpdatesState $state = null; /** * Constructor. * diff --git a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php index a9d3c594..bcb9d5aa 100644 --- a/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/AuthKeyHandler.php @@ -267,14 +267,14 @@ trait AuthKeyHandler $server_time = $server_DH_inner_data['server_time']; $connection->time_delta = $server_time - \time(); $this->logger->logger(\sprintf('Server-client time delta = %.1f s', $connection->time_delta), \danog\MadelineProto\Logger::VERBOSE); - $this->checkPG($dh_prime, $g); - $this->checkG($g_a, $dh_prime); + Crypt::checkPG($dh_prime, $g); + Crypt::checkG($g_a, $dh_prime); for ($retry_id = 0; $retry_id <= $this->settings->getAuth()->getMaxAuthTries(); $retry_id++) { $this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE); $b = new BigInteger(\danog\MadelineProto\Tools::random(256), 256); $this->logger->logger('Generating g_b...', \danog\MadelineProto\Logger::VERBOSE); $g_b = $g->powMod($b, $dh_prime); - $this->checkG($g_b, $dh_prime); + Crypt::checkG($g_b, $dh_prime); /* * *********************************************************************** * Check validity of g_b @@ -401,87 +401,6 @@ trait AuthKeyHandler throw new \danog\MadelineProto\SecurityException('Auth Failed'); } } - /** - * Check validity of g_a parameters. - * - * @param BigInteger $g_a - * @param BigInteger $p - * - * @internal - * - * @return bool - */ - public function checkG(BigInteger $g_a, BigInteger $p): bool - { - /* - * *********************************************************************** - * Check validity of g_a - * 1 < g_a < p - 1 - */ - $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 < 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 < g_a < p - 2^1984 is false).'); - } - return true; - } - /** - * Check validity of p and g parameters. - * - * @param BigInteger $p - * @param BigInteger $g - * - * @internal - * - * @return boolean - */ - public function checkPG(BigInteger $p, BigInteger $g): bool - { - /* - * *********************************************************************** - * Check validity of dh_prime - * Is it a prime? - */ - $this->logger->logger('Executing p/g checks (1/2)...', \danog\MadelineProto\Logger::VERBOSE); - if (!$p->isPrime()) { - throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime (p isn't a prime)."); - } - /* - * *********************************************************************** - * Check validity of p - * Is (p - 1) / 2 a prime? - * - * Almost always fails - */ - /* - $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)."); - } - */ - /* - * *********************************************************************** - * Check validity of p - * 2^2047 < p < 2^2048 - */ - $this->logger->logger('Executing p/g checks (2/2)...', \danog\MadelineProto\Logger::VERBOSE); - if ($p->compare(\danog\MadelineProto\Magic::$twoe2047) <= 0 || $p->compare(\danog\MadelineProto\Magic::$twoe2048) >= 0) { - throw new \danog\MadelineProto\SecurityException("g isn't a safe 2048-bit prime (2^2047 < p < 2^2048 is false)."); - } - /* - * *********************************************************************** - * Check validity of g - * 1 < g < p - 1 - */ - $this->logger->logger('Executing g check...', \danog\MadelineProto\Logger::VERBOSE); - if ($g->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g->compare($p->subtract(\danog\MadelineProto\Magic::$one)) >= 0) { - throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).'); - } - return true; - } /** * Get diffie-hellman configuration. * @@ -498,7 +417,7 @@ trait AuthKeyHandler } $dh_config['p'] = new BigInteger((string) $dh_config['p'], 256); $dh_config['g'] = new BigInteger($dh_config['g']); - $this->checkPG($dh_config['p'], $dh_config['g']); + Crypt::checkPG($dh_config['p'], $dh_config['g']); return $this->dh_config = $dh_config; } /** diff --git a/src/danog/MadelineProto/MTProtoTools/Crypt.php b/src/danog/MadelineProto/MTProtoTools/Crypt.php index 4192fe01..d6168c70 100644 --- a/src/danog/MadelineProto/MTProtoTools/Crypt.php +++ b/src/danog/MadelineProto/MTProtoTools/Crypt.php @@ -19,6 +19,9 @@ namespace danog\MadelineProto\MTProtoTools; +use danog\MadelineProto\Logger; +use tgseclib\Math\BigInteger; + abstract class Crypt { /** @@ -117,4 +120,85 @@ abstract class Crypt $cipher->setIV($iv); return @$cipher->decrypt($message); } + /** + * Check validity of g_a parameters. + * + * @param BigInteger $g_a + * @param BigInteger $p + * + * @internal + * + * @return bool + */ + public static function checkG(BigInteger $g_a, BigInteger $p): bool + { + /* + * *********************************************************************** + * Check validity of g_a + * 1 < g_a < p - 1 + */ + Logger::log('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 < p - 1 is false).'); + } + Logger::log('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 < g_a < p - 2^1984 is false).'); + } + return true; + } + /** + * Check validity of p and g parameters. + * + * @param BigInteger $p + * @param BigInteger $g + * + * @internal + * + * @return boolean + */ + public static function checkPG(BigInteger $p, BigInteger $g): bool + { + /* + * *********************************************************************** + * Check validity of dh_prime + * Is it a prime? + */ + Logger::log('Executing p/g checks (1/2)...', \danog\MadelineProto\Logger::VERBOSE); + if (!$p->isPrime()) { + throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime (p isn't a prime)."); + } + /* + * *********************************************************************** + * Check validity of p + * Is (p - 1) / 2 a prime? + * + * Almost always fails + */ + /* + $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)."); + } + */ + /* + * *********************************************************************** + * Check validity of p + * 2^2047 < p < 2^2048 + */ + Logger::log('Executing p/g checks (2/2)...', \danog\MadelineProto\Logger::VERBOSE); + if ($p->compare(\danog\MadelineProto\Magic::$twoe2047) <= 0 || $p->compare(\danog\MadelineProto\Magic::$twoe2048) >= 0) { + throw new \danog\MadelineProto\SecurityException("g isn't a safe 2048-bit prime (2^2047 < p < 2^2048 is false)."); + } + /* + * *********************************************************************** + * Check validity of g + * 1 < g < p - 1 + */ + Logger::log('Executing g check...', \danog\MadelineProto\Logger::VERBOSE); + if ($g->compare(\danog\MadelineProto\Magic::$one) <= 0 || $g->compare($p->subtract(\danog\MadelineProto\Magic::$one)) >= 0) { + throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).'); + } + return true; + } } diff --git a/src/danog/MadelineProto/MTProtoTools/FilesLogic.php b/src/danog/MadelineProto/MTProtoTools/FilesLogic.php index 783551e9..569e0123 100644 --- a/src/danog/MadelineProto/MTProtoTools/FilesLogic.php +++ b/src/danog/MadelineProto/MTProtoTools/FilesLogic.php @@ -19,6 +19,8 @@ use Amp\Http\Status; use Amp\Producer; use danog\MadelineProto\Exception; use danog\MadelineProto\FileCallbackInterface; +use danog\MadelineProto\Ipc\Client; +use danog\MadelineProto\Settings; use danog\MadelineProto\Stream\Common\BufferedRawStream; use danog\MadelineProto\Stream\Common\SimpleBufferedRawStream; use danog\MadelineProto\Stream\ConnectionContext; @@ -225,7 +227,9 @@ trait FilesLogic if (\is_resource($file) || (\is_object($file) && $file instanceof InputStream)) { return yield from $this->uploadFromStream($file, 0, '', $fileName, $cb, $encrypted); } - if (!$this->settings->getFiles()->getAllowAutomaticUpload()) { + /** @var Settings */ + $settings = $this instanceof Client ? yield $this->getSettings() : $this->settings; + if (!$settings->getFiles()->getAllowAutomaticUpload()) { return yield from $this->uploadFromUrl($file, 0, $fileName, $cb, $encrypted); } $file = Tools::absolute($file); diff --git a/src/danog/MadelineProto/MTProtoTools/PasswordCalculator.php b/src/danog/MadelineProto/MTProtoTools/PasswordCalculator.php index 132673ce..49f8a0bb 100644 --- a/src/danog/MadelineProto/MTProtoTools/PasswordCalculator.php +++ b/src/danog/MadelineProto/MTProtoTools/PasswordCalculator.php @@ -32,8 +32,6 @@ use tgseclib\Math\BigInteger; */ class PasswordCalculator { - use AuthKeyHandler; - /** * The algorithm to use for calculating the hash of new passwords (a PasswordKdfAlgo object). * @@ -107,7 +105,7 @@ class PasswordCalculator 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->checkPG($object['current_algo']['p'], $object['current_algo']['g']); + Crypt::checkPG($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; @@ -137,7 +135,7 @@ class PasswordCalculator 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->checkPG($object['new_algo']['p'], $object['new_algo']['g']); + Crypt::checkPG($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; @@ -211,7 +209,7 @@ class PasswordCalculator $kg_x = $k->multiply($g_x)->powMod(Magic::$one, $p); $a = new BigInteger(\danog\MadelineProto\Tools::random(2048 / 8), 256); $A = $g->powMod($a, $p); - $this->checkG($A, $p); + Crypt::checkG($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); diff --git a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php index 51856160..1b0a1768 100644 --- a/src/danog/MadelineProto/MTProtoTools/PeerHandler.php +++ b/src/danog/MadelineProto/MTProtoTools/PeerHandler.php @@ -636,7 +636,21 @@ trait PeerHandler /** * @return (((mixed|string)[]|mixed|string)[]|int|mixed|string)[] * - * @psalm-return array{InputPeer: array{_: string, user_id?: mixed, access_hash?: mixed, min?: mixed, chat_id?: mixed, channel_id?: mixed}, Peer: array{_: string, user_id?: mixed, chat_id?: mixed, channel_id?: mixed}, DialogPeer: array{_: string, peer: array{_: string, user_id?: mixed, chat_id?: mixed, channel_id?: mixed}}, NotifyPeer: array{_: string, peer: array{_: string, user_id?: mixed, chat_id?: mixed, channel_id?: mixed}}, InputDialogPeer: array{_: string, peer: array{_: string, user_id?: mixed, access_hash?: mixed, min?: mixed, chat_id?: mixed, channel_id?: mixed}}, InputNotifyPeer: array{_: string, peer: array{_: string, user_id?: mixed, access_hash?: mixed, min?: mixed, chat_id?: mixed, channel_id?: mixed}}, bot_api_id: int|mixed, type: string} + * @psalm-return array{ + * InputPeer: array{_: string, user_id?: mixed, access_hash?: mixed, min?: mixed, chat_id?: mixed, channel_id?: mixed}, + * Peer: array{_: string, user_id?: mixed, chat_id?: mixed, channel_id?: mixed}, + * DialogPeer: array{_: string, peer: array{_: string, user_id?: mixed, chat_id?: mixed, channel_id?: mixed}}, + * NotifyPeer: array{_: string, peer: array{_: string, user_id?: mixed, chat_id?: mixed, channel_id?: mixed}}, + * InputDialogPeer: array{_: string, peer: array{_: string, user_id?: mixed, access_hash?: mixed, min?: mixed, chat_id?: mixed, channel_id?: mixed}}, + * InputNotifyPeer: array{_: string, peer: array{_: string, user_id?: mixed, access_hash?: mixed, min?: mixed, chat_id?: mixed, channel_id?: mixed}}, + * bot_api_id: int|string, + * user_id?: int, + * chat_id?: int, + * channel_id?: int, + * InputUser?: {_: string, user_id?: int, access_hash?: mixed, min?: bool}, + * InputChannel?: {_: string, channel_id: int, access_hash: mixed, min: bool}, + * type: string + * } */ private function genAll($constructor, $folder_id = null): array { diff --git a/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php b/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php index 651e8a33..a4fb7b17 100644 --- a/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php +++ b/src/danog/MadelineProto/MTProtoTools/ReferenceDatabase.php @@ -504,6 +504,7 @@ class ReferenceDatabase implements TLCallback $local_id = \danog\MadelineProto\Tools::packSignedInt($location['local_id']); return $locationType.$dc_id.$volume_id.$local_id; } + throw new \danog\MadelineProto\Exception("Invalid location type specified!"); } public function __debugInfo() { diff --git a/src/danog/MadelineProto/MyTelegramOrgWrapper.php b/src/danog/MadelineProto/MyTelegramOrgWrapper.php index 5607ea39..4c7d80d8 100644 --- a/src/danog/MadelineProto/MyTelegramOrgWrapper.php +++ b/src/danog/MadelineProto/MyTelegramOrgWrapper.php @@ -86,6 +86,11 @@ class MyTelegramOrgWrapper public function __construct($settings) { $this->settings = Settings::parseFromLegacy($settings); + if (!$this->settings instanceof Settings) { + $settings = new Settings; + $settings->merge($this->settings); + $this->settings = $settings; + } $this->__wakeup(); } /** diff --git a/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php b/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php index 12c793be..50187e3b 100644 --- a/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php +++ b/src/danog/MadelineProto/SecretChats/AuthKeyHandler.php @@ -21,6 +21,7 @@ namespace danog\MadelineProto\SecretChats; use danog\MadelineProto\Loop\Update\UpdateLoop; use danog\MadelineProto\MTProto; +use danog\MadelineProto\MTProtoTools\Crypt; /** * Manages secret chats. @@ -60,7 +61,7 @@ trait AuthKeyHandler $this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE); $b = new \tgseclib\Math\BigInteger(\danog\MadelineProto\Tools::random(256), 256); $params['g_a'] = new \tgseclib\Math\BigInteger((string) $params['g_a'], 256); - $this->checkG($params['g_a'], $dh_config['p']); + Crypt::checkG($params['g_a'], $dh_config['p']); $key = ['auth_key' => \str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, \chr(0), \STR_PAD_LEFT)]; //$this->logger->logger($key); $key['fingerprint'] = \substr(\sha1($key['auth_key'], true), -8); @@ -68,7 +69,7 @@ trait AuthKeyHandler $key['visualization_46'] = \substr(\hash('sha256', $key['auth_key'], true), 20); $this->secret_chats[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputEncryptedChat' => ['_' => 'inputEncryptedChat', 'chat_id' => $params['id'], 'access_hash' => $params['access_hash']], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'in_seq_no' => 0, 'out_seq_no' => 0, 'layer' => 8, 'ttl' => 0, 'ttr' => 100, 'updated' => \time(), 'incoming' => [], 'outgoing' => [], 'created' => \time(), 'rekeying' => [0], 'key_x' => 'from server', 'mtproto' => 1]; $g_b = $dh_config['g']->powMod($b, $dh_config['p']); - $this->checkG($g_b, $dh_config['p']); + Crypt::checkG($g_b, $dh_config['p']); yield from $this->methodCallAsyncRead('messages.acceptEncryption', ['peer' => $params['id'], 'g_b' => $g_b->toBytes(), 'key_fingerprint' => $key['fingerprint']], ['datacenter' => $this->datacenter->curdc]); yield from $this->notifyLayer($params['id']); $this->logger->logger('Secret chat '.$params['id'].' accepted successfully!', \danog\MadelineProto\Logger::NOTICE); @@ -93,7 +94,7 @@ trait AuthKeyHandler $a = new \tgseclib\Math\BigInteger(\danog\MadelineProto\Tools::random(256), 256); $this->logger->logger('Generating g_a...', \danog\MadelineProto\Logger::VERBOSE); $g_a = $dh_config['g']->powMod($a, $dh_config['p']); - $this->checkG($g_a, $dh_config['p']); + Crypt::checkG($g_a, $dh_config['p']); $res = yield from $this->methodCallAsyncRead('messages.requestEncryption', ['user_id' => $user, 'g_a' => $g_a->toBytes()], ['datacenter' => $this->datacenter->curdc]); $this->temp_requested_secret_chats[$res['id']] = $a; $this->updaters[UpdateLoop::GENERIC]->resume(); @@ -116,7 +117,7 @@ trait AuthKeyHandler } $dh_config = (yield from $this->getDhConfig()); $params['g_a_or_b'] = new \tgseclib\Math\BigInteger((string) $params['g_a_or_b'], 256); - $this->checkG($params['g_a_or_b'], $dh_config['p']); + Crypt::checkG($params['g_a_or_b'], $dh_config['p']); $key = ['auth_key' => \str_pad($params['g_a_or_b']->powMod($this->temp_requested_secret_chats[$params['id']], $dh_config['p'])->toBytes(), 256, \chr(0), \STR_PAD_LEFT)]; unset($this->temp_requested_secret_chats[$params['id']]); $key['fingerprint'] = \substr(\sha1($key['auth_key'], true), -8); @@ -159,7 +160,7 @@ trait AuthKeyHandler $a = new \tgseclib\Math\BigInteger(\danog\MadelineProto\Tools::random(256), 256); $this->logger->logger('Generating g_a...', \danog\MadelineProto\Logger::VERBOSE); $g_a = $dh_config['g']->powMod($a, $dh_config['p']); - $this->checkG($g_a, $dh_config['p']); + Crypt::checkG($g_a, $dh_config['p']); $e = \danog\MadelineProto\Tools::random(8); $this->temp_rekeyed_secret_chats[$e] = $a; $this->secret_chats[$chat]['rekeying'] = [1, $e]; @@ -194,7 +195,7 @@ trait AuthKeyHandler $this->logger->logger('Generating b...', \danog\MadelineProto\Logger::VERBOSE); $b = new \tgseclib\Math\BigInteger(\danog\MadelineProto\Tools::random(256), 256); $params['g_a'] = new \tgseclib\Math\BigInteger((string) $params['g_a'], 256); - $this->checkG($params['g_a'], $dh_config['p']); + Crypt::checkG($params['g_a'], $dh_config['p']); $key = ['auth_key' => \str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, \chr(0), \STR_PAD_LEFT)]; $key['fingerprint'] = \substr(\sha1($key['auth_key'], true), -8); $key['visualization_orig'] = $this->secret_chats[$chat]['key']['visualization_orig']; @@ -202,7 +203,7 @@ trait AuthKeyHandler $this->temp_rekeyed_secret_chats[$params['exchange_id']] = $key; $this->secret_chats[$chat]['rekeying'] = [2, $params['exchange_id']]; $g_b = $dh_config['g']->powMod($b, $dh_config['p']); - $this->checkG($g_b, $dh_config['p']); + Crypt::checkG($g_b, $dh_config['p']); yield from $this->methodCallAsyncRead('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAcceptKey', 'g_b' => $g_b->toBytes(), 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]); $this->updaters[UpdateLoop::GENERIC]->resume(); } @@ -223,7 +224,7 @@ trait AuthKeyHandler $this->logger->logger('Committing rekeying of secret chat '.$chat.'...', \danog\MadelineProto\Logger::VERBOSE); $dh_config = (yield from $this->getDhConfig()); $params['g_b'] = new \tgseclib\Math\BigInteger((string) $params['g_b'], 256); - $this->checkG($params['g_b'], $dh_config['p']); + Crypt::checkG($params['g_b'], $dh_config['p']); $key = ['auth_key' => \str_pad($params['g_b']->powMod($this->temp_rekeyed_secret_chats[$params['exchange_id']], $dh_config['p'])->toBytes(), 256, \chr(0), \STR_PAD_LEFT)]; $key['fingerprint'] = \substr(\sha1($key['auth_key'], true), -8); $key['visualization_orig'] = $this->secret_chats[$chat]['key']['visualization_orig']; diff --git a/src/danog/MadelineProto/SessionPaths.php b/src/danog/MadelineProto/SessionPaths.php index af089a8f..07e158d1 100644 --- a/src/danog/MadelineProto/SessionPaths.php +++ b/src/danog/MadelineProto/SessionPaths.php @@ -191,6 +191,7 @@ class SessionPaths /** * Get IPC state. * + * @psalm-suppress InvalidReturnType * @return Promise */ public function getIpcState(): Promise @@ -222,6 +223,7 @@ class SessionPaths /** * Get light state. * + * @psalm-suppress InvalidReturnType * @return Promise */ public function getLightState(): Promise diff --git a/src/danog/MadelineProto/Stream/Async/RawStream.php b/src/danog/MadelineProto/Stream/Async/RawStream.php index 357b1d29..4b63af5a 100644 --- a/src/danog/MadelineProto/Stream/Async/RawStream.php +++ b/src/danog/MadelineProto/Stream/Async/RawStream.php @@ -40,6 +40,12 @@ trait RawStream } public function end(string $finalData = ''): Promise { - return \danog\MadelineProto\Tools::call($this->endGenerator($finalData)); + if (\method_exists($this, 'endGenerator')) { + return \danog\MadelineProto\Tools::call($this->endGenerator($finalData)); + } else { + $promise = $this->write($finalData); + $promise->onResolve(fn () => $this->disconnect()); + return $promise; + } } } diff --git a/src/danog/MadelineProto/TL/Conversion/TD.php b/src/danog/MadelineProto/TL/Conversion/TD.php index b9dcc706..2ebd0dbb 100644 --- a/src/danog/MadelineProto/TL/Conversion/TD.php +++ b/src/danog/MadelineProto/TL/Conversion/TD.php @@ -122,7 +122,7 @@ trait TD } else { switch (\end($mtproto)) { case 'choose_chat_id_from_botapi': - $newparams[$td] = (yield from $this->getInfo($params[$mtproto[0]]))['bot_api_id'] == $this->authorization['user']['id'] ? $this->getId($params['from_id']) : (yield from $this->getInfo($params[$mtproto[0]])['bot_api_id']); + $newparams[$td] = (yield from $this->getInfo($params[$mtproto[0]]))['bot_api_id'] == $this->authorization['user']['id'] ? $this->getId($params['from_id']) : (yield from $this->getInfo($params[$mtproto[0]]))['bot_api_id']; break; case 'choose_incoming_or_sent': $newparams[$td] = ['_' => $params['out'] ? 'messageIsSuccessfullySent' : 'messageIsIncoming']; diff --git a/src/danog/MadelineProto/Tools.php b/src/danog/MadelineProto/Tools.php index 053cb47c..9c331e15 100644 --- a/src/danog/MadelineProto/Tools.php +++ b/src/danog/MadelineProto/Tools.php @@ -427,7 +427,7 @@ abstract class Tools extends StrTools * @param mixed $default * * @psalm-param Promise|TGenerator $promise Promise to which the timeout is applied. - * @psalm-param TReturnAlt $timeout + * @psalm-param TReturnAlt $default * * @return Promise * diff --git a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php index 2fea7647..1bf7ffa3 100644 --- a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php +++ b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php @@ -20,6 +20,7 @@ namespace danog\MadelineProto\VoIP; use danog\MadelineProto\Loop\Update\UpdateLoop; +use danog\MadelineProto\MTProtoTools\Crypt; use danog\MadelineProto\Tools; /** @@ -104,7 +105,7 @@ trait AuthKeyHandler $a = \tgseclib\Math\BigInteger::randomRange(\danog\MadelineProto\Magic::$two, $dh_config['p']->subtract(\danog\MadelineProto\Magic::$two)); $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['generating_g_a'], \danog\MadelineProto\Logger::VERBOSE); $g_a = $dh_config['g']->powMod($a, $dh_config['p']); - $this->checkG($g_a, $dh_config['p']); + Crypt::checkG($g_a, $dh_config['p']); $controller = new \danog\MadelineProto\VoIP(true, $user['user_id'], $this, \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED); $controller->storage = ['a' => $a, 'g_a' => \str_pad($g_a->toBytes(), 256, \chr(0), \STR_PAD_LEFT)]; $res = yield from $this->methodCallAsyncRead('phone.requestCall', ['user_id' => $user, 'g_a_hash' => \hash('sha256', $g_a->toBytes(), true), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_p2p' => true, 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc]); @@ -134,7 +135,7 @@ trait AuthKeyHandler $this->logger->logger(\danog\MadelineProto\Lang::$current_lang['generating_b'], \danog\MadelineProto\Logger::VERBOSE); $b = \tgseclib\Math\BigInteger::randomRange(\danog\MadelineProto\Magic::$two, $dh_config['p']->subtract(\danog\MadelineProto\Magic::$two)); $g_b = $dh_config['g']->powMod($b, $dh_config['p']); - $this->checkG($g_b, $dh_config['p']); + Crypt::checkG($g_b, $dh_config['p']); try { $res = yield from $this->methodCallAsyncRead('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc]); } catch (\danog\MadelineProto\RPCErrorException $e) { @@ -172,7 +173,7 @@ trait AuthKeyHandler $this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['call_confirming'], $this->calls[$params['id']]->getOtherID()), \danog\MadelineProto\Logger::VERBOSE); $dh_config = (yield from $this->getDhConfig()); $params['g_b'] = new \tgseclib\Math\BigInteger((string) $params['g_b'], 256); - $this->checkG($params['g_b'], $dh_config['p']); + Crypt::checkG($params['g_b'], $dh_config['p']); $key = \str_pad($params['g_b']->powMod($this->calls[$params['id']]->storage['a'], $dh_config['p'])->toBytes(), 256, \chr(0), \STR_PAD_LEFT); try { $res = (yield from $this->methodCallAsyncRead('phone.confirmCall', ['key_fingerprint' => \substr(\sha1($key, true), -8), 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]->storage['g_a'], 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => \danog\MadelineProto\VoIP::getConnectionMaxLayer()]], ['datacenter' => $this->datacenter->curdc]))['phone_call']; @@ -224,7 +225,7 @@ trait AuthKeyHandler throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['invalid_g_a']); } $params['g_a_or_b'] = new \tgseclib\Math\BigInteger((string) $params['g_a_or_b'], 256); - $this->checkG($params['g_a_or_b'], $dh_config['p']); + Crypt::checkG($params['g_a_or_b'], $dh_config['p']); $key = \str_pad($params['g_a_or_b']->powMod($this->calls[$params['id']]->storage['b'], $dh_config['p'])->toBytes(), 256, \chr(0), \STR_PAD_LEFT); if (\substr(\sha1($key, true), -8) != $params['key_fingerprint']) { throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_invalid']);