Refactor auth key management
This commit is contained in:
parent
3107aee776
commit
ea2b28dbb1
@ -297,8 +297,8 @@ class API extends APIFactory
|
||||
if ($filename == '') {
|
||||
throw new \danog\MadelineProto\Exception('Empty filename');
|
||||
}
|
||||
if (isset($this->API->setdem) && $this->API->setdem) {
|
||||
$this->API->setdem = false;
|
||||
if (isset($this->API->flushSettings) && $this->API->flushSettings) {
|
||||
$this->API->flushSettings = false;
|
||||
$this->API->__construct($this->API->settings);
|
||||
}
|
||||
if ($this->API === null && !$this->getting_api_id) {
|
||||
|
@ -177,8 +177,8 @@ class APIFactory extends AsyncConstruct
|
||||
Logger::log("Didn't serialize in a while, doing that now...");
|
||||
$this->serialize($this->session);
|
||||
}
|
||||
if ($this->API->setdem) {
|
||||
$this->API->setdem = false;
|
||||
if ($this->API->flushSettings) {
|
||||
$this->API->flushSettings = false;
|
||||
$this->API->__construct($this->API->settings);
|
||||
yield $this->API->initAsync();
|
||||
}
|
||||
@ -207,7 +207,7 @@ class APIFactory extends AsyncConstruct
|
||||
$this->wait($this->asyncAPIPromise);
|
||||
}
|
||||
if ($name === 'settings') {
|
||||
$this->API->setdem = true;
|
||||
$this->API->flushSettings = true;
|
||||
|
||||
return $this->API->settings;
|
||||
}
|
||||
|
@ -262,13 +262,9 @@ class Connection extends Session
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect function.
|
||||
* Connects to a telegram DC using the specified protocol, proxy and connection parameters.
|
||||
*
|
||||
* Connects to a telegram DC using the specified protocol, proxy and connection parameters
|
||||
*
|
||||
* @param string $proxy Proxy class name
|
||||
*
|
||||
* @internal
|
||||
* @param ConnectionContext $ctx Connection context
|
||||
*
|
||||
* @return \Amp\Promise
|
||||
*/
|
||||
@ -388,7 +384,7 @@ class Connection extends Session
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush pending packets
|
||||
* Flush pending packets.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -399,7 +395,9 @@ class Connection extends Session
|
||||
/**
|
||||
* Connect main instance.
|
||||
*
|
||||
* @param MTProto $extra
|
||||
* @param DataCenterConnection $extra Extra
|
||||
* @param callable $readingCallback Read callback
|
||||
* @param callable $writingCallback Write callback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -423,7 +421,7 @@ class Connection extends Session
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from DC
|
||||
* Disconnect from DC.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -447,7 +445,7 @@ class Connection extends Session
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconnect to DC
|
||||
* Reconnect to DC.
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
@ -468,7 +466,7 @@ class Connection extends Session
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name
|
||||
* Get name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@ -42,6 +42,8 @@ use Amp\Socket\ConnectException;
|
||||
use Amp\Socket\Socket;
|
||||
use Amp\TimeoutException;
|
||||
use danog\MadelineProto\AuthKey\AuthKey;
|
||||
use danog\MadelineProto\AuthKey\PermAuthKey;
|
||||
use danog\MadelineProto\AuthKey\TempAuthKey;
|
||||
use danog\MadelineProto\Stream\Common\BufferedRawStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\MTProtoTransport\AbridgedStream;
|
||||
@ -128,16 +130,54 @@ class DataCenter
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
foreach ($this->sockets as &$socket) {
|
||||
$array = [];
|
||||
foreach ($this->sockets as $id => $socket) {
|
||||
if ($socket instanceof Connection) {
|
||||
$new = new DataCenterConnection;
|
||||
if ($socket->temp_auth_key) {
|
||||
$new->setAuthKey(new AuthKey($socket->temp_auth_key), true);
|
||||
$array[$id]['tempAuthKey'] = $socket->temp_auth_key;
|
||||
}
|
||||
if ($socket->auth_key) {
|
||||
$new->setAuthKey(new AuthKey($socket->auth_key), false);
|
||||
$array[$id]['authKey'] = $socket->auth_key;
|
||||
$array[$id]['authKey']['authorized'] = $socket->authorized;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->setDataCenterConnections($array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set auth key information from saved auth array
|
||||
*
|
||||
* @param array $saved Saved auth array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setDataCenterConnections(array $saved)
|
||||
{
|
||||
foreach ($saved as $id => $data) {
|
||||
$connection = $this->sockets[$id] = new DataCenterConnection;
|
||||
if (isset($data['authKey'])) {
|
||||
$connection->setPermAuthKey(new PermAuthKey($data['authKey']));
|
||||
}
|
||||
if (isset($data['linked'])) {
|
||||
continue;
|
||||
}
|
||||
if (isset($data['tempAuthKey'])) {
|
||||
$connection->setTempAuthKey(new TempAuthKey($data['tempAuthKey']));
|
||||
if ($data['tempAuthKey']['bound'] ?? false && $connection->hasPermAuthKey()) {
|
||||
$connection->bind();
|
||||
}
|
||||
}
|
||||
unset($saved[$id]);
|
||||
}
|
||||
foreach ($saved as $id => $data) {
|
||||
$connection = $this->sockets[$id];
|
||||
$connection->link($data['linked']);
|
||||
if (isset($data['tempAuthKey'])) {
|
||||
$connection->setTempAuthKey(new TempAuthKey($data['tempAuthKey']));
|
||||
if ($data['tempAuthKey']['bound'] ?? false && $connection->hasPermAuthKey()) {
|
||||
$connection->bind();
|
||||
}
|
||||
$new->authorized($socket->authorized);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -786,6 +826,17 @@ class DataCenter
|
||||
return yield (yield $this->getHTTPClient()->request($url))->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Connection instance for authorization
|
||||
*
|
||||
* @param string $dc DC ID
|
||||
*
|
||||
* @return Connection
|
||||
*/
|
||||
public function getAuthConnection(string $dc): Connection
|
||||
{
|
||||
return $this->sockets[$dc]->getAuthConnection();
|
||||
}
|
||||
/**
|
||||
* Get Connection instance.
|
||||
*
|
||||
|
@ -19,6 +19,8 @@
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
use danog\MadelineProto\AuthKey\AuthKey;
|
||||
use danog\MadelineProto\AuthKey\PermAuthKey;
|
||||
use danog\MadelineProto\AuthKey\TempAuthKey;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use JsonSerializable;
|
||||
|
||||
@ -27,23 +29,16 @@ class DataCenterConnection implements JsonSerializable
|
||||
/**
|
||||
* Temporary auth key.
|
||||
*
|
||||
* @var AuthKey
|
||||
* @var TempAuthKey|null
|
||||
*/
|
||||
private $tempAuthKey;
|
||||
/**
|
||||
* Permanent auth key.
|
||||
*
|
||||
* @var AuthKey
|
||||
* @var PermAuthKey|null
|
||||
*/
|
||||
private $authKey;
|
||||
|
||||
/**
|
||||
* Whether this auth key is authorized (as in logged in).
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $authorized = false;
|
||||
|
||||
/**
|
||||
* Connections open to a certain DC.
|
||||
*
|
||||
@ -79,11 +74,11 @@ class DataCenterConnection implements JsonSerializable
|
||||
private $datacenter;
|
||||
|
||||
/**
|
||||
* Index for round robin.
|
||||
* Linked DC ID.
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
private $index = 0;
|
||||
private $linked;
|
||||
|
||||
/**
|
||||
* Loop to keep weights at sane value.
|
||||
@ -106,19 +101,19 @@ class DataCenterConnection implements JsonSerializable
|
||||
/**
|
||||
* Check if auth key is present.
|
||||
*
|
||||
* @param boolean $temp Whether to fetch the temporary auth key
|
||||
* @param boolean|null $temp Whether to fetch the temporary auth key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAuthKey(bool $temp = true): bool
|
||||
{
|
||||
return $this->{$temp ? 'tempAuthKey' : 'authKey'} !== null;
|
||||
return $this->{$temp ? 'tempAuthKey' : 'authKey'} !== null && $this->{$temp ? 'tempAuthKey' : 'authKey'}->hasAuthKey();
|
||||
}
|
||||
/**
|
||||
* Set auth key.
|
||||
*
|
||||
* @param AuthKey|null $key The auth key
|
||||
* @param boolean $temp Whether to set the temporary auth key
|
||||
* @param boolean|null $temp Whether to set the temporary auth key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@ -127,6 +122,79 @@ class DataCenterConnection implements JsonSerializable
|
||||
$this->{$temp ? 'tempAuthKey' : 'authKey'} = $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get temporary authorization key.
|
||||
*
|
||||
* @return AuthKey
|
||||
*/
|
||||
public function getTempAuthKey(): TempAuthKey
|
||||
{
|
||||
return $this->getAuthKey(true);
|
||||
}
|
||||
/**
|
||||
* Get permanent authorization key.
|
||||
*
|
||||
* @return AuthKey
|
||||
*/
|
||||
public function getPermAuthKey(): PermAuthKey
|
||||
{
|
||||
return $this->getAuthKey(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has temporary authorization key.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasTempAuthKey(): bool
|
||||
{
|
||||
return $this->hasAuthKey(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has permanent authorization key.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasPermAuthKey(): bool
|
||||
{
|
||||
return $this->hasAuthKey(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set temporary authorization key.
|
||||
*
|
||||
* @param TempAuthKey|null $key Auth key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setTempAuthKey(?TempAuthKey $key)
|
||||
{
|
||||
return $this->setAuthKey($key, true);
|
||||
}
|
||||
/**
|
||||
* Set permanent authorization key.
|
||||
*
|
||||
* @param PermAuthKey|null $key Auth key
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setPermAuthKey(?PermAuthKey $key)
|
||||
{
|
||||
return $this->setAuthKey($key, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind temporary and permanent auth keys.
|
||||
*
|
||||
* @param bool $pfs Whether to bind using PFS
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bind(bool $pfs = true)
|
||||
{
|
||||
$this->tempAuthKey->bind($this->authKey, $pfs);
|
||||
}
|
||||
/**
|
||||
* Check if we are logged in.
|
||||
*
|
||||
@ -134,7 +202,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function isAuthorized(): bool
|
||||
{
|
||||
return $this->authorized;
|
||||
return $this->hasTempAuthKey() ? $this->getTempAuthKey()->isAuthorized() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +214,20 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function authorized(bool $authorized)
|
||||
{
|
||||
$this->authorized = $authorized;
|
||||
$this->getTempAuthKey()->authorized($authorized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link permanent authorization info of main DC to media DC.
|
||||
*
|
||||
* @param string $dc Main DC ID
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function link(string $dc)
|
||||
{
|
||||
$this->linked = $dc;
|
||||
$this->authKey = &$this->API->datacenter->getDataCenterConnection($dc)->authKey;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,6 +252,7 @@ class DataCenterConnection implements JsonSerializable
|
||||
$socket->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection context.
|
||||
*
|
||||
@ -196,7 +278,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
$this->datacenter = $ctx->getDc();
|
||||
$media = $ctx->isMedia();
|
||||
|
||||
|
||||
$count = $media ? $this->API->settings['connection_settings']['media_socket_count'] : 1;
|
||||
|
||||
if ($count > 1) {
|
||||
@ -258,6 +339,16 @@ class DataCenterConnection implements JsonSerializable
|
||||
yield $this->connect($this->ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get connection for authorization.
|
||||
*
|
||||
* @return Connection
|
||||
*/
|
||||
public function getAuthConnection(): Connection
|
||||
{
|
||||
return $this->connections[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get best socket in round robin.
|
||||
*
|
||||
@ -317,10 +408,14 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
return $this->linked ?
|
||||
[
|
||||
'linked' => $this->linked,
|
||||
'tempAuthKey' => $this->tempAuthKey
|
||||
] :
|
||||
[
|
||||
'authKey' => $this->authKey,
|
||||
'tempAuthKey' => $this->tempAuthKey,
|
||||
'authorized' => $this->authorized,
|
||||
'tempAuthKey' => $this->tempAuthKey
|
||||
];
|
||||
}
|
||||
/**
|
||||
@ -332,6 +427,6 @@ class DataCenterConnection implements JsonSerializable
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
return ['authKey', 'tempAuthKey', 'authorized'];
|
||||
return $this->linked ? ['linked', 'tempAuthKey'] : ['linked', 'authKey', 'tempAuthKey'];
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class ReadLoop extends SignalLoop
|
||||
|
||||
$connection->httpReceived();
|
||||
|
||||
Loop::defer([$API, 'handle_messages'], $datacenter);
|
||||
Loop::defer([$connection, 'handle_messages']);
|
||||
|
||||
if ($this->API->is_http($datacenter)) {
|
||||
Loop::defer([$connection->waiter, 'resume']);
|
||||
|
@ -139,7 +139,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
];
|
||||
|
||||
/**
|
||||
* Localized message info flags
|
||||
* Localized message info flags.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
@ -281,22 +281,102 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
public $channel_participants = [];
|
||||
|
||||
/**
|
||||
* When we last stored data in remote peer database (now doesn't exist anymore).
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $last_stored = 0;
|
||||
/**
|
||||
* Temporary array of data to be sent to remote peer database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $qres = [];
|
||||
/**
|
||||
* Full chat info database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $full_chats = [];
|
||||
/**
|
||||
* Latest chat message ID map for update handling.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $msg_ids = [];
|
||||
/**
|
||||
* Version integer for upgrades.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $v = 0;
|
||||
private $dialog_params = ['_' => 'MadelineProto.dialogParams', 'limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
|
||||
public $setdem = false;
|
||||
/**
|
||||
* Cached getdialogs params.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $dialog_params = ['limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
|
||||
/**
|
||||
* Whether new settings were set and should be applied.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $flushSettings = false;
|
||||
/**
|
||||
* Storage for arbitrary data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $storage = [];
|
||||
/**
|
||||
* Support user ID.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $supportUser = 0;
|
||||
/**
|
||||
* File reference database.
|
||||
*
|
||||
* @var \danog\MadelineProto\MTProtoTools\ReferenceDatabase
|
||||
*/
|
||||
public $referenceDatabase;
|
||||
public $phoneConfigWatcherId;
|
||||
/**
|
||||
* Phone config loop.
|
||||
*
|
||||
* @var \danog\MadelineProto\Loop\Update\PeriodicLoop
|
||||
*/
|
||||
public $phoneConfigLoop;
|
||||
/**
|
||||
* Call checker loop.
|
||||
*
|
||||
* @var \danog\MadelineProto\Loop\Update\PeriodicLoop
|
||||
*/
|
||||
private $callCheckerLoop;
|
||||
/**
|
||||
* Autoserialization loop.
|
||||
*
|
||||
* @var \danog\MadelineProto\Loop\Update\PeriodicLoop
|
||||
*/
|
||||
private $serializeLoop;
|
||||
/**
|
||||
* Feeder loops.
|
||||
*
|
||||
* @var array<\danog\MadelineProto\Loop\Update\FeedLoop>
|
||||
*/
|
||||
public $feeders = [];
|
||||
/**
|
||||
* Updater loops.
|
||||
*
|
||||
* @var array<\danog\MadelineProto\Loop\Update\UpdateLoop>
|
||||
*/
|
||||
public $updaters = [];
|
||||
public $destructing = false; // Avoid problems with exceptions thrown by forked strands, see tools
|
||||
/**
|
||||
* Boolean to avoid problems with exceptions thrown by forked strands, see tools.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $destructing = false;
|
||||
|
||||
/**
|
||||
* DataCenter instance.
|
||||
@ -305,11 +385,25 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*/
|
||||
public $datacenter;
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __magic_construct($settings = [])
|
||||
{
|
||||
$this->setInitPromise($this->__construct_async($settings));
|
||||
}
|
||||
|
||||
/**
|
||||
* Async constructor function.
|
||||
*
|
||||
* @param array $settings Settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct_async($settings = [])
|
||||
{
|
||||
\danog\MadelineProto\Magic::class_exists();
|
||||
@ -349,7 +443,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
yield $this->connect_to_all_dcs_async();
|
||||
$this->startLoops();
|
||||
$this->datacenter->curdc = 2;
|
||||
if ((!isset($this->authorization['user']['bot']) || !$this->authorization['user']['bot']) && $this->datacenter->getDataCenterConnection($this->datacenter->curdc)->hasAuthKey()) {
|
||||
if ((!isset($this->authorization['user']['bot']) || !$this->authorization['user']['bot']) && $this->datacenter->getDataCenterConnection($this->datacenter->curdc)->hasTempAuthKey()) {
|
||||
try {
|
||||
$nearest_dc = yield $this->method_call_async_read('help.getNearestDc', [], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['nearest_dc'], $nearest_dc['country'], $nearest_dc['nearest_dc']), Logger::NOTICE);
|
||||
@ -375,8 +469,11 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
return ['supportUser', 'referenceDatabase', 'channel_participants', 'event_handler', 'event_handler_instance', 'loop_callback', 'web_template', 'encrypted_layer', 'settings', 'config', 'authorization', 'authorized', 'rsa_keys', 'dh_config', 'chats', 'last_stored', 'qres', 'got_state', 'channels_state', 'updates', 'updates_key', 'full_chats', 'msg_ids', 'dialog_params', 'datacenter', 'v', 'constructors', 'td_constructors', 'methods', 'td_methods', 'td_descriptions', 'tl_callbacks', 'temp_requested_secret_chats', 'temp_rekeyed_secret_chats', 'secret_chats', 'hook_url', 'storage', 'authorized_dc', 'tos'];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cleanup memory and session file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cleanup()
|
||||
{
|
||||
@ -434,14 +531,14 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
}
|
||||
|
||||
foreach ($this->datacenter->getDataCenterConnections() as $dc) {
|
||||
if (!$dc->isAuthorized() || $dc->hasAuthKey() === null) {
|
||||
if (!$dc->isAuthorized() || !$dc->hasTempAuthKey()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public function trySerialize()
|
||||
public function serialize()
|
||||
{
|
||||
if ($this->wrapper instanceof API && isset($this->wrapper->session) && !\is_null($this->wrapper->session) && !$this->asyncInitPromise) {
|
||||
$this->logger->logger("Didn't serialize in a while, doing that now...");
|
||||
@ -453,12 +550,16 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if (!$this->callCheckerLoop) {
|
||||
$this->callCheckerLoop = new PeriodicLoop($this, [$this, 'checkCalls'], 'call check', 10);
|
||||
}
|
||||
$this->callCheckerLoop->start();
|
||||
|
||||
if (!$this->serializeLoop) {
|
||||
$this->serializeLoop = new PeriodicLoop($this, [$this, 'trySerialize'], 'serialize', $this->settings['serialization']['serialization_interval']);
|
||||
$this->serializeLoop = new PeriodicLoop($this, [$this, 'serialize'], 'serialize', $this->settings['serialization']['serialization_interval']);
|
||||
}
|
||||
if (!$this->phoneConfigLoop) {
|
||||
$this->phoneConfigLoop = new PeriodicLoop($this, [$this, 'get_phone_config_async'], 'phone config', 24 * 3600 * 1000);
|
||||
}
|
||||
|
||||
$this->callCheckerLoop->start();
|
||||
$this->serializeLoop->start();
|
||||
$this->phoneConfigLoop->start();
|
||||
}
|
||||
public function stopLoops()
|
||||
{
|
||||
@ -470,6 +571,10 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$this->serializeLoop->signal(true);
|
||||
$this->serializeLoop = null;
|
||||
}
|
||||
if ($this->phoneConfigLoop) {
|
||||
$this->phoneConfigLoop->signal(true);
|
||||
$this->phoneConfigLoop = null;
|
||||
}
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
@ -527,7 +632,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$this->setEventHandler($this->event_handler);
|
||||
}
|
||||
$force = false;
|
||||
$this->resetSession();
|
||||
$this->resetMTProtoSession();
|
||||
if (isset($backtrace[2]['function'], $backtrace[2]['class'], $backtrace[2]['args']) && $backtrace[2]['class'] === 'danog\\MadelineProto\\API' && $backtrace[2]['function'] === '__construct_async') {
|
||||
if (\count($backtrace[2]['args']) >= 2) {
|
||||
$this->parse_settings(\array_replace_recursive($this->settings, $backtrace[2]['args'][1]));
|
||||
@ -541,7 +646,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if (!isset($this->v) || $this->v !== self::V) {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['serialization_ofd'], Logger::WARNING);
|
||||
foreach ($this->datacenter->getDataCenterConnections() as $dc_id => $socket) {
|
||||
if ($this->authorized === self::LOGGED_IN && \strpos($dc_id, '_') === false && $socket->hasAuthKey(true) && $socket->hasAuthKey(false)) {
|
||||
if ($this->authorized === self::LOGGED_IN && \strpos($dc_id, '_') === false && $socket->hasPermAuthKey() && $socket->hasTempAuthKey()) {
|
||||
$socket->authorized(true);
|
||||
}
|
||||
}
|
||||
@ -601,7 +706,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
if ($settings['app_info']['api_id'] === 6) {
|
||||
unset($settings['app_info']);
|
||||
}
|
||||
$this->resetSession(true, true);
|
||||
$this->resetMTProtoSession(true, true);
|
||||
$this->config = ['expires' => -1];
|
||||
$this->dh_config = ['version' => 0];
|
||||
yield $this->__construct_async($settings);
|
||||
@ -655,9 +760,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
public function __destruct()
|
||||
{
|
||||
$this->stopLoops();
|
||||
if ($this->phoneConfigWatcherId) {
|
||||
Loop::cancel($this->phoneConfigWatcherId);
|
||||
}
|
||||
if (isset($this->seqUpdater)) {
|
||||
$this->seqUpdater->signal(true);
|
||||
}
|
||||
@ -680,12 +782,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
$this->logger("Successfully destroyed MadelineProto");
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
if ($this->wrapper instanceof \danog\MadelineProto\API && isset($this->wrapper->session) && !\is_null($this->wrapper->session)) {
|
||||
$this->wrapper->serialize($this->wrapper->session);
|
||||
}
|
||||
}
|
||||
public static function getSettings($settings, $previousSettings = [])
|
||||
{
|
||||
Magic::class_exists();
|
||||
@ -1036,7 +1132,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetSession(bool $de = true, bool $auth_key = false)
|
||||
public function resetMTProtoSession(bool $de = true, bool $auth_key = false)
|
||||
{
|
||||
if (!\is_object($this->datacenter)) {
|
||||
throw new Exception(\danog\MadelineProto\Lang::$current_lang['session_corrupted']);
|
||||
@ -1086,9 +1182,6 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
}
|
||||
yield $this->all($dcs);
|
||||
yield $this->init_authorization_async();
|
||||
if (!$this->phoneConfigWatcherId) {
|
||||
$this->phoneConfigWatcherId = Loop::repeat(24 * 3600 * 1000, [$this, 'get_phone_config_async']);
|
||||
}
|
||||
|
||||
yield $this->get_phone_config_async();
|
||||
}
|
||||
@ -1200,7 +1293,7 @@ class MTProto extends AsyncConstruct implements TLCallback
|
||||
|
||||
public function get_phone_config_async($watcherId = null)
|
||||
{
|
||||
if ($this->authorized === self::LOGGED_IN && \class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal') && !$this->authorization['user']['bot'] && $this->datacenter->getDataCenterConnection($this->settings['connection_settings']['default_dc'])->hasAuthKey()) {
|
||||
if ($this->authorized === self::LOGGED_IN && \class_exists('\\danog\\MadelineProto\\VoIPServerConfigInternal') && !$this->authorization['user']['bot'] && $this->datacenter->getDataCenterConnection($this->settings['connection_settings']['default_dc'])->hasTempAuthKey()) {
|
||||
$this->logger->logger('Fetching phone config...');
|
||||
VoIPServerConfig::updateDefault(yield $this->method_call_async_read('phone.getCallConfig', [], ['datacenter' => $this->settings['connection_settings']['default_dc']]));
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* MTProto Auth key
|
||||
* MTProto Auth key.
|
||||
*
|
||||
* 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.
|
||||
@ -21,68 +21,50 @@ namespace danog\MadelineProto\AuthKey;
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* MTProto auth key
|
||||
* MTProto auth key.
|
||||
*/
|
||||
class AuthKey implements JsonSerializable
|
||||
abstract class AuthKey extends JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Auth key
|
||||
* Auth key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $authKey;
|
||||
protected $authKey;
|
||||
/**
|
||||
* Auth key ID
|
||||
* Auth key ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
protected $id;
|
||||
/**
|
||||
* Server salt
|
||||
* Server salt.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $serverSalt;
|
||||
/**
|
||||
* Whether the auth key is bound
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $bound = false;
|
||||
/**
|
||||
* Whether the connection is inited for this auth key
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $inited = false;
|
||||
protected $serverSalt;
|
||||
|
||||
/**
|
||||
* Constructor function
|
||||
* Constructor function.
|
||||
*
|
||||
* @param array $old Old auth key array
|
||||
*/
|
||||
public function __construct(array $old = [])
|
||||
{
|
||||
if (isset($old['auth_key'])) {
|
||||
if (strlen($old['auth_key']) !== 2048/8 && strpos($old['authkey'], 'pony') === 0) {
|
||||
$old['auth_key'] = base64_decode(substr($old['auth_key'], 4));
|
||||
if (\strlen($old['auth_key']) !== 2048/8 && \strpos($old['authkey'], 'pony') === 0) {
|
||||
$old['auth_key'] = \base64_decode(\substr($old['auth_key'], 4));
|
||||
}
|
||||
$this->setAuthKey($old['auth_key']);
|
||||
}
|
||||
if (isset($old['server_salt'])) {
|
||||
$this->setServerSalt($old['server_salt']);
|
||||
}
|
||||
if (isset($old['bound'])) {
|
||||
$this->bind($old['bound']);
|
||||
}
|
||||
if (isset($old['connection_inited'])) {
|
||||
$this->init($old['connection_inited']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set auth key
|
||||
* Set auth key.
|
||||
*
|
||||
* @param string $authKey Authorization key
|
||||
*
|
||||
@ -91,11 +73,11 @@ class AuthKey implements JsonSerializable
|
||||
public function setAuthKey(string $authKey)
|
||||
{
|
||||
$this->authKey = $authKey;
|
||||
$this->id = substr(sha1($authKey, true), -8);
|
||||
$this->id = \substr(\sha1($authKey, true), -8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key is present
|
||||
* Check if auth key is present.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
@ -105,7 +87,7 @@ class AuthKey implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get auth key
|
||||
* Get auth key.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -115,7 +97,7 @@ class AuthKey implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get auth key ID
|
||||
* Get auth key ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -125,7 +107,7 @@ class AuthKey implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Set server salt
|
||||
* Set server salt.
|
||||
*
|
||||
* @param string $salt Server salt
|
||||
*
|
||||
@ -137,7 +119,7 @@ class AuthKey implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server salt
|
||||
* Get server salt.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -147,7 +129,7 @@ class AuthKey implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if has server salt
|
||||
* Check if has server salt.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
@ -157,61 +139,18 @@ class AuthKey implements JsonSerializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind auth key
|
||||
*
|
||||
* @param boolean $bound Bind or unbind
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bind(bool $bound = true)
|
||||
{
|
||||
$this->bound = $bound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key is bound
|
||||
* Check if we are logged in.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBound(): bool
|
||||
{
|
||||
return $this->bound;
|
||||
}
|
||||
abstract public function isAuthorized(): bool;
|
||||
|
||||
/**
|
||||
* Init or deinit connection for auth key
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
* @param boolean $init Init or deinit
|
||||
* @param boolean $authorized Whether we are authorized
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(bool $init = true)
|
||||
{
|
||||
$this->inited = $init;
|
||||
}
|
||||
/**
|
||||
* Check if connection is inited for auth key
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInited(): bool
|
||||
{
|
||||
return $this->inited;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JSON serialization function
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'auth_key' => 'pony'.base64_encode($this->authKey),
|
||||
'server_salt' => $this->serverSalt,
|
||||
'bound' => $this->bound,
|
||||
'connection_inited' => $this->inited
|
||||
];
|
||||
}
|
||||
abstract public function authorized(bool $authorized);
|
||||
}
|
81
src/danog/MadelineProto/MTProto/PermAuthKey.php
Normal file
81
src/danog/MadelineProto/MTProto/PermAuthKey.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* MTProto permanent auth key.
|
||||
*
|
||||
* 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-2019 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\AuthKey;
|
||||
|
||||
/**
|
||||
* MTProto permanent auth key.
|
||||
*/
|
||||
class PermAuthKey extends AuthKey
|
||||
{
|
||||
/**
|
||||
* Whether this auth key is authorized (as in associated to an account on Telegram).
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $authorized = false;
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
* @param array $old Old auth key array
|
||||
*/
|
||||
public function __construct(array $old = [])
|
||||
{
|
||||
parent::__construct($old);
|
||||
if (isset($old['authorized'])) {
|
||||
$this->authorized($old['authorized']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Check if we are logged in.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAuthorized(): bool
|
||||
{
|
||||
return $this->authorized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
* @param boolean $authorized Whether we are authorized
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function authorized(bool $authorized)
|
||||
{
|
||||
$this->authorized = $authorized;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JSON serialization function.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'auth_key' => 'pony'.\base64_encode($this->authKey),
|
||||
'server_salt' => $this->serverSalt,
|
||||
'authorized' => $this->authorized
|
||||
];
|
||||
}
|
||||
}
|
173
src/danog/MadelineProto/MTProto/TempAuthKey.php
Normal file
173
src/danog/MadelineProto/MTProto/TempAuthKey.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
/**
|
||||
* MTProto temporary auth key.
|
||||
*
|
||||
* 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-2019 Daniil Gentili <daniil@daniil.it>
|
||||
* @license https://opensource.org/licenses/AGPL-3.0 AGPLv3
|
||||
*
|
||||
* @link https://docs.madelineproto.xyz MadelineProto documentation
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\AuthKey;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
/**
|
||||
* MTProto temporary auth key.
|
||||
*/
|
||||
class TempAuthKey extends AuthKey implements JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Bound auth key instance.
|
||||
*
|
||||
* @var PermAuthKey|null
|
||||
*/
|
||||
private $bound;
|
||||
|
||||
/**
|
||||
* Expiration date.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $expires = 0;
|
||||
|
||||
/**
|
||||
* Whether the connection is inited for this auth key.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $inited = false;
|
||||
|
||||
/**
|
||||
* Constructor function.
|
||||
*
|
||||
* @param array $old Old auth key array
|
||||
*/
|
||||
public function __construct(array $old = [])
|
||||
{
|
||||
parent::__construct($old);
|
||||
if (isset($old['expires'])) {
|
||||
$this->expires($old['expires']);
|
||||
}
|
||||
if (isset($old['connection_inited'])) {
|
||||
$this->init($old['connection_inited']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init or deinit connection for auth key.
|
||||
*
|
||||
* @param boolean $init Init or deinit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init(bool $init = true)
|
||||
{
|
||||
$this->inited = $init;
|
||||
}
|
||||
/**
|
||||
* Check if connection is inited for auth key.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isInited(): bool
|
||||
{
|
||||
return $this->inited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind auth key.
|
||||
*
|
||||
* @param PermAuthKey|null $bound Permanent auth key
|
||||
* @param bool $pfs Whether to bind using PFS
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bind(?PermAuthKey $bound, bool $pfs = true)
|
||||
{
|
||||
$this->bound = $bound;
|
||||
if (!$pfs) {
|
||||
foreach (['authKey', 'id', 'inited', 'serverSalt'] as $key) {
|
||||
$this->{$key} = &$bound->{$key};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key is bound.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isBound(): bool
|
||||
{
|
||||
return $this->bound !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are logged in.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isAuthorized(): bool
|
||||
{
|
||||
return $this->bound ? $this->bound->isAuthorized() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authorized boolean.
|
||||
*
|
||||
* @param boolean $authorized Whether we are authorized
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function authorized(bool $authorized)
|
||||
{
|
||||
$this->bound->authorized($authorized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set expiration date of temporary auth key.
|
||||
*
|
||||
* @param integer $expires Expiration date
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function expires(int $expires)
|
||||
{
|
||||
$this->expires = $expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if auth key has expired.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function expired(): bool
|
||||
{
|
||||
return \time() > $this->expires;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON serialization function.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'auth_key' => 'pony'.\base64_encode($this->authKey),
|
||||
'server_salt' => $this->serverSalt,
|
||||
'bound' => $this->isBound(),
|
||||
'expires' => $this->expires,
|
||||
'connection_inited' => $this->inited
|
||||
];
|
||||
}
|
||||
}
|
@ -173,7 +173,7 @@ trait CallHandler
|
||||
|
||||
return yield all($promises);
|
||||
}
|
||||
$args = yield $this->botAPI_to_MTProto_async($args);
|
||||
$args = yield $this->API->botAPI_to_MTProto_async($args);
|
||||
if (isset($args['ping_id']) && \is_int($args['ping_id'])) {
|
||||
$args['ping_id'] = $this->pack_signed_long($args['ping_id']);
|
||||
}
|
||||
@ -188,7 +188,7 @@ trait CallHandler
|
||||
'content_related' => $this->content_related($method),
|
||||
'promise' => $deferred,
|
||||
'method' => true,
|
||||
'unencrypted' => $this->shared->hasAuthKey() && \strpos($method, '.') === false
|
||||
'unencrypted' => !$this->shared->hasTempAuthKey() && \strpos($method, '.') === false
|
||||
]
|
||||
);
|
||||
|
||||
@ -220,7 +220,7 @@ trait CallHandler
|
||||
*/
|
||||
public function object_call_async(string $object, $args = [], array $aargs = ['msg_id' => null]): Promise
|
||||
{
|
||||
$message = ['_' => $object, 'body' => $args, 'content_related' => $this->content_related($object), 'unencrypted' => $this->shared->hasAuthKey(), 'method' => false];
|
||||
$message = ['_' => $object, 'body' => $args, 'content_related' => $this->content_related($object), 'unencrypted' => !$this->shared->hasTempAuthKey(), 'method' => false];
|
||||
if (isset($aargs['promise'])) {
|
||||
$message['promise'] = $aargs['promise'];
|
||||
}
|
||||
|
@ -114,11 +114,11 @@ trait ResponseHandler
|
||||
$this->check_in_seq_no($current_msg_id);
|
||||
$only_updates = false;
|
||||
|
||||
$this->temp_auth_key['server_salt'] = $this->incoming_messages[$current_msg_id]['content']['server_salt'];
|
||||
$this->shared->getTempAuthKey()->setServerSalt($this->incoming_messages[$current_msg_id]['content']['server_salt']);
|
||||
$this->ack_incoming_message_id($current_msg_id);
|
||||
|
||||
// Acknowledge that I received the server's response
|
||||
if ($this->authorized === self::LOGGED_IN && !$this->initing_authorization && $this->API->datacenter->getDataCenterConnection($this->API->datacenter->curdc)->hasAuthKey() && isset($this->updaters[false])) {
|
||||
if ($this->authorized === self::LOGGED_IN && !$this->initing_authorization && $this->API->datacenter->getDataCenterConnection($this->API->datacenter->curdc)->hasTempAuthKey() && isset($this->updaters[false])) {
|
||||
$this->updaters[false]->resumeDefer();
|
||||
}
|
||||
|
||||
@ -344,8 +344,8 @@ trait ResponseHandler
|
||||
if (isset($response['_'])) {
|
||||
switch ($response['_']) {
|
||||
case 'rpc_error':
|
||||
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->temp_auth_key !== null && (!isset($this->temp_auth_key['connection_inited']) || $this->temp_auth_key['connection_inited'] === false)) {
|
||||
$this->temp_auth_key['connection_inited'] = true;
|
||||
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->shared->hasTempAuthKey() && !$this->shared->getTempAuthKey()->isInited()) {
|
||||
$this->shared->getTempAuthKey()->init(true);
|
||||
}
|
||||
|
||||
if (\in_array($response['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
|
||||
@ -447,9 +447,8 @@ trait ResponseHandler
|
||||
return;
|
||||
}
|
||||
$this->session_id = null;
|
||||
$this->temp_auth_key = null;
|
||||
$this->auth_key = null;
|
||||
$this->authorized = false;
|
||||
$this->shared->setTempAuthKey(null);
|
||||
$this->shared->setPermAuthKey(null);
|
||||
|
||||
$this->logger->logger('Auth key not registered, resetting temporary and permanent auth keys...', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
@ -491,7 +490,7 @@ trait ResponseHandler
|
||||
case 'AUTH_KEY_PERM_EMPTY':
|
||||
$this->logger->logger('Temporary auth key not bound, resetting temporary auth key...', \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$this->temp_auth_key = null;
|
||||
$this->shared->setTempAuthKey(null);
|
||||
$this->callFork((function () use ($request_id) {
|
||||
yield $this->API->init_authorization_async();
|
||||
$this->method_recall('', ['message_id' => $request_id, ]);
|
||||
@ -535,7 +534,7 @@ trait ResponseHandler
|
||||
$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->temp_auth_key['server_salt'] = $response['new_server_salt'];
|
||||
$this->getTempAuthKey()->setServerSalt($response['new_server_salt']);
|
||||
$this->method_recall('', ['message_id' => $request_id, 'postpone' => true]);
|
||||
|
||||
return;
|
||||
@ -543,8 +542,8 @@ trait ResponseHandler
|
||||
case 17:
|
||||
$this->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->time_delta, \danog\MadelineProto\Logger::WARNING);
|
||||
$this->API->resetSession();
|
||||
$this->temp_auth_key = null;
|
||||
$this->API->resetMTProtoSession();
|
||||
$this->shared->setTempAuthKey(null);
|
||||
$this->callFork((function () use ($request_id) {
|
||||
yield $this->API->init_authorization_async();
|
||||
$this->method_recall('', ['message_id' => $request_id, ]);
|
||||
@ -559,8 +558,8 @@ trait ResponseHandler
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->temp_auth_key !== null && (!isset($this->temp_auth_key['connection_inited']) || $this->temp_auth_key['connection_inited'] === false)) {
|
||||
$this->temp_auth_key['connection_inited'] = true;
|
||||
if (isset($request['method']) && $request['method'] && $request['_'] !== 'auth.bindTempAuthKey' && $this->shared->hasTempAuthKey() && !$this->shared->getTempAuthKey()->isInited()) {
|
||||
$this->shared->getTempAuthKey()->init(true);
|
||||
}
|
||||
|
||||
if (!isset($request['promise'])) {
|
||||
|
@ -26,7 +26,6 @@ abstract class Session
|
||||
use AckHandler;
|
||||
use MsgIdHandler;
|
||||
use ResponseHandler;
|
||||
use SaltHandler;
|
||||
use SeqNoHandler;
|
||||
use CallHandler;
|
||||
|
||||
@ -44,7 +43,7 @@ abstract class Session
|
||||
public $ack_queue = [];
|
||||
|
||||
/**
|
||||
* Reset MTProto session
|
||||
* Reset MTProto session.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
@ -19,8 +19,12 @@
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use danog\MadelineProto\Exception;
|
||||
use Amp\Artax\Request;
|
||||
use danog\MadelineProto\AuthKey\AuthKey;
|
||||
use danog\MadelineProto\AuthKey\PermAuthKey;
|
||||
use danog\MadelineProto\AuthKey\TempAuthKey;
|
||||
use danog\MadelineProto\DataCenterConnection;
|
||||
use phpseclib\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Manages the creation of the authorization key.
|
||||
@ -30,13 +34,40 @@ use Amp\Artax\Request;
|
||||
*/
|
||||
trait AuthKeyHandler
|
||||
{
|
||||
/**
|
||||
* DCs currently initing authorization.
|
||||
*
|
||||
* @var array<bool>
|
||||
*/
|
||||
private $init_auth_dcs = [];
|
||||
/**
|
||||
* Currently initing authorization?
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $pending_auth = false;
|
||||
|
||||
public function create_auth_key_async($expires_in, $datacenter): \Generator
|
||||
/**
|
||||
* DataCenter instance.
|
||||
*
|
||||
* @var \danog\MadelineProto\DataCenter
|
||||
*/
|
||||
public $datacenter;
|
||||
|
||||
/**
|
||||
* Create authorization key.
|
||||
*
|
||||
* @param int $expires_in Expiry date of auth key, -1 for permanent auth key
|
||||
* @param string $datacenter DC ID
|
||||
*
|
||||
* @return \Generator<AuthKey>
|
||||
*/
|
||||
public function create_auth_key_async(int $expires_in, string $datacenter): \Generator
|
||||
{
|
||||
$cdn = strpos($datacenter, 'cdn');
|
||||
$cdn = \strpos($datacenter, 'cdn');
|
||||
$req_pq = $cdn ? 'req_pq' : 'req_pq_multi';
|
||||
$connection = $this->datacenter->getAuthConnection($datacenter);
|
||||
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['req_pq'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -58,7 +89,7 @@ trait AuthKeyHandler
|
||||
* ]
|
||||
*/
|
||||
$nonce = $this->random(16);
|
||||
$ResPQ = yield $this->method_call_async_read($req_pq, ['nonce' => $nonce], ['datacenter' => $datacenter]);
|
||||
$ResPQ = yield $connection->method_call_async_read($req_pq, ['nonce' => $nonce]);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
@ -70,8 +101,8 @@ trait AuthKeyHandler
|
||||
* ***********************************************************************
|
||||
* Find our key in the server_public_key_fingerprints vector
|
||||
*/
|
||||
foreach ($cdn ? array_merge($this->cdn_rsa_keys, $this->rsa_keys) : $this->rsa_keys as $curkey) {
|
||||
if (in_array($curkey->fp, $ResPQ['server_public_key_fingerprints'])) {
|
||||
foreach ($cdn ? \array_merge($this->cdn_rsa_keys, $this->rsa_keys) : $this->rsa_keys as $curkey) {
|
||||
if (\in_array($curkey->fp, $ResPQ['server_public_key_fingerprints'])) {
|
||||
$key = $curkey;
|
||||
}
|
||||
}
|
||||
@ -161,14 +192,14 @@ 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, 'dc' => preg_replace('|_.*|', '', $datacenter)];
|
||||
$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 = yield $this->serialize_object_async(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Encrypt serialized object
|
||||
*/
|
||||
$sha_digest = sha1($p_q_inner_data, true);
|
||||
$random_bytes = $this->random(255 - strlen($p_q_inner_data) - strlen($sha_digest));
|
||||
$sha_digest = \sha1($p_q_inner_data, true);
|
||||
$random_bytes = $this->random(255 - \strlen($p_q_inner_data) - \strlen($sha_digest));
|
||||
$to_encrypt = $sha_digest.$p_q_inner_data.$random_bytes;
|
||||
$encrypted_data = $key->encrypt($to_encrypt);
|
||||
$this->logger->logger('Starting Diffie Hellman key exchange', \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -192,7 +223,7 @@ trait AuthKeyHandler
|
||||
* ]
|
||||
*/
|
||||
//
|
||||
$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]);
|
||||
$server_dh_params = yield $connection->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]);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
@ -212,7 +243,7 @@ trait AuthKeyHandler
|
||||
* Check valid new nonce hash if return from server
|
||||
* new nonce hash return in server_DH_params_fail
|
||||
*/
|
||||
if (isset($server_dh_params['new_nonce_hash']) && substr(sha1($new_nonce), -32) != $server_dh_params['new_nonce_hash']) {
|
||||
if (isset($server_dh_params['new_nonce_hash']) && \substr(\sha1($new_nonce), -32) != $server_dh_params['new_nonce_hash']) {
|
||||
throw new \danog\MadelineProto\SecurityException('wrong new nonce hash.');
|
||||
}
|
||||
/*
|
||||
@ -220,15 +251,15 @@ trait AuthKeyHandler
|
||||
* Get key, iv and decrypt answer
|
||||
*/
|
||||
$encrypted_answer = $server_dh_params['encrypted_answer'];
|
||||
$tmp_aes_key = sha1($new_nonce.$server_nonce, true).substr(sha1($server_nonce.$new_nonce, true), 0, 12);
|
||||
$tmp_aes_iv = substr(sha1($server_nonce.$new_nonce, true), 12, 8).sha1($new_nonce.$new_nonce, true).substr($new_nonce, 0, 4);
|
||||
$tmp_aes_key = \sha1($new_nonce.$server_nonce, true).\substr(\sha1($server_nonce.$new_nonce, true), 0, 12);
|
||||
$tmp_aes_iv = \substr(\sha1($server_nonce.$new_nonce, true), 12, 8).\sha1($new_nonce.$new_nonce, true).\substr($new_nonce, 0, 4);
|
||||
$answer_with_hash = $this->ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Separate answer and hash
|
||||
*/
|
||||
$answer_hash = substr($answer_with_hash, 0, 20);
|
||||
$answer = substr($answer_with_hash, 20);
|
||||
$answer_hash = \substr($answer_with_hash, 0, 20);
|
||||
$answer = \substr($answer_with_hash, 20);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Deserialize answer
|
||||
@ -247,7 +278,7 @@ trait AuthKeyHandler
|
||||
* Do some checks
|
||||
*/
|
||||
$server_DH_inner_data_length = $this->get_length($answer);
|
||||
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
|
||||
if (\sha1(\substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
|
||||
throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.');
|
||||
}
|
||||
if ($nonce != $server_DH_inner_data['nonce']) {
|
||||
@ -264,8 +295,8 @@ trait AuthKeyHandler
|
||||
* Time delta
|
||||
*/
|
||||
$server_time = $server_DH_inner_data['server_time'];
|
||||
$this->datacenter->sockets[$datacenter]->time_delta = $server_time - time();
|
||||
$this->logger->logger(sprintf('Server-client time delta = %.1f s', $this->datacenter->sockets[$datacenter]->time_delta), \danog\MadelineProto\Logger::VERBOSE);
|
||||
$connection->time_delta = $server_time - \time();
|
||||
$this->logger->logger(\sprintf('Server-client time delta = %.1f s', $connection->time_delta), \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->check_p_g($dh_prime, $g);
|
||||
$this->check_G($g_a, $dh_prime);
|
||||
for ($retry_id = 0; $retry_id <= $this->settings['max_tries']['authorization']; $retry_id++) {
|
||||
@ -301,8 +332,8 @@ trait AuthKeyHandler
|
||||
* ***********************************************************************
|
||||
* encrypt client_DH_inner_data
|
||||
*/
|
||||
$data_with_sha = sha1($data, true).$data;
|
||||
$data_with_sha_padded = $data_with_sha.$this->random($this->posmod(-strlen($data_with_sha), 16));
|
||||
$data_with_sha = \sha1($data, true).$data;
|
||||
$data_with_sha_padded = $data_with_sha.$this->random($this->posmod(-\strlen($data_with_sha), 16));
|
||||
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
|
||||
$this->logger->logger('Executing set_client_DH_params...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
/*
|
||||
@ -322,7 +353,7 @@ trait AuthKeyHandler
|
||||
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_fail
|
||||
* ]
|
||||
*/
|
||||
$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]);
|
||||
$Set_client_DH_params_answer = yield $connection->method_call_async_read('set_client_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'encrypted_data' => $encrypted_data]);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Generate auth_key
|
||||
@ -330,11 +361,11 @@ trait AuthKeyHandler
|
||||
$this->logger->logger('Generating authorization key...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$auth_key = $g_a->powMod($b, $dh_prime);
|
||||
$auth_key_str = $auth_key->toBytes();
|
||||
$auth_key_sha = sha1($auth_key_str, true);
|
||||
$auth_key_aux_hash = substr($auth_key_sha, 0, 8);
|
||||
$new_nonce_hash1 = substr(sha1($new_nonce.chr(1).$auth_key_aux_hash, true), -16);
|
||||
$new_nonce_hash2 = substr(sha1($new_nonce.chr(2).$auth_key_aux_hash, true), -16);
|
||||
$new_nonce_hash3 = substr(sha1($new_nonce.chr(3).$auth_key_aux_hash, true), -16);
|
||||
$auth_key_sha = \sha1($auth_key_str, true);
|
||||
$auth_key_aux_hash = \substr($auth_key_sha, 0, 8);
|
||||
$new_nonce_hash1 = \substr(\sha1($new_nonce.\chr(1).$auth_key_aux_hash, true), -16);
|
||||
$new_nonce_hash2 = \substr(\sha1($new_nonce.\chr(2).$auth_key_aux_hash, true), -16);
|
||||
$new_nonce_hash3 = \substr(\sha1($new_nonce.\chr(3).$auth_key_aux_hash, true), -16);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
@ -359,13 +390,17 @@ trait AuthKeyHandler
|
||||
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash1');
|
||||
}
|
||||
$this->logger->logger('Diffie Hellman key exchange processed successfully!', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$res_authorization['server_salt'] = substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0);
|
||||
$res_authorization['auth_key'] = $auth_key_str;
|
||||
$res_authorization['id'] = substr($auth_key_sha, -8);
|
||||
$res_authorization['connection_inited'] = false;
|
||||
|
||||
$key = $expires_in < 0 ? new PermAuthKey() : new TempAuthKey();
|
||||
if ($expires_in >= 0) {
|
||||
$key->expires(\time() + $expires_in);
|
||||
}
|
||||
$key->setServerSalt(\substr($new_nonce, 0, 8 - 0) ^ \substr($server_nonce, 0, 8 - 0));
|
||||
$key->setAuthKey($auth_key_str);
|
||||
|
||||
$this->logger->logger('Auth key generated', \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $res_authorization;
|
||||
return $key;
|
||||
case 'dh_gen_retry':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
|
||||
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash_2');
|
||||
@ -385,9 +420,9 @@ trait AuthKeyHandler
|
||||
}
|
||||
}
|
||||
} catch (\danog\MadelineProto\SecurityException $e) {
|
||||
$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);
|
||||
$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);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$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);
|
||||
$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) {
|
||||
$this->logger->logger('An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
|
||||
@ -395,12 +430,20 @@ trait AuthKeyHandler
|
||||
$this->logger->logger('An exception occurred while generating the authorization key: '.$e.PHP_EOL.' Retrying (try number '.$retry_id_total.')...', \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (strpos($datacenter, 'cdn') === false) {
|
||||
if (\strpos($datacenter, 'cdn') === false) {
|
||||
throw new \danog\MadelineProto\SecurityException('Auth Failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function check_G($g_a, $p)
|
||||
/**
|
||||
* Check validity of g_a parameters.
|
||||
*
|
||||
* @param BigInteger $g_a
|
||||
* @param BigInteger $p
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check_G(BigInteger $g_a, BigInteger $p): bool
|
||||
{
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -419,7 +462,15 @@ trait AuthKeyHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
public function check_p_g($p, $g)
|
||||
/**
|
||||
* Check validity of p and g parameters.
|
||||
*
|
||||
* @param BigInteger $p
|
||||
* @param BigInteger $g
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function check_p_g(BigInteger $p, BigInteger $g): bool
|
||||
{
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -465,7 +516,12 @@ trait AuthKeyHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_dh_config_async()
|
||||
/**
|
||||
* Get diffie-hellman configuration.
|
||||
*
|
||||
* @return \Generator<array>
|
||||
*/
|
||||
public function get_dh_config_async(): \Generator
|
||||
{
|
||||
$dh_config = yield $this->method_call_async_read('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
|
||||
if ($dh_config['_'] === 'messages.dhConfigNotModified') {
|
||||
@ -480,29 +536,40 @@ trait AuthKeyHandler
|
||||
return $this->dh_config = $dh_config;
|
||||
}
|
||||
|
||||
public function bind_temp_auth_key_async($expires_in, $datacenter)
|
||||
/**
|
||||
* Bind temporary and permanent auth keys.
|
||||
*
|
||||
* @param integer $expires_in Date of expiry for binding
|
||||
* @param string $datacenter DC ID
|
||||
*
|
||||
* @return \Generator<bool>
|
||||
*/
|
||||
public function bind_temp_auth_key_async(int $expires_in, string $datacenter): \Generator
|
||||
{
|
||||
$datacenterConnection = $this->datacenter->getDataCenterConnection($datacenter);
|
||||
$connection = $datacenterConnection->getAuthConnection();
|
||||
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
$this->logger->logger('Binding authorization keys...', \danog\MadelineProto\Logger::VERBOSE);
|
||||
$nonce = $this->random(8);
|
||||
$expires_at = time() + $expires_in;
|
||||
$temp_auth_key_id = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'];
|
||||
$perm_auth_key_id = $this->datacenter->sockets[$datacenter]->auth_key['id'];
|
||||
$temp_session_id = $this->datacenter->sockets[$datacenter]->session_id;
|
||||
$expires_at = \time() + $expires_in;
|
||||
$temp_auth_key_id = $datacenterConnection->getTempAuthKey()->getID();
|
||||
$perm_auth_key_id = $datacenterConnection->getPermAuthKey()->getID();
|
||||
$temp_session_id = $connection->session_id;
|
||||
$message_data = yield $this->serialize_object_async(['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->datacenter->sockets[$datacenter]->generate_message_id();
|
||||
$message_id = $connection->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 = 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]);
|
||||
$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, $datacenterConnection->getPermAuthKey()->getAuthKey());
|
||||
$encrypted_message = $datacenterConnection->getPermAuthKey()->getID().$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
$res = yield $connection->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]);
|
||||
if ($res === true) {
|
||||
$this->logger->logger('Successfully binded temporary and permanent authorization keys, DC '.$datacenter, \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['bound'] = true;
|
||||
$this->datacenter->sockets[$datacenter]->writer->resume();
|
||||
$datacenterConnection->bind();
|
||||
$datacenterConnection->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -518,7 +585,14 @@ trait AuthKeyHandler
|
||||
throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.');
|
||||
}
|
||||
|
||||
public function wolfram_single_async($what)
|
||||
/**
|
||||
* Factorize number asynchronously using the wolfram API.
|
||||
*
|
||||
* @param string|integer $what Number to factorize
|
||||
*
|
||||
* @return \Generator<string|bool>
|
||||
*/
|
||||
public function wolfram_single_async($what): \Generator
|
||||
{
|
||||
$code = yield $this->datacenter->fileGetContents('http://www.wolframalpha.com/api/v1/code');
|
||||
$query = 'Do prime factorization of '.$what;
|
||||
@ -530,28 +604,28 @@ trait AuthKeyHandler
|
||||
'formattimeout' => 8,
|
||||
'input' => $query,
|
||||
'output' => 'JSON',
|
||||
'proxycode' => json_decode($code, true)['code'],
|
||||
'proxycode' => \json_decode($code, true)['code'],
|
||||
];
|
||||
$url = 'https://www.wolframalpha.com/input/json.jsp?'.http_build_query($params);
|
||||
$url = 'https://www.wolframalpha.com/input/json.jsp?'.\http_build_query($params);
|
||||
|
||||
$request = (new Request($url))->withHeader('referer', 'https://www.wolframalpha.com/input/?i='.urlencode($query));
|
||||
$request = (new Request($url))->withHeader('referer', 'https://www.wolframalpha.com/input/?i='.\urlencode($query));
|
||||
|
||||
$res = json_decode(yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody(), true);
|
||||
$res = \json_decode(yield (yield $this->datacenter->getHTTPClient()->request($request))->getBody(), true);
|
||||
if (!isset($res['queryresult']['pods'])) {
|
||||
return false;
|
||||
}
|
||||
$fres = false;
|
||||
foreach ($res['queryresult']['pods'] as $cur) {
|
||||
if ($cur['id'] === 'Divisors') {
|
||||
$fres = explode(', ', preg_replace(["/{\d+, /", "/, \d+}$/"], '', $cur['subpods'][0]['moutput']));
|
||||
$fres = \explode(', ', \preg_replace(["/{\d+, /", "/, \d+}$/"], '', $cur['subpods'][0]['moutput']));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_array($fres)) {
|
||||
if (\is_array($fres)) {
|
||||
$fres = $fres[0];
|
||||
|
||||
$newval = intval($fres);
|
||||
if (is_int($newval)) {
|
||||
$newval = \intval($fres);
|
||||
if (\is_int($newval)) {
|
||||
$fres = $newval;
|
||||
}
|
||||
|
||||
@ -561,7 +635,12 @@ trait AuthKeyHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
public function init_authorization_async()
|
||||
/**
|
||||
* Asynchronously create, bind and check auth keys for all DCs.
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function init_authorization_async(): \Generator
|
||||
{
|
||||
if ($this->pending_auth) {
|
||||
return;
|
||||
@ -573,9 +652,9 @@ trait AuthKeyHandler
|
||||
try {
|
||||
$dcs = [];
|
||||
$postpone = [];
|
||||
foreach ($this->datacenter->sockets as $id => $socket) {
|
||||
if (strpos($id, 'media') !== false) {
|
||||
$oid = intval($id);
|
||||
foreach ($this->datacenter->getDataCenterConnections() as $id => $socket) {
|
||||
if (\strpos($id, 'media') !== false) {
|
||||
$oid = \intval($id);
|
||||
if (isset($dcs[$oid])) {
|
||||
$postpone[$id] = $socket;
|
||||
}
|
||||
@ -590,7 +669,7 @@ trait AuthKeyHandler
|
||||
};
|
||||
}
|
||||
if ($dcs) {
|
||||
$first = array_shift($dcs)();
|
||||
$first = \array_shift($dcs)();
|
||||
yield $first;
|
||||
}
|
||||
|
||||
@ -613,59 +692,65 @@ trait AuthKeyHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function init_authorization_socket_async($id, $socket)
|
||||
/**
|
||||
* Init auth keys for single DC.
|
||||
*
|
||||
* @param string $id DC ID
|
||||
* @param DataCenterConnection $socket DC object
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function init_authorization_socket_async(string $id, DataCenterConnection $socket): \Generator
|
||||
{
|
||||
$this->init_auth_dcs[$id] = true;
|
||||
$connection = $socket->getAuthConnection();
|
||||
|
||||
try {
|
||||
if ($socket->session_id === null) {
|
||||
$socket->session_id = $this->random(8);
|
||||
$socket->session_in_seq_no = 0;
|
||||
$socket->session_out_seq_no = 0;
|
||||
if ($connection->session_id === null) {
|
||||
$connection->session_id = $this->random(8);
|
||||
$connection->session_in_seq_no = 0;
|
||||
$connection->session_out_seq_no = 0;
|
||||
}
|
||||
$cdn = strpos($id, 'cdn');
|
||||
$media = strpos($id, 'media');
|
||||
if ($socket->temp_auth_key === null || $socket->auth_key === null) {
|
||||
$cdn = \strpos($id, 'cdn');
|
||||
$media = \strpos($id, 'media');
|
||||
if (!$socket->hasTempAuthKey() || !$socket->hasPermAuthKey()) {
|
||||
$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 (!$socket->hasPermAuthKey() && !$cdn && !$media) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_perm_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setPermAuthKey(yield $this->create_auth_key_async(-1, $id));
|
||||
$socket->authorized(false);
|
||||
}
|
||||
if ($media) {
|
||||
$socket->authorized = &$this->datacenter->sockets[intval($id)]->authorized;
|
||||
$socket->link(\intval($id));
|
||||
}
|
||||
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);
|
||||
$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);
|
||||
$socket->setTempAuthKey(null);
|
||||
$socket->setTempAuthKey(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);
|
||||
|
||||
$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 (!$socket->hasTempAuthKey()) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setTempAuthKey(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;
|
||||
$socket->bind(false);
|
||||
$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 (!$socket->hasTempAuthKey()) {
|
||||
$this->logger->logger(\sprintf(\danog\MadelineProto\Lang::$current_lang['gen_temp_auth_key'], $id), \danog\MadelineProto\Logger::NOTICE);
|
||||
$socket->setTempAuthKey(yield $this->create_auth_key_async($this->settings['authorization']['default_temp_auth_key_expires_in'], $id));
|
||||
}
|
||||
}
|
||||
} elseif (!$cdn) {
|
||||
@ -676,23 +761,30 @@ trait AuthKeyHandler
|
||||
}
|
||||
}
|
||||
|
||||
public function sync_authorization_async($id)
|
||||
/**
|
||||
* Sync authorization data between DCs.
|
||||
*
|
||||
* @param string $id DC ID
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function sync_authorization_async(string $id): \Generator
|
||||
{
|
||||
if (!isset($this->datacenter->sockets[$id])) {
|
||||
if (!$this->datacenter->has($id)) {
|
||||
return false;
|
||||
}
|
||||
$socket = $this->datacenter->sockets[$id];
|
||||
if ($this->authorized === self::LOGGED_IN && $socket->authorized === false) {
|
||||
foreach ($this->datacenter->sockets as $authorized_dc_id => $authorized_socket) {
|
||||
$socket = $this->datacenter->getDataCenterConnection($id);
|
||||
if ($this->authorized === self::LOGGED_IN && !$socket->isAuthorized()) {
|
||||
foreach ($this->datacenter->getDataCenterConnections() as $authorized_dc_id => $authorized_socket) {
|
||||
if ($this->authorized_dc !== -1 && $authorized_dc_id !== $this->authorized_dc) {
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
if ($authorized_socket->hasTempAuthKey() && $authorized_socket->hasPermAuthKey() && $authorized_socket->isAuthorized() && $this->authorized === self::LOGGED_IN && !$socket->isAuthorized() && \strpos($authorized_dc_id, 'cdn') === false) {
|
||||
try {
|
||||
$this->logger->logger('Trying to copy authorization from dc '.$authorized_dc_id.' to dc '.$id);
|
||||
$exported_authorization = yield $this->method_call_async_read('auth.exportAuthorization', ['dc_id' => preg_replace('|_.*|', '', $id)], ['datacenter' => $authorized_dc_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;
|
||||
$socket->authorized(true);
|
||||
break;
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$this->logger->logger('Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage(), \danog\MadelineProto\Logger::ERROR);
|
||||
|
@ -19,15 +19,15 @@
|
||||
|
||||
namespace danog\MadelineProto\MTProtoTools;
|
||||
|
||||
use Amp\Artax\Client;
|
||||
use Amp\ByteStream\InputStream;
|
||||
use Amp\ByteStream\OutputStream;
|
||||
use Amp\ByteStream\ResourceInputStream;
|
||||
use Amp\ByteStream\ResourceOutputStream;
|
||||
use Amp\ByteStream\StreamException;
|
||||
use Amp\Deferred;
|
||||
use Amp\File\BlockingHandle;
|
||||
use Amp\File\Handle;
|
||||
use Amp\File\StatCache;
|
||||
use Amp\Promise;
|
||||
use Amp\Success;
|
||||
use danog\MadelineProto\Async\AsyncParameters;
|
||||
use danog\MadelineProto\Exception;
|
||||
@ -37,14 +37,10 @@ use danog\MadelineProto\RPCErrorException;
|
||||
use danog\MadelineProto\Stream\Common\SimpleBufferedRawStream;
|
||||
use danog\MadelineProto\Stream\ConnectionContext;
|
||||
use danog\MadelineProto\Stream\Transport\PremadeStream;
|
||||
use danog\MadelineProto\Tools;
|
||||
use function Amp\File\exists;
|
||||
use function Amp\File\open;
|
||||
use function Amp\File\stat;
|
||||
use function Amp\File\touch;
|
||||
use danog\MadelineProto\Tools;
|
||||
use function Amp\Promise\all;
|
||||
use Amp\File\BlockingHandle;
|
||||
use Amp\Artax\Client;
|
||||
|
||||
/**
|
||||
* Manages upload and download of files.
|
||||
@ -53,15 +49,15 @@ trait Files
|
||||
{
|
||||
public function upload_async($file, $file_name = '', $cb = null, $encrypted = false)
|
||||
{
|
||||
if (is_object($file) && $file instanceof FileCallbackInterface) {
|
||||
if (\is_object($file) && $file instanceof FileCallbackInterface) {
|
||||
$cb = $file;
|
||||
$file = $file->getFile();
|
||||
}
|
||||
if (is_string($file) || (is_object($file) && method_exists($file, '__toString'))) {
|
||||
if (filter_var($file, FILTER_VALIDATE_URL)) {
|
||||
if (\is_string($file) || (\is_object($file) && \method_exists($file, '__toString'))) {
|
||||
if (\filter_var($file, FILTER_VALIDATE_URL)) {
|
||||
return yield $this->upload_from_url_async($file);
|
||||
}
|
||||
} else if (is_array($file)) {
|
||||
} elseif (\is_array($file)) {
|
||||
return yield $this->upload_from_tgfile_async($file, $cb, $encrypted);
|
||||
}
|
||||
|
||||
@ -70,12 +66,12 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['file_not_exist']);
|
||||
}
|
||||
if (empty($file_name)) {
|
||||
$file_name = basename($file);
|
||||
$file_name = \basename($file);
|
||||
}
|
||||
|
||||
StatCache::clear($file);
|
||||
|
||||
$size = (yield stat($file))['size'];
|
||||
$size = (yield \stat($file))['size'];
|
||||
if ($size > 512 * 1024 * 3000) {
|
||||
throw new \danog\MadelineProto\Exception('Given file is too big!');
|
||||
}
|
||||
@ -91,7 +87,7 @@ trait Files
|
||||
}
|
||||
public function upload_from_url_async($url, int $size = 0, string $file_name = '', $cb = null, bool $encrypted = false)
|
||||
{
|
||||
if (is_object($url) && $url instanceof FileCallbackInterface) {
|
||||
if (\is_object($url) && $url instanceof FileCallbackInterface) {
|
||||
$cb = $url;
|
||||
$url = $url->getFile();
|
||||
}
|
||||
@ -100,7 +96,7 @@ trait Files
|
||||
if (200 !== $status = $response->getStatus()) {
|
||||
throw new Exception("Wrong status code: $status ".$response->getReason());
|
||||
}
|
||||
$mime = trim(explode(';', $response->getHeader('content-type') ?? 'application/octet-stream')[0]);
|
||||
$mime = \trim(\explode(';', $response->getHeader('content-type') ?? 'application/octet-stream')[0]);
|
||||
$size = $response->getHeader('content-length') ?? $size;
|
||||
|
||||
$stream = $response->getBody();
|
||||
@ -108,7 +104,7 @@ trait Files
|
||||
$this->logger->logger("No content length for $url, caching first");
|
||||
|
||||
$body = $stream;
|
||||
$stream = new BlockingHandle(fopen('php://temp', 'r+b'), 'php://temp', 'r+b');
|
||||
$stream = new BlockingHandle(\fopen('php://temp', 'r+b'), 'php://temp', 'r+b');
|
||||
|
||||
while (null !== $chunk = yield $body->read()) {
|
||||
yield $stream->write($chunk);
|
||||
@ -124,20 +120,20 @@ trait Files
|
||||
}
|
||||
public function upload_from_stream_async($stream, int $size, string $mime, string $file_name = '', $cb = null, bool $encrypted = false)
|
||||
{
|
||||
if (is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||
if (\is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||
$cb = $stream;
|
||||
$stream = $stream->getFile();
|
||||
}
|
||||
|
||||
/** @var $stream \Amp\ByteStream\OutputStream */
|
||||
if (!is_object($stream)) {
|
||||
if (!\is_object($stream)) {
|
||||
$stream = new ResourceOutputStream($stream);
|
||||
}
|
||||
if (!$stream instanceof InputStream) {
|
||||
throw new Exception("Invalid stream provided");
|
||||
}
|
||||
$seekable = false;
|
||||
if (method_exists($stream, 'seek')) {
|
||||
if (\method_exists($stream, 'seek')) {
|
||||
try {
|
||||
yield $stream->seek(0);
|
||||
$seekable = true;
|
||||
@ -184,11 +180,11 @@ trait Files
|
||||
}
|
||||
public function upload_from_callable_async($callable, int $size, string $mime, string $file_name = '', $cb = null, bool $refetchable = true, bool $encrypted = false)
|
||||
{
|
||||
if (is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||
if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||
$cb = $callable;
|
||||
$callable = $callable->getFile();
|
||||
}
|
||||
if (!is_callable($callable)) {
|
||||
if (!\is_callable($callable)) {
|
||||
throw new Exception('Invalid callable provided');
|
||||
}
|
||||
if ($cb === null) {
|
||||
@ -198,14 +194,14 @@ trait Files
|
||||
}
|
||||
|
||||
$datacenter = $this->settings['connection_settings']['default_dc'];
|
||||
if (isset($this->datacenter->sockets[$datacenter.'_media'])) {
|
||||
if ($this->datacenter->has($datacenter.'_media')) {
|
||||
$datacenter .= '_media';
|
||||
}
|
||||
|
||||
$part_size = $this->settings['upload']['part_size'];
|
||||
$parallel_chunks = $this->settings['upload']['parallel_chunks'] ? $this->settings['upload']['parallel_chunks'] : 3000;
|
||||
|
||||
$part_total_num = (int) ceil($size / $part_size);
|
||||
$part_total_num = (int) \ceil($size / $part_size);
|
||||
$part_num = 0;
|
||||
$method = $size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
|
||||
$constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($size > 10 * 1024 * 1024 ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
|
||||
@ -215,15 +211,15 @@ trait Files
|
||||
if ($encrypted === true) {
|
||||
$key = $this->random(32);
|
||||
$iv = $this->random(32);
|
||||
$digest = hash('md5', $key.$iv, true);
|
||||
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
|
||||
$digest = \hash('md5', $key.$iv, true);
|
||||
$fingerprint = $this->unpack_signed_int(\substr($digest, 0, 4) ^ \substr($digest, 4, 4));
|
||||
$ige = new \phpseclib\Crypt\AES('ige');
|
||||
$ige->setIV($iv);
|
||||
$ige->setKey($key);
|
||||
$ige->enableContinuousBuffer();
|
||||
$refetchable = false;
|
||||
}
|
||||
$ctx = hash_init('md5');
|
||||
$ctx = \hash_init('md5');
|
||||
$promises = [];
|
||||
|
||||
$cb = function () use ($cb, $part_total_num) {
|
||||
@ -232,7 +228,7 @@ trait Files
|
||||
$this->callFork($cb($cur * 100 / $part_total_num));
|
||||
};
|
||||
|
||||
$start = microtime(true);
|
||||
$start = \microtime(true);
|
||||
while ($part_num < $part_total_num) {
|
||||
$read_deferred = yield $this->method_call_async_write(
|
||||
$method,
|
||||
@ -245,10 +241,10 @@ trait Files
|
||||
$bytes = yield $callable($part_num * $part_size, $part_size);
|
||||
|
||||
if (!$already_fetched) {
|
||||
hash_update($ctx, $bytes);
|
||||
\hash_update($ctx, $bytes);
|
||||
}
|
||||
if ($ige) {
|
||||
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
|
||||
$bytes = $ige->encrypt(\str_pad($bytes, $part_size, \chr(0)));
|
||||
}
|
||||
|
||||
return ['file_id' => $file_id, 'file_part' => $part_num, 'file_total_parts' => $part_total_num, 'bytes' => $bytes];
|
||||
@ -275,7 +271,7 @@ trait Files
|
||||
}
|
||||
$promises = [];
|
||||
|
||||
$time = microtime(true) - $start;
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Partial upload time: $time");
|
||||
$this->logger->logger("Partial upload speed: $speed mbps");
|
||||
@ -288,7 +284,7 @@ trait Files
|
||||
throw new \danog\MadelineProto\Exception('Upload of part '.$kkey.' failed');
|
||||
}
|
||||
}
|
||||
$time = microtime(true) - $start;
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Total upload time: $time");
|
||||
$this->logger->logger("Total upload speed: $speed mbps");
|
||||
@ -299,7 +295,7 @@ trait Files
|
||||
$constructor['key'] = $key;
|
||||
$constructor['iv'] = $iv;
|
||||
}
|
||||
$constructor['md5_checksum'] = hash_final($ctx);
|
||||
$constructor['md5_checksum'] = \hash_final($ctx);
|
||||
|
||||
return $constructor;
|
||||
}
|
||||
@ -311,7 +307,7 @@ trait Files
|
||||
|
||||
public function upload_from_tgfile_async($media, $cb = null, $encrypted = false)
|
||||
{
|
||||
if (is_object($media) && $media instanceof FileCallbackInterface) {
|
||||
if (\is_object($media) && $media instanceof FileCallbackInterface) {
|
||||
$cb = $media;
|
||||
$media = $media->getFile();
|
||||
}
|
||||
@ -324,9 +320,7 @@ trait Files
|
||||
|
||||
$chunk_size = $this->settings['upload']['part_size'];
|
||||
|
||||
$bridge = new class
|
||||
|
||||
{
|
||||
$bridge = new class {
|
||||
private $done = [];
|
||||
private $pending = [];
|
||||
public $nextRead;
|
||||
@ -343,7 +337,7 @@ trait Files
|
||||
}
|
||||
|
||||
if (isset($this->done[$offset])) {
|
||||
if (strlen($this->done[$offset]) > $size) {
|
||||
if (\strlen($this->done[$offset]) > $size) {
|
||||
throw new Exception('Wrong size!');
|
||||
}
|
||||
$result = $this->done[$offset];
|
||||
@ -362,7 +356,7 @@ trait Files
|
||||
} else {
|
||||
$this->done[$offset] = $data;
|
||||
}
|
||||
$length = strlen($data);
|
||||
$length = \strlen($data);
|
||||
if ($offset + $length === $this->size || $length < $this->part_size) {
|
||||
return;
|
||||
}
|
||||
@ -477,7 +471,7 @@ trait Files
|
||||
|
||||
public function get_file_info_async($constructor)
|
||||
{
|
||||
if (is_string($constructor)) {
|
||||
if (\is_string($constructor)) {
|
||||
$constructor = $this->unpack_file_id($constructor)['MessageMedia'];
|
||||
}
|
||||
switch ($constructor['_']) {
|
||||
@ -487,6 +481,7 @@ trait Files
|
||||
case 'updateEditChannelMessage':
|
||||
$constructor = $constructor['message'];
|
||||
|
||||
// no break
|
||||
case 'message':
|
||||
$constructor = $constructor['media'];
|
||||
}
|
||||
@ -499,7 +494,7 @@ trait Files
|
||||
}
|
||||
public function get_download_info_async($message_media)
|
||||
{
|
||||
if (is_string($message_media)) {
|
||||
if (\is_string($message_media)) {
|
||||
$message_media = $this->unpack_file_id($message_media)['MessageMedia'];
|
||||
}
|
||||
if (!isset($message_media['_'])) {
|
||||
@ -511,12 +506,14 @@ trait Files
|
||||
case 'updateNewMessage':
|
||||
case 'updateNewChannelMessage':
|
||||
$message_media = $message_media['message'];
|
||||
// no break
|
||||
case 'message':
|
||||
return yield $this->get_download_info_async($message_media['media']);
|
||||
case 'updateNewEncryptedMessage':
|
||||
$message_media = $message_media['message'];
|
||||
|
||||
// Secret media
|
||||
// no break
|
||||
case 'encryptedMessage':
|
||||
if ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaExternalDocument') {
|
||||
return yield $this->get_download_info_async($message_media['decrypted_message']['media']);
|
||||
@ -527,7 +524,7 @@ trait Files
|
||||
$res['key'] = $message_media['decrypted_message']['media']['key'];
|
||||
$res['iv'] = $message_media['decrypted_message']['media']['iv'];
|
||||
if (isset($message_media['decrypted_message']['media']['file_name'])) {
|
||||
$pathinfo = pathinfo($message_media['decrypted_message']['media']['file_name']);
|
||||
$pathinfo = \pathinfo($message_media['decrypted_message']['media']['file_name']);
|
||||
if (isset($pathinfo['extension'])) {
|
||||
$res['ext'] = '.'.$pathinfo['extension'];
|
||||
}
|
||||
@ -542,7 +539,7 @@ trait Files
|
||||
foreach ($message_media['decrypted_message']['media']['attributes'] as $attribute) {
|
||||
switch ($attribute['_']) {
|
||||
case 'documentAttributeFilename':
|
||||
$pathinfo = pathinfo($attribute['file_name']);
|
||||
$pathinfo = \pathinfo($attribute['file_name']);
|
||||
if (isset($pathinfo['extension'])) {
|
||||
$res['ext'] = '.'.$pathinfo['extension'];
|
||||
}
|
||||
@ -582,9 +579,9 @@ trait Files
|
||||
}
|
||||
$res['MessageMedia'] = $message_media;
|
||||
$message_media = $message_media['photo'];
|
||||
$size = end($message_media['sizes']);
|
||||
$size = \end($message_media['sizes']);
|
||||
|
||||
$res = array_merge($res, yield $this->get_download_info_async($size));
|
||||
$res = \array_merge($res, yield $this->get_download_info_async($size));
|
||||
|
||||
$res['InputFileLocation'] = [
|
||||
'_' => 'inputPhotoFileLocation',
|
||||
@ -625,13 +622,13 @@ trait Files
|
||||
$res['InputFileLocation']['dc_id'] = $message_media['dc_id'];
|
||||
return $res;
|
||||
case 'photoStrippedSize':
|
||||
$res['size'] = strlen($message_media['bytes']);
|
||||
$res['size'] = \strlen($message_media['bytes']);
|
||||
$res['data'] = $message_media['bytes'];
|
||||
$res['thumb_size'] = 'JPG';
|
||||
return $res;
|
||||
|
||||
case 'photoCachedSize':
|
||||
$res['size'] = strlen($message_media['bytes']);
|
||||
$res['size'] = \strlen($message_media['bytes']);
|
||||
$res['data'] = $message_media['bytes'];
|
||||
//$res['thumb_size'] = $res['data'];
|
||||
$res['thumb_size'] = $message_media['type'];
|
||||
@ -641,7 +638,7 @@ trait Files
|
||||
$res['mime'] = $this->get_mime_from_buffer($res['data']);
|
||||
$res['ext'] = $this->get_extension_from_mime($res['mime']);
|
||||
} else {
|
||||
$res = array_merge($res, yield $this->get_download_info_async($message_media['location']));
|
||||
$res = \array_merge($res, yield $this->get_download_info_async($message_media['location']));
|
||||
}
|
||||
|
||||
return $res;
|
||||
@ -691,13 +688,14 @@ trait Files
|
||||
case 'decryptedMessageMediaExternalDocument':
|
||||
case 'document':
|
||||
$message_media = ['_' => 'messageMediaDocument', 'ttl_seconds' => 0, 'document' => $message_media];
|
||||
// no break
|
||||
case 'messageMediaDocument':
|
||||
$res['MessageMedia'] = $message_media;
|
||||
|
||||
foreach ($message_media['document']['attributes'] as $attribute) {
|
||||
switch ($attribute['_']) {
|
||||
case 'documentAttributeFilename':
|
||||
$pathinfo = pathinfo($attribute['file_name']);
|
||||
$pathinfo = \pathinfo($attribute['file_name']);
|
||||
if (isset($pathinfo['extension'])) {
|
||||
$res['ext'] = '.'.$pathinfo['extension'];
|
||||
}
|
||||
@ -842,7 +840,7 @@ trait Files
|
||||
}
|
||||
public function download_to_dir_async($message_media, $dir, $cb = null)
|
||||
{
|
||||
if (is_object($dir) && $dir instanceof FileCallbackInterface) {
|
||||
if (\is_object($dir) && $dir instanceof FileCallbackInterface) {
|
||||
$cb = $dir;
|
||||
$dir = $dir->getFile();
|
||||
}
|
||||
@ -854,20 +852,20 @@ trait Files
|
||||
|
||||
public function download_to_file_async($message_media, $file, $cb = null)
|
||||
{
|
||||
if (is_object($file) && $file instanceof FileCallbackInterface) {
|
||||
if (\is_object($file) && $file instanceof FileCallbackInterface) {
|
||||
$cb = $file;
|
||||
$file = $file->getFile();
|
||||
}
|
||||
$file = \danog\MadelineProto\Absolute::absolute(preg_replace('|/+|', '/', $file));
|
||||
$file = \danog\MadelineProto\Absolute::absolute(\preg_replace('|/+|', '/', $file));
|
||||
if (!yield exists($file)) {
|
||||
yield touch($file);
|
||||
yield \touch($file);
|
||||
}
|
||||
$file = realpath($file);
|
||||
$file = \realpath($file);
|
||||
$message_media = yield $this->get_download_info_async($message_media);
|
||||
|
||||
StatCache::clear($file);
|
||||
|
||||
$size = (yield stat($file))['size'];
|
||||
$size = (yield \stat($file))['size'];
|
||||
$stream = yield open($file, 'cb');
|
||||
|
||||
$this->logger->logger('Waiting for lock of file to download...');
|
||||
@ -887,20 +885,20 @@ trait Files
|
||||
{
|
||||
$message_media = yield $this->get_download_info_async($message_media);
|
||||
|
||||
if (is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||
if (\is_object($stream) && $stream instanceof FileCallbackInterface) {
|
||||
$cb = $stream;
|
||||
$stream = $stream->getFile();
|
||||
}
|
||||
|
||||
/** @var $stream \Amp\ByteStream\OutputStream */
|
||||
if (!is_object($stream)) {
|
||||
if (!\is_object($stream)) {
|
||||
$stream = new ResourceOutputStream($stream);
|
||||
}
|
||||
if (!$stream instanceof OutputStream) {
|
||||
throw new Exception("Invalid stream provided");
|
||||
}
|
||||
$seekable = false;
|
||||
if (method_exists($stream, 'seek')) {
|
||||
if (\method_exists($stream, 'seek')) {
|
||||
try {
|
||||
yield $stream->seek($offset);
|
||||
$seekable = true;
|
||||
@ -922,12 +920,12 @@ trait Files
|
||||
{
|
||||
$message_media = yield $this->get_download_info_async($message_media);
|
||||
|
||||
if (is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||
if (\is_object($callable) && $callable instanceof FileCallbackInterface) {
|
||||
$cb = $callable;
|
||||
$callable = $callable->getFile();
|
||||
}
|
||||
|
||||
if (!is_callable($callable)) {
|
||||
if (!\is_callable($callable)) {
|
||||
throw new Exception('Wrong callable provided');
|
||||
}
|
||||
if ($cb === null) {
|
||||
@ -944,13 +942,13 @@ trait Files
|
||||
$parallel_chunks = $this->settings['download']['parallel_chunks'] ? $this->settings['download']['parallel_chunks'] : 3000;
|
||||
|
||||
$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'])) {
|
||||
if ($this->datacenter->has($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));
|
||||
$digest = \hash('md5', $message_media['key'].$message_media['iv'], true);
|
||||
$fingerprint = $this->unpack_signed_int(\substr($digest, 0, 4) ^ \substr($digest, 4, 4));
|
||||
if ($fingerprint !== $message_media['key_fingerprint']) {
|
||||
throw new \danog\MadelineProto\Exception('Fingerprint mismatch!');
|
||||
}
|
||||
@ -995,7 +993,7 @@ trait Files
|
||||
$cb(100);
|
||||
return true;
|
||||
}
|
||||
$count = count($params);
|
||||
$count = \count($params);
|
||||
|
||||
$cb = function () use ($cb, $count) {
|
||||
static $cur = 0;
|
||||
@ -1007,8 +1005,8 @@ trait Files
|
||||
|
||||
$params[0]['previous_promise'] = new Success(true);
|
||||
|
||||
$start = microtime(true);
|
||||
$size = yield $this->download_part($message_media, $cdn, $datacenter, $old_dc, $ige, $cb, array_shift($params), $callable, $parallelize);
|
||||
$start = \microtime(true);
|
||||
$size = yield $this->download_part($message_media, $cdn, $datacenter, $old_dc, $ige, $cb, \array_shift($params), $callable, $parallelize);
|
||||
|
||||
if ($params) {
|
||||
$previous_promise = new Success(true);
|
||||
@ -1029,7 +1027,7 @@ trait Files
|
||||
yield $this->all($promises);
|
||||
$promises = [];
|
||||
|
||||
$time = microtime(true) - $start;
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Partial download time: $time");
|
||||
$this->logger->logger("Partial download speed: $speed mbps");
|
||||
@ -1039,7 +1037,7 @@ trait Files
|
||||
yield $this->all($promises);
|
||||
}
|
||||
}
|
||||
$time = microtime(true) - $start;
|
||||
$time = \microtime(true) - $start;
|
||||
$speed = (int) (($size * 8) / $time) / 1000000;
|
||||
$this->logger->logger("Total download time: $time");
|
||||
$this->logger->logger("Total download speed: $speed mbps");
|
||||
@ -1081,7 +1079,7 @@ trait Files
|
||||
]
|
||||
);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if (strpos($e->rpc, 'FLOOD_WAIT_') === 0) {
|
||||
if (\strpos($e->rpc, 'FLOOD_WAIT_') === 0) {
|
||||
if (isset($message_media['MessageMedia']) && !$this->authorization['user']['bot'] && $this->settings['download']['report_broken_media']) {
|
||||
try {
|
||||
yield $this->method_call_async_read('messages.sendMedia', ['peer' => 'support', 'media' => $message_media['MessageMedia'], 'message' => "I can't download this file, could you please help?"], ['datacenter' => $this->datacenter->curdc]);
|
||||
@ -1110,12 +1108,12 @@ trait Files
|
||||
$message_media['cdn_iv'] = $res['encryption_iv'];
|
||||
$old_dc = $datacenter;
|
||||
$datacenter = $res['dc_id'].'_cdn';
|
||||
if (!isset($this->datacenter->sockets[$datacenter])) {
|
||||
if (!$this->datacenter->has($datacenter)) {
|
||||
$this->config['expires'] = -1;
|
||||
yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['stored_on_cdn'], \danog\MadelineProto\Logger::NOTICE);
|
||||
} else if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
|
||||
} elseif ($res['_'] === 'upload.cdnFileReuploadNeeded') {
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['cdn_reupload'], \danog\MadelineProto\Logger::NOTICE);
|
||||
yield $this->get_config_async([], ['datacenter' => $this->datacenter->curdc]);
|
||||
|
||||
@ -1139,13 +1137,13 @@ trait Files
|
||||
while ($cdn === false &&
|
||||
$res['type']['_'] === 'storage.fileUnknown' &&
|
||||
$res['bytes'] === '' &&
|
||||
isset($this->datacenter->sockets[++$datacenter])
|
||||
$this->datacenter->has(++$datacenter)
|
||||
) {
|
||||
$res = yield $this->method_call_async_read('upload.getFile', $basic_param + $offset, ['heavy' => true, 'file' => true, 'FloodWaitLimit' => 0, 'datacenter' => $datacenter]);
|
||||
}
|
||||
|
||||
if (isset($message_media['cdn_key'])) {
|
||||
$ivec = substr($message_media['cdn_iv'], 0, 12).pack('N', $offset['offset'] >> 4);
|
||||
$ivec = \substr($message_media['cdn_iv'], 0, 12).\pack('N', $offset['offset'] >> 4);
|
||||
$res['bytes'] = $this->ctr_encrypt($res['bytes'], $message_media['cdn_key'], $ivec);
|
||||
$this->check_cdn_hash($message_media['file_token'], $offset['offset'], $res['bytes'], $old_dc);
|
||||
}
|
||||
@ -1153,7 +1151,7 @@ trait Files
|
||||
$res['bytes'] = $ige->decrypt($res['bytes']);
|
||||
}
|
||||
if ($offset['part_start_at'] || $offset['part_end_at'] !== $offset['limit']) {
|
||||
$res['bytes'] = substr($res['bytes'], $offset['part_start_at'], $offset['part_end_at'] - $offset['part_start_at']);
|
||||
$res['bytes'] = \substr($res['bytes'], $offset['part_start_at'], $offset['part_end_at'] - $offset['part_start_at']);
|
||||
}
|
||||
|
||||
if (!$seekable) {
|
||||
@ -1179,17 +1177,17 @@ trait Files
|
||||
|
||||
private function check_cdn_hash($file, $offset, $data, &$datacenter)
|
||||
{
|
||||
while (strlen($data)) {
|
||||
while (\strlen($data)) {
|
||||
if (!isset($this->cdn_hashes[$file][$offset])) {
|
||||
$this->add_cdn_hashes($file, yield $this->method_call_async_read('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset], ['datacenter' => $datacenter]));
|
||||
}
|
||||
if (!isset($this->cdn_hashes[$file][$offset])) {
|
||||
throw new \danog\MadelineProto\Exception('Could not fetch CDN hashes for offset '.$offset);
|
||||
}
|
||||
if (hash('sha256', substr($data, 0, $this->cdn_hashes[$file][$offset]['limit']), true) !== $this->cdn_hashes[$file][$offset]['hash']) {
|
||||
if (\hash('sha256', \substr($data, 0, $this->cdn_hashes[$file][$offset]['limit']), true) !== $this->cdn_hashes[$file][$offset]['hash']) {
|
||||
throw new \danog\MadelineProto\SecurityException('CDN hash mismatch for offset '.$offset);
|
||||
}
|
||||
$data = substr($data, $this->cdn_hashes[$file][$offset]['limit']);
|
||||
$data = \substr($data, $this->cdn_hashes[$file][$offset]['limit']);
|
||||
$offset += $this->cdn_hashes[$file][$offset]['limit'];
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,12 @@ class ConnectionContext
|
||||
* @var bool
|
||||
*/
|
||||
private $media = false;
|
||||
/**
|
||||
* Whether to use CDN servers.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $cdn = false;
|
||||
/**
|
||||
* The connection URI.
|
||||
*
|
||||
@ -232,6 +238,16 @@ class ConnectionContext
|
||||
return $this->media;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this is a CDN connection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCDN(): bool
|
||||
{
|
||||
return $this->cdn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this connection context will only be used by the DNS client
|
||||
*
|
||||
@ -292,6 +308,7 @@ class ConnectionContext
|
||||
}
|
||||
$this->dc = $dc;
|
||||
$this->media = strpos($dc, '_media') !== false;
|
||||
$this->cdn = strpos($dc, '_cdn') !== false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
namespace danog\MadelineProto\Wrappers;
|
||||
|
||||
use danog\MadelineProto\AuthKey\PermAuthKey;
|
||||
use danog\MadelineProto\MTProtoTools\PasswordCalculator;
|
||||
|
||||
/**
|
||||
@ -26,6 +27,12 @@ use danog\MadelineProto\MTProtoTools\PasswordCalculator;
|
||||
*/
|
||||
trait Login
|
||||
{
|
||||
/**
|
||||
* Datacenter instance.
|
||||
*
|
||||
* @var \danog\MadelineProto\DataCenter
|
||||
*/
|
||||
public $datacenter;
|
||||
public function logout_async()
|
||||
{
|
||||
yield $this->method_call_async_read('auth.logOut', [], ['datacenter' => $this->datacenter->curdc]);
|
||||
@ -46,7 +53,7 @@ trait Login
|
||||
$this->authorization = yield $this->method_call_async_read('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorized_dc = $this->datacenter->curdc;
|
||||
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
$this->updates = [];
|
||||
$this->updates_key = 0;
|
||||
yield $this->init_authorization_async();
|
||||
@ -117,7 +124,7 @@ trait Login
|
||||
}
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->authorization = $authorization;
|
||||
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
yield $this->init_authorization_async();
|
||||
yield $this->get_phone_config_async();
|
||||
$this->startUpdateSystem();
|
||||
@ -135,20 +142,16 @@ trait Login
|
||||
}
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_auth_key'], \danog\MadelineProto\Logger::NOTICE);
|
||||
list($dc_id, $auth_key) = $authorization;
|
||||
if (!is_array($auth_key)) {
|
||||
$auth_key = ['auth_key' => $auth_key, 'id' => substr(sha1($auth_key, true), -8), 'server_salt' => ''];
|
||||
if (!\is_array($auth_key)) {
|
||||
$auth_key = ['auth_key' => $auth_key];
|
||||
}
|
||||
$auth_key = new PermAuthKey($auth_key);
|
||||
|
||||
$this->authorized_dc = $dc_id;
|
||||
$this->datacenter->sockets[$dc_id]->session_id = $this->random(8);
|
||||
$this->datacenter->sockets[$dc_id]->session_in_seq_no = 0;
|
||||
$this->datacenter->sockets[$dc_id]->session_out_seq_no = 0;
|
||||
$this->datacenter->sockets[$dc_id]->auth_key = $auth_key;
|
||||
$this->datacenter->sockets[$dc_id]->temp_auth_key = null;
|
||||
$this->datacenter->sockets[$dc_id]->incoming_messages = [];
|
||||
$this->datacenter->sockets[$dc_id]->outgoing_messages = [];
|
||||
$this->datacenter->sockets[$dc_id]->new_outgoing = [];
|
||||
$this->datacenter->sockets[$dc_id]->new_incoming = [];
|
||||
$this->datacenter->sockets[$dc_id]->authorized = true;
|
||||
$dataCenterConnection = $this->datacenter->getDataCenterConnection($dc_id);
|
||||
$dataCenterConnection->resetSession();
|
||||
$dataCenterConnection->setPermAuthKey($auth_key);
|
||||
$dataCenterConnection->authorized(true);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
yield $this->init_authorization_async();
|
||||
yield $this->get_phone_config_async();
|
||||
@ -168,7 +171,7 @@ trait Login
|
||||
yield $this->get_self_async();
|
||||
$this->authorized_dc = $this->datacenter->curdc;
|
||||
|
||||
return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']];
|
||||
return [$this->datacenter->curdc, $this->datacenter->getDataCenterConnection($this->datacenter->curdc)->getPermAuthKey()->getAuthKey()];
|
||||
}
|
||||
|
||||
public function complete_signup_async($first_name, $last_name)
|
||||
@ -180,7 +183,7 @@ trait Login
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['signing_up'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield $this->method_call_async_read('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
yield $this->init_authorization_async();
|
||||
yield $this->get_phone_config_async();
|
||||
|
||||
@ -201,7 +204,7 @@ trait Login
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_user'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->authorization = yield $this->method_call_async_read('auth.checkPassword', ['password' => $hasher->getCheckPassword($password)], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->authorized = self::LOGGED_IN;
|
||||
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
|
||||
$this->datacenter->getDataCenterConnection($this->datacenter->curdc)->authorized(true);
|
||||
yield $this->init_authorization_async();
|
||||
$this->logger->logger(\danog\MadelineProto\Lang::$current_lang['login_ok'], \danog\MadelineProto\Logger::NOTICE);
|
||||
yield $this->get_phone_config_async();
|
||||
@ -211,7 +214,7 @@ trait Login
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the 2FA password
|
||||
* Update the 2FA password.
|
||||
*
|
||||
* The params array can contain password, new_password, email and hint params.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user