Added APIFactory for calling namespaced mtproto methods, cleaned up testing.php and API.php, moved authorization keys, session_id, seq_no and time delta to the Connection class, added close_and_reopen method to the Connection class, improved DataCenter class, renamed Logging class to Logger, added a bit more logging, added setup_logger, switch_dc, init_authorization methods to the MTProto class, added parameter to disable automatic switching to nearest DC in write_client_info, added a try catch block in the create_auth_key method, fixed switching of DCs in wait_for_response method, added arguments check in the method calling methods, added a message id check in MessageHandler, added method_name_namespaced array in TL for APIFactory, renamed a lot of stuff and removed a few checks in the TL class, moved sendCode test call to testing.php
This commit is contained in:
parent
49a555d99f
commit
1ded657739
1
.gitignore
vendored
1
.gitignore
vendored
@ -63,3 +63,4 @@ target/
|
||||
vendor
|
||||
*save
|
||||
*bak
|
||||
number.php
|
||||
|
@ -30,6 +30,7 @@ src/danog/MadelineProto/
|
||||
TLConstructor - Represents a TL Constructor
|
||||
TLMethod - Represents a TL method
|
||||
API - Wrapper class that istantiates the MTProto class, sets the error handler, and provides a wrapper for calling mtproto methods directly as class submethods
|
||||
APIFactory - Provides a wrapper for calling namespaced mtproto methods directly as class submethods
|
||||
Connection - Handles tcp/udp/http connections and wrapping payloads generated by MTProtoTools/MessageHandler into the right message according to the protocol
|
||||
DataCenter - Handles mtproto datacenters
|
||||
DebugTools - Various debugging tools
|
||||
@ -47,4 +48,3 @@ This project adheres to the [Hacktoberfest](https://hacktoberfest.digitalocean.c
|
||||
Browse the issues of this project and help close at least four of them with a pull request to get a free t-shirt!
|
||||
Check out the [Contribution guide first though](https://github.com/danog/MadelineProto/blob/master/CONTRIBUTING.md).
|
||||
|
||||
The name of this project is inspired by [this person](https://s-media-cache-ak0.pinimg.com/736x/f0/a1/70/f0a170718baeb0e3817c612d96f5d1cf.jpg).
|
||||
|
@ -1,5 +0,0 @@
|
||||
[App data]
|
||||
api_id = 25628
|
||||
api_hash = 1fe17cda7d355166cdaa71f04122873c
|
||||
ip_address = 149.154.167.50
|
||||
port = 443
|
@ -20,11 +20,23 @@ class API extends Tools
|
||||
{
|
||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
||||
$this->session = new MTProto($params);
|
||||
$ping_res = $this->ping(3);
|
||||
if (isset($ping['_']) && $ping['_'] == 'pong') {
|
||||
$this->log->log('Pong: '.$ping['ping_id']);
|
||||
|
||||
\danog\MadelineProto\Logger::log('Running APIFactory...');
|
||||
foreach ($this->session->tl->method_name_namespaced as $method) {
|
||||
if (isset($method[1])) {
|
||||
if (!isset($this->{$method[0]})) {
|
||||
$this->{$method[0]} = new APIFactory($method[0], $this->session);
|
||||
}
|
||||
$this->{$method[0]}->allowed_methods[] = $method[1];
|
||||
}
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Ping...');
|
||||
$ping = $this->ping(3);
|
||||
\danog\MadelineProto\Logger::log('Pong: '.$ping['ping_id']);
|
||||
\danog\MadelineProto\Logger::log('Getting future salts...');
|
||||
$future_salts = $this->get_future_salts(3);
|
||||
\danog\MadelineProto\Logger::log('MadelineProto is ready!');
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@ -35,6 +47,9 @@ class API extends Tools
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (!in_array($name, $this->session->tl->method_name_namespaced)) {
|
||||
throw new Exception("The called method doesn't exist!");
|
||||
}
|
||||
return $this->session->method_call($name, $arguments);
|
||||
}
|
||||
}
|
||||
|
34
src/danog/MadelineProto/APIFactory.php
Normal file
34
src/danog/MadelineProto/APIFactory.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016 Daniil Gentili
|
||||
(https://daniil.it)
|
||||
This file is part of MadelineProto.
|
||||
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with the MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
class APIFactory
|
||||
{
|
||||
public $namespace;
|
||||
public $session;
|
||||
public $allowed_methods = [];
|
||||
|
||||
public function __construct($namespace, $session) {
|
||||
$this->namespace = $namespace;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if (!in_array($name, $this->allowed_methods)) {
|
||||
throw new Exception("The called method doesn't exist!");
|
||||
}
|
||||
return $this->session->method_call($this->namespace.'.'.$name, $arguments);
|
||||
}
|
||||
|
||||
}
|
@ -18,8 +18,14 @@ namespace danog\MadelineProto;
|
||||
class Connection extends Tools
|
||||
{
|
||||
public $sock = null;
|
||||
|
||||
public $protocol = null;
|
||||
private $_delta = 0;
|
||||
|
||||
public $time_delta = 0;
|
||||
public $temp_auth_key;
|
||||
public $auth_key;
|
||||
public $session_id;
|
||||
public $seq_no = 0;
|
||||
|
||||
public function __construct($ip, $port, $protocol = 'tcp_full')
|
||||
{
|
||||
@ -33,6 +39,8 @@ class Connection extends Tools
|
||||
- udp
|
||||
*/
|
||||
$this->protocol = $protocol;
|
||||
$this->ip = $ip;
|
||||
$this->port = $port;
|
||||
switch ($this->protocol) {
|
||||
case 'tcp_abridged':
|
||||
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
|
||||
@ -59,6 +67,10 @@ class Connection extends Tools
|
||||
$this->out_seq_no = -1;
|
||||
$this->in_seq_no = -1;
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
default:
|
||||
throw new Exception('Connection: invalid protocol specified.');
|
||||
break;
|
||||
@ -73,20 +85,19 @@ class Connection extends Tools
|
||||
case 'tcp_full':
|
||||
fclose($this->sock);
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
default:
|
||||
throw new Exception('Connection: invalid protocol specified.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function set_time_delta($delta)
|
||||
{
|
||||
$this->_delta = $delta;
|
||||
}
|
||||
|
||||
public function get_time_delta()
|
||||
{
|
||||
return $this->_delta;
|
||||
public function close_and_reopen() {
|
||||
$this->__destruct();
|
||||
$this->__construct($this->ip, $this->port, $this->protocol);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,6 +125,10 @@ class Connection extends Tools
|
||||
|
||||
return fwrite($this->sock, $what);
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
default:
|
||||
throw new Exception('Connection: invalid protocol specified.');
|
||||
break;
|
||||
@ -132,6 +147,10 @@ class Connection extends Tools
|
||||
|
||||
return fread($this->sock, $length);
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
default:
|
||||
throw new Exception('Connection: invalid protocol specified.');
|
||||
break;
|
||||
@ -182,6 +201,10 @@ class Connection extends Tools
|
||||
$packet = $this->sock->read($packet_length);
|
||||
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
}
|
||||
|
||||
return $payload;
|
||||
@ -209,6 +232,10 @@ class Connection extends Tools
|
||||
}
|
||||
$this->write($step1);
|
||||
break;
|
||||
case 'http':
|
||||
case 'https':
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ namespace danog\MadelineProto;
|
||||
*/
|
||||
class DataCenter extends Tools
|
||||
{
|
||||
public $referenced_variables = ["time_delta", "temp_auth_key", "auth_key", "session_id", "seq_no"];
|
||||
|
||||
public function __construct($dclist, $settings)
|
||||
{
|
||||
$this->dclist = $dclist;
|
||||
@ -36,13 +38,12 @@ class DataCenter extends Tools
|
||||
];
|
||||
}
|
||||
}
|
||||
$this->dc_connect($settings['default_dc']);
|
||||
}
|
||||
|
||||
public function dc_disconnect($dc_number)
|
||||
{
|
||||
if (isset($this->sockets[$dc_number])) {
|
||||
\danog\MadelineProto\Logging::log('Disconnecting from DC '.$dc_number.'...');
|
||||
\danog\MadelineProto\Logger::log('Disconnecting from DC '.$dc_number.'...');
|
||||
unset($this->sockets[$dc_number]);
|
||||
unset($this->curdc);
|
||||
}
|
||||
@ -51,9 +52,9 @@ class DataCenter extends Tools
|
||||
public function dc_connect($dc_number, $settings = [])
|
||||
{
|
||||
if (isset($this->sockets[$dc_number])) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
\danog\MadelineProto\Logging::log('Connecting to DC '.$dc_number.'...');
|
||||
\danog\MadelineProto\Logger::log('Connecting to DC '.$dc_number.'...');
|
||||
|
||||
if ($settings == []) {
|
||||
$settings = $this->settings[$dc_number];
|
||||
@ -65,47 +66,32 @@ class DataCenter extends Tools
|
||||
$address = 'https://'.$subdomain.'.web.telegram.org/'.$path;
|
||||
}
|
||||
$this->sockets[$dc_number] = new Connection($address, $settings['port'], $settings['protocol']);
|
||||
$this->set_curdc($dc_number);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function set_curdc($dc_number) {
|
||||
$this->curdc = $dc_number;
|
||||
foreach ($referenced_variables as $key) {
|
||||
$this->{$key} = &$this->sockets[$dc_number]->{$key};
|
||||
}
|
||||
}
|
||||
public function unset_curdc($dc_number) {
|
||||
unset($this->curdc);
|
||||
foreach ($referenced_variables as $key) {
|
||||
unset($this->sockets[$dc_number]->{$key});
|
||||
}
|
||||
}
|
||||
|
||||
public function set_time_delta($delta, $dc_number = -1)
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
if ($dc_number == -1) {
|
||||
$dc_number = $this->curdc;
|
||||
}
|
||||
|
||||
return $this->sockets[$dc_number]->set_time_delta($delta);
|
||||
}
|
||||
|
||||
public function get_time_delta($dc_number = -1)
|
||||
{
|
||||
if ($dc_number == -1) {
|
||||
$dc_number = $this->curdc;
|
||||
}
|
||||
|
||||
return $this->sockets[$dc_number]->get_time_delta();
|
||||
}
|
||||
|
||||
public function send_message($message, $dc_number = -1)
|
||||
{
|
||||
if ($dc_number == -1) {
|
||||
$dc_number = $this->curdc;
|
||||
}
|
||||
|
||||
return $this->sockets[$dc_number]->send_message($message);
|
||||
}
|
||||
|
||||
public function read_message($dc_number = -1)
|
||||
{
|
||||
if ($dc_number == -1) {
|
||||
$dc_number = $this->curdc;
|
||||
}
|
||||
|
||||
return $this->sockets[$dc_number]->read_message();
|
||||
return $this->sockets[$this->curdc]->{$name}(...$arguments);
|
||||
}
|
||||
|
||||
public function __destroy()
|
||||
{
|
||||
$this->unset_curdc();
|
||||
foreach ($this->sockets as $n => $socket) {
|
||||
unset($this->sockets[$n]);
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ You should have received a copy of the GNU General Public License along with the
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
* Logging class
|
||||
* Logger class
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto;
|
||||
|
||||
class Logging
|
||||
class Logger
|
||||
{
|
||||
public static $mode = null;
|
||||
public static $optional = null;
|
||||
@ -23,9 +23,9 @@ class Logging
|
||||
|
||||
/*
|
||||
* Constructor function
|
||||
* Accepts various logging modes:
|
||||
* 0 - No logging
|
||||
* 1 - Log to the default logging destination
|
||||
* Accepts various logger modes:
|
||||
* 0 - No logger
|
||||
* 1 - Log to the default logger destination
|
||||
* 2 - Log to file defined in second parameter
|
||||
* 3 - Echo logs
|
||||
*/
|
@ -24,10 +24,7 @@ class MTProto extends MTProtoTools
|
||||
// Set default settings
|
||||
$default_settings = [
|
||||
'authorization' => [
|
||||
'auth_key' => null,
|
||||
'temp_auth_key' => null,
|
||||
'default_temp_auth_key_expires_in' => 86400,
|
||||
'session_id' => \phpseclib\Crypt\Random::string(8),
|
||||
'rsa_key' => '-----BEGIN RSA PUBLIC KEY-----
|
||||
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
|
||||
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
|
||||
@ -61,7 +58,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
'connection_settings' => [
|
||||
'all' => [
|
||||
'protocol' => 'tcp_full',
|
||||
'test_mode' => true,
|
||||
'test_mode' => false,
|
||||
'port' => '443',
|
||||
],
|
||||
'default_dc' => 2,
|
||||
@ -81,10 +78,10 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
__DIR__.'/TL_telegram_v55.json',
|
||||
],
|
||||
],
|
||||
'logging' => [
|
||||
'logging' => 1,
|
||||
'logging_param' => '/tmp/MadelineProto.log',
|
||||
'logging' => 3,
|
||||
'logger' => [
|
||||
'logger' => 1,
|
||||
'logger_param' => '/tmp/MadelineProto.log',
|
||||
'logger' => 3,
|
||||
],
|
||||
'max_tries' => [
|
||||
'query' => 5,
|
||||
@ -107,42 +104,67 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
}
|
||||
}
|
||||
$this->settings = $settings;
|
||||
// Set up logging class
|
||||
\danog\MadelineProto\Logging::constructor($this->settings['logging']['logging'], $this->settings['logging']['logging_param']);
|
||||
|
||||
// Setup logger
|
||||
$this->setup_logger();
|
||||
|
||||
// Connect to servers
|
||||
\danog\MadelineProto\Logging::log('Connecting to server...');
|
||||
$this->connection = new DataCenter($this->settings['connection'], $this->settings['connection_settings']);
|
||||
\danog\MadelineProto\Logger::log('Istantiating DataCenter...');
|
||||
$this->datacenter = new DataCenter($this->settings['connection'], $this->settings['connection_settings']);
|
||||
|
||||
// Load rsa key
|
||||
\danog\MadelineProto\Logging::log('Loading RSA key...');
|
||||
\danog\MadelineProto\Logger::log('Loading RSA key...');
|
||||
$this->key = new RSA($settings['authorization']['rsa_key']);
|
||||
|
||||
// Istantiate TL class
|
||||
\danog\MadelineProto\Logging::log('Translating tl schemas...');
|
||||
\danog\MadelineProto\Logger::log('Translating tl schemas...');
|
||||
$this->tl = new TL\TL($this->settings['tl_schema']['src']);
|
||||
|
||||
$this->seq_no = 0;
|
||||
$this->incoming_messages = [];
|
||||
$this->outgoing_messages = [];
|
||||
$this->future_salts = [];
|
||||
|
||||
if ($this->settings['authorization']['temp_auth_key'] == null || $this->settings['authorization']['auth_key'] == null) {
|
||||
if ($this->settings['authorization']['auth_key'] == null) {
|
||||
\danog\MadelineProto\Logging::log('Generating permanent authorization key...');
|
||||
$this->settings['authorization']['auth_key'] = $this->create_auth_key(-1);
|
||||
}
|
||||
\danog\MadelineProto\Logging::log('Generating temporary authorization key...');
|
||||
$this->settings['authorization']['temp_auth_key'] = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']);
|
||||
}
|
||||
$this->write_client_info();
|
||||
$this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']);
|
||||
\danog\MadelineProto\Logging::log('You may now login to Telegram.');
|
||||
$this->switch_dc($this->settings['connection_settings']['default_dc'], true);
|
||||
|
||||
}
|
||||
|
||||
public function write_client_info()
|
||||
public function setup_logger()
|
||||
{
|
||||
\danog\MadelineProto\Logging::log('Writing client info...');
|
||||
if (!\danog\MadelineProto\Logger::$constructed) {
|
||||
// Set up logger class
|
||||
\danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param']);
|
||||
}
|
||||
}
|
||||
|
||||
// Switches to a new datacenter and if necessary creates authorization keys, binds them and writes client info
|
||||
public function switch_dc($new_dc, $allow_nearestdc_switch = false)
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Switching to DC '.$new_dc.'...');
|
||||
if ($this->datacenter->dc_connect($new_dc)) {
|
||||
$this->init_authorization();
|
||||
$this->write_client_info();
|
||||
$this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $allow_nearestdc_switch);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates authorization keys
|
||||
public function init_authorization()
|
||||
{
|
||||
if ($this->datacenter->session_id == null) {
|
||||
$this->datacenter->session_id = \phpseclib\Crypt\Random::string(8);
|
||||
}
|
||||
if ($this->datacenter->temp_auth_key == null || $this->datacenter->auth_key == null) {
|
||||
if ($this->datacenter->auth_key == null) {
|
||||
\danog\MadelineProto\Logger::log('Generating permanent authorization key...');
|
||||
$this->datacenter->auth_key = $this->create_auth_key(-1);
|
||||
}
|
||||
\danog\MadelineProto\Logger::log('Generating temporary authorization key...');
|
||||
$this->datacenter->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']);
|
||||
}
|
||||
}
|
||||
public function write_client_info($allow_switch) {
|
||||
|
||||
\danog\MadelineProto\Logger::log('Writing client info...');
|
||||
$nearestDc = $this->method_call(
|
||||
'invokeWithLayer',
|
||||
[
|
||||
@ -155,17 +177,17 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
),
|
||||
]
|
||||
);
|
||||
\danog\MadelineProto\Logging::log('Current dc is '.$nearestDc['this_dc'].', nearest dc is '.$nearestDc['nearest_dc'].' in '.$nearestDc['country'].'.');
|
||||
\danog\MadelineProto\Logger::log('Current dc is '.$nearestDc['this_dc'].', nearest dc is '.$nearestDc['nearest_dc'].' in '.$nearestDc['country'].'.');
|
||||
|
||||
if ($nearestDc['nearest_dc'] != $nearestDc['this_dc']) {
|
||||
\danog\MadelineProto\Logging::log('Switching to dc '.$nearestDc['nearest_dc'].'...');
|
||||
$this->connection->dc_connect($nearestDc['nearest_dc']);
|
||||
if ($nearestDc['nearest_dc'] != $nearestDc['this_dc'] && $allow_switch) {
|
||||
$this->switch_dc($nearestDc['nearest_dc']);
|
||||
$this->settings['connection_settings']['default_dc'] = $nearestDc['nearest_dc'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->sock);
|
||||
unset($this->datacenter);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class AckHandler extends \danog\MadelineProto\PrimeModule
|
||||
|
||||
public function ack_incoming_message_id($message_id)
|
||||
{
|
||||
if ($this->settings['authorization']['temp_auth_key']['id'] === null || $this->settings['authorization']['temp_auth_key']['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
|
||||
if ($this->datacenter->temp_auth_key['id'] === null || $this->datacenter->temp_auth_key['id'] == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
|
||||
return;
|
||||
}
|
||||
// I let the server know that I received its message
|
||||
|
@ -23,435 +23,439 @@ class AuthKeyHandler extends AckHandler
|
||||
public function create_auth_key($expires_in = -1)
|
||||
{
|
||||
foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id_total) {
|
||||
\danog\MadelineProto\Logging::log('Requesting pq');
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log('Requesting pq');
|
||||
|
||||
/**
|
||||
* ***********************************************************************
|
||||
* Make pq request, DH exchange initiation.
|
||||
*
|
||||
* @method req_pq
|
||||
*
|
||||
* @param [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* ]
|
||||
*
|
||||
* @return ResPQ [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the server
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $pq : This is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers
|
||||
* Vector long $server_public_key_fingerprints : This is a list of public RSA key fingerprints
|
||||
* ]
|
||||
*/
|
||||
$nonce = \phpseclib\Crypt\Random::string(16);
|
||||
$ResPQ = $this->method_call('req_pq',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
*/
|
||||
if ($ResPQ['nonce'] !== $nonce) {
|
||||
throw new Exception('wrong nonce');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Find our key in the server_public_key_fingerprints vector
|
||||
*/
|
||||
foreach ($ResPQ['server_public_key_fingerprints'] as $curfp) {
|
||||
$curfp_biginteger = new \phpseclib\Math\BigInteger($curfp);
|
||||
|
||||
if ($this->key->fp->equals($curfp_biginteger)) {
|
||||
$public_key_fingerprint = $curfp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($public_key_fingerprint)) {
|
||||
throw new Exception("Couldn't find our key in the server_public_key_fingerprints vector.");
|
||||
}
|
||||
|
||||
$pq_bytes = $ResPQ['pq'];
|
||||
$server_nonce = $ResPQ['server_nonce'];
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Compute p and q
|
||||
*/
|
||||
$pq = new \phpseclib\Math\BigInteger($pq_bytes, 256);
|
||||
list($p, $q) = $this->PrimeFactors($pq);
|
||||
$p = new \phpseclib\Math\BigInteger($p);
|
||||
$q = new \phpseclib\Math\BigInteger($q);
|
||||
|
||||
if ($p->compare($q) > 0) {
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
|
||||
if (!($pq->equals($p->multiply($q)) && $p->compare($q) < 0)) {
|
||||
throw new Exception("couldn't compute p and q.");
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logging::log('Factorization '.$pq.' = '.$p.' * '.$q);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Serialize object for req_DH_params
|
||||
*/
|
||||
$p_bytes = \danog\PHP\Struct::pack('>I', (string) $p);
|
||||
$q_bytes = \danog\PHP\Struct::pack('>I', (string) $q);
|
||||
|
||||
$new_nonce = \phpseclib\Crypt\Random::string(32);
|
||||
if ($expires_in < 0) {
|
||||
$data = $this->tl->serialize_obj('p_q_inner_data',
|
||||
/**
|
||||
* ***********************************************************************
|
||||
* Make pq request, DH exchange initiation.
|
||||
*
|
||||
* @method req_pq
|
||||
*
|
||||
* @param [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* ]
|
||||
*
|
||||
* @return ResPQ [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the server
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $pq : This is a representation of a natural number (in binary big endian format). This number is the product of two different odd prime numbers
|
||||
* Vector long $server_public_key_fingerprints : This is a list of public RSA key fingerprints
|
||||
* ]
|
||||
*/
|
||||
$nonce = \phpseclib\Crypt\Random::string(16);
|
||||
$ResPQ = $this->method_call('req_pq',
|
||||
[
|
||||
'pq' => $pq_bytes,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'new_nonce' => $new_nonce,
|
||||
'nonce' => $nonce,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$data = $this->tl->serialize_obj('p_q_inner_data_temp',
|
||||
[
|
||||
'pq' => $pq_bytes,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'new_nonce' => $new_nonce,
|
||||
'expires_in' => $expires_in,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Encrypt serialized object
|
||||
*/
|
||||
$sha_digest = sha1($data, true);
|
||||
$random_bytes = \phpseclib\Crypt\Random::string(255 - strlen($data) - strlen($sha_digest));
|
||||
$to_encrypt = $sha_digest.$data.$random_bytes;
|
||||
$encrypted_data = $this->key->encrypt($to_encrypt);
|
||||
|
||||
\danog\MadelineProto\Logging::log('Starting Diffie Hellman key exchange');
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Starting Diffie Hellman key exchange, Server authentication
|
||||
* @method req_DH_params
|
||||
* @param [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $p : The value of BigInteger
|
||||
* string $q : The value of BigInteger
|
||||
* long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector
|
||||
* string $encrypted_data
|
||||
* ]
|
||||
* @return Server_DH_Params [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the server
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $new_nonce_hash : Return this value If server responds is server_DH_params_fail
|
||||
* string $encrypted_answer : Return this value If server responds is server_DH_params_ok
|
||||
* ]
|
||||
*/
|
||||
//
|
||||
$server_dh_params = $this->method_call('req_DH_params',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'public_key_fingerprint' => $public_key_fingerprint,
|
||||
'encrypted_data' => $encrypted_data,
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
*/
|
||||
if ($nonce != $server_dh_params['nonce']) {
|
||||
throw new Exception('wrong nonce.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if server_nonce and new server_nonce are the same
|
||||
*/
|
||||
if ($server_nonce != $server_dh_params['server_nonce']) {
|
||||
throw new Exception('wrong server nonce.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* 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']) {
|
||||
throw new Exception('wrong new nonce hash.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* 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);
|
||||
$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);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Deserialize answer
|
||||
* @return Server_DH_inner_data [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* int $g
|
||||
* string $dh_prime
|
||||
* string $g_a
|
||||
* int $server_time
|
||||
* ]
|
||||
*/
|
||||
$server_DH_inner_data = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $answer));
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Do some checks
|
||||
*/
|
||||
$server_DH_inner_data_length = $this->tl->get_length($this->fopen_and_write('php://memory', 'rw+b', $answer));
|
||||
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
|
||||
throw new Exception('answer_hash mismatch.');
|
||||
}
|
||||
|
||||
if ($nonce != $server_DH_inner_data['nonce']) {
|
||||
throw new Exception('wrong nonce');
|
||||
}
|
||||
|
||||
if ($server_nonce != $server_DH_inner_data['server_nonce']) {
|
||||
throw new Exception('wrong server nonce');
|
||||
}
|
||||
|
||||
$g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']);
|
||||
$g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256);
|
||||
$dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Time delta
|
||||
*/
|
||||
$server_time = $server_DH_inner_data['server_time'];
|
||||
$this->connection->set_time_delta($server_time - time());
|
||||
|
||||
\danog\MadelineProto\Logging::log(sprintf('Server-client time delta = %.1f s', $this->connection->get_time_delta()));
|
||||
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Define some needed numbers for BigInteger
|
||||
*/
|
||||
\danog\MadelineProto\Logging::log('Executing dh_prime checks...');
|
||||
$one = new \phpseclib\Math\BigInteger(1);
|
||||
$two = new \phpseclib\Math\BigInteger(2);
|
||||
$twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328');
|
||||
$twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656');
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of dh_prime
|
||||
* 2^2047 < dh_prime < 2^2048
|
||||
*/
|
||||
if (!$dh_prime->isPrime()) {
|
||||
throw new Exception("dh_prime isn't a safe 2048-bit prime (dh_prime isn't a prime).");
|
||||
}
|
||||
|
||||
/*
|
||||
// Almost always fails
|
||||
if (!$dh_prime->subtract($one)->divide($two)[0]->isPrime()) {
|
||||
throw new Exception("dh_prime isn't a safe 2048-bit prime ((dh_prime - 1) / 2 isn't a prime).");
|
||||
}
|
||||
*/
|
||||
|
||||
if ($dh_prime->compare($twoe2047) <= 0 // 2^2047 < dh_prime or dh_prime > 2^2047 or ! dh_prime <= 2^2047
|
||||
|| $dh_prime->compare($twoe2048) >= 0 // dh_prime < 2^2048 or ! dh_prime >= 2^2048
|
||||
) {
|
||||
throw new Exception("g isn't a safe 2048-bit prime (2^2047 < dh_prime < 2^2048 is false).");
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of g
|
||||
* 1 < g < dh_prime - 1
|
||||
*/
|
||||
if ($g->compare($one) <= 0 // 1 < g or g > 1 or ! g <= 1
|
||||
|| $g->compare($dh_prime->subtract($one)) >= 0 // g < dh_prime - 1 or ! g >= dh_prime - 1
|
||||
) {
|
||||
throw new Exception('g is invalid (1 < g < dh_prime - 1 is false).');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of g_a
|
||||
* 1 < g_a < dh_prime - 1
|
||||
*/
|
||||
if ($g_a->compare($one) <= 0 // 1 < g_a or g_a > 1 or ! g_a <= 1
|
||||
|| $g_a->compare($dh_prime->subtract($one)) >= 0 // g_a < dh_prime - 1 or ! g_a >= dh_prime - 1
|
||||
) {
|
||||
throw new Exception('g_a is invalid (1 < g_a < dh_prime - 1 is false).');
|
||||
}
|
||||
|
||||
foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) {
|
||||
$b = new \phpseclib\Math\BigInteger(\phpseclib\Crypt\Random::string(256), 256);
|
||||
$g_b = $g->powMod($b, $dh_prime);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of g_b
|
||||
* 1 < g_b < dh_prime - 1
|
||||
*/
|
||||
if ($g_b->compare($one) <= 0 // 1 < g_b or g_b > 1 or ! g_b <= 1
|
||||
|| $g_b->compare($dh_prime->subtract($one)) >= 0 // g_b < dh_prime - 1 or ! g_b >= dh_prime - 1
|
||||
) {
|
||||
throw new Exception('g_b is invalid (1 < g_b < dh_prime - 1 is false).');
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
*/
|
||||
if ($ResPQ['nonce'] !== $nonce) {
|
||||
throw new Exception('wrong nonce');
|
||||
}
|
||||
|
||||
$g_b_str = $g_b->toBytes();
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Find our key in the server_public_key_fingerprints vector
|
||||
*/
|
||||
foreach ($ResPQ['server_public_key_fingerprints'] as $curfp) {
|
||||
$curfp_biginteger = new \phpseclib\Math\BigInteger($curfp);
|
||||
|
||||
if ($this->key->fp->equals($curfp_biginteger)) {
|
||||
$public_key_fingerprint = $curfp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($public_key_fingerprint)) {
|
||||
throw new Exception("Couldn't find our key in the server_public_key_fingerprints vector.");
|
||||
}
|
||||
|
||||
$pq_bytes = $ResPQ['pq'];
|
||||
$server_nonce = $ResPQ['server_nonce'];
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* serialize client_DH_inner_data
|
||||
* @method client_DH_inner_data
|
||||
* @param Server_DH_inner_data [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* long $retry_id : First attempt
|
||||
* string $g_b : g^b mod dh_prime
|
||||
* ]
|
||||
*/
|
||||
$data = $this->tl->serialize_obj('client_DH_inner_data',
|
||||
* ***********************************************************************
|
||||
* Compute p and q
|
||||
*/
|
||||
$pq = new \phpseclib\Math\BigInteger($pq_bytes, 256);
|
||||
list($p, $q) = $this->PrimeFactors($pq);
|
||||
$p = new \phpseclib\Math\BigInteger($p);
|
||||
$q = new \phpseclib\Math\BigInteger($q);
|
||||
|
||||
if ($p->compare($q) > 0) {
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
|
||||
if (!($pq->equals($p->multiply($q)) && $p->compare($q) < 0)) {
|
||||
throw new Exception("couldn't compute p and q.");
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Factorization '.$pq.' = '.$p.' * '.$q);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Serialize object for req_DH_params
|
||||
*/
|
||||
$p_bytes = \danog\PHP\Struct::pack('>I', (string) $p);
|
||||
$q_bytes = \danog\PHP\Struct::pack('>I', (string) $q);
|
||||
|
||||
$new_nonce = \phpseclib\Crypt\Random::string(32);
|
||||
if ($expires_in < 0) {
|
||||
$data = $this->tl->serialize_obj('p_q_inner_data',
|
||||
[
|
||||
'pq' => $pq_bytes,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'new_nonce' => $new_nonce,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$data = $this->tl->serialize_obj('p_q_inner_data_temp',
|
||||
[
|
||||
'pq' => $pq_bytes,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'new_nonce' => $new_nonce,
|
||||
'expires_in' => $expires_in,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Encrypt serialized object
|
||||
*/
|
||||
$sha_digest = sha1($data, true);
|
||||
$random_bytes = \phpseclib\Crypt\Random::string(255 - strlen($data) - strlen($sha_digest));
|
||||
$to_encrypt = $sha_digest.$data.$random_bytes;
|
||||
$encrypted_data = $this->key->encrypt($to_encrypt);
|
||||
|
||||
\danog\MadelineProto\Logger::log('Starting Diffie Hellman key exchange');
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Starting Diffie Hellman key exchange, Server authentication
|
||||
* @method req_DH_params
|
||||
* @param [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $p : The value of BigInteger
|
||||
* string $q : The value of BigInteger
|
||||
* long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector
|
||||
* string $encrypted_data
|
||||
* ]
|
||||
* @return Server_DH_Params [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the server
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $new_nonce_hash : Return this value If server responds is server_DH_params_fail
|
||||
* string $encrypted_answer : Return this value If server responds is server_DH_params_ok
|
||||
* ]
|
||||
*/
|
||||
//
|
||||
$server_dh_params = $this->method_call('req_DH_params',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'retry_id' => $retry_id,
|
||||
'g_b' => $g_b_str,
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'public_key_fingerprint' => $public_key_fingerprint,
|
||||
'encrypted_data' => $encrypted_data,
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* encrypt client_DH_inner_data
|
||||
*/
|
||||
$data_with_sha = sha1($data, true).$data;
|
||||
$data_with_sha_padded = $data_with_sha.\phpseclib\Crypt\Random::string($this->posmod(-strlen($data_with_sha), 16));
|
||||
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Send set_client_DH_params query
|
||||
* @method set_client_DH_params
|
||||
* @param Server_DH_inner_data [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $encrypted_data
|
||||
* ]
|
||||
* @return Set_client_DH_params_answer [
|
||||
* string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* int128 $new_nonce_hash1 : Return this value If server responds is dh_gen_ok
|
||||
* int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_retry
|
||||
* int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_fail
|
||||
* ]
|
||||
*/
|
||||
$Set_client_DH_params_answer = $this->method_call('set_client_DH_params',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'encrypted_data' => $encrypted_data,
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Generate auth_key
|
||||
*/
|
||||
$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);
|
||||
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
*/
|
||||
if ($Set_client_DH_params_answer['nonce'] != $nonce) {
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
*/
|
||||
if ($nonce != $server_dh_params['nonce']) {
|
||||
throw new Exception('wrong nonce.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if server_nonce and new server_nonce are the same
|
||||
*/
|
||||
if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) {
|
||||
throw new Exception('wrong server nonce');
|
||||
* ***********************************************************************
|
||||
* Check if server_nonce and new server_nonce are the same
|
||||
*/
|
||||
if ($server_nonce != $server_dh_params['server_nonce']) {
|
||||
throw new Exception('wrong server nonce.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check Set_client_DH_params_answer type
|
||||
*/
|
||||
switch ($Set_client_DH_params_answer['_']) {
|
||||
case 'dh_gen_ok':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) {
|
||||
throw new Exception('wrong new_nonce_hash1');
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logging::log('Diffie Hellman key exchange processed successfully');
|
||||
|
||||
$res_authorization['server_salt'] = \danog\PHP\Struct::unpack('<q', substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0))[0];
|
||||
$res_authorization['auth_key'] = $auth_key_str;
|
||||
$res_authorization['id'] = substr($auth_key_sha, -8);
|
||||
|
||||
if ($expires_in < 0) { //check if permanent authorization
|
||||
$res_authorization['expires_in'] = $expires_in;
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logging::log('Auth key generated');
|
||||
|
||||
return $res_authorization;
|
||||
case 'dh_gen_retry':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
|
||||
throw new Exception('wrong new_nonce_hash_2');
|
||||
}
|
||||
|
||||
//repeat foreach
|
||||
\danog\MadelineProto\Logging::log('Retrying Auth');
|
||||
break;
|
||||
case 'dh_gen_fail':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash3'] != $new_nonce_hash3) {
|
||||
throw new Exception('wrong new_nonce_hash_3');
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logging::log('Auth Failed');
|
||||
break 2;
|
||||
default:
|
||||
throw new Exception('Response Error');
|
||||
break;
|
||||
* ***********************************************************************
|
||||
* 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']) {
|
||||
throw new Exception('wrong new nonce hash.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* 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);
|
||||
$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);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Deserialize answer
|
||||
* @return Server_DH_inner_data [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* int $g
|
||||
* string $dh_prime
|
||||
* string $g_a
|
||||
* int $server_time
|
||||
* ]
|
||||
*/
|
||||
$server_DH_inner_data = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $answer));
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Do some checks
|
||||
*/
|
||||
$server_DH_inner_data_length = $this->tl->get_length($this->fopen_and_write('php://memory', 'rw+b', $answer));
|
||||
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
|
||||
throw new Exception('answer_hash mismatch.');
|
||||
}
|
||||
|
||||
if ($nonce != $server_DH_inner_data['nonce']) {
|
||||
throw new Exception('wrong nonce');
|
||||
}
|
||||
|
||||
if ($server_nonce != $server_DH_inner_data['server_nonce']) {
|
||||
throw new Exception('wrong server nonce');
|
||||
}
|
||||
|
||||
$g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']);
|
||||
$g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256);
|
||||
$dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Time delta
|
||||
*/
|
||||
$server_time = $server_DH_inner_data['server_time'];
|
||||
$this->datacenter->set_time_delta($server_time - time());
|
||||
|
||||
\danog\MadelineProto\Logger::log(sprintf('Server-client time delta = %.1f s', $this->datacenter->get_time_delta()));
|
||||
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Define some needed numbers for BigInteger
|
||||
*/
|
||||
\danog\MadelineProto\Logger::log('Executing dh_prime checks...');
|
||||
$one = new \phpseclib\Math\BigInteger(1);
|
||||
$two = new \phpseclib\Math\BigInteger(2);
|
||||
$twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328');
|
||||
$twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656');
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of dh_prime
|
||||
* 2^2047 < dh_prime < 2^2048
|
||||
*/
|
||||
if (!$dh_prime->isPrime()) {
|
||||
throw new Exception("dh_prime isn't a safe 2048-bit prime (dh_prime isn't a prime).");
|
||||
}
|
||||
|
||||
/*
|
||||
// Almost always fails
|
||||
if (!$dh_prime->subtract($one)->divide($two)[0]->isPrime()) {
|
||||
throw new Exception("dh_prime isn't a safe 2048-bit prime ((dh_prime - 1) / 2 isn't a prime).");
|
||||
}
|
||||
*/
|
||||
|
||||
if ($dh_prime->compare($twoe2047) <= 0 // 2^2047 < dh_prime or dh_prime > 2^2047 or ! dh_prime <= 2^2047
|
||||
|| $dh_prime->compare($twoe2048) >= 0 // dh_prime < 2^2048 or ! dh_prime >= 2^2048
|
||||
) {
|
||||
throw new Exception("g isn't a safe 2048-bit prime (2^2047 < dh_prime < 2^2048 is false).");
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of g
|
||||
* 1 < g < dh_prime - 1
|
||||
*/
|
||||
if ($g->compare($one) <= 0 // 1 < g or g > 1 or ! g <= 1
|
||||
|| $g->compare($dh_prime->subtract($one)) >= 0 // g < dh_prime - 1 or ! g >= dh_prime - 1
|
||||
) {
|
||||
throw new Exception('g is invalid (1 < g < dh_prime - 1 is false).');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of g_a
|
||||
* 1 < g_a < dh_prime - 1
|
||||
*/
|
||||
if ($g_a->compare($one) <= 0 // 1 < g_a or g_a > 1 or ! g_a <= 1
|
||||
|| $g_a->compare($dh_prime->subtract($one)) >= 0 // g_a < dh_prime - 1 or ! g_a >= dh_prime - 1
|
||||
) {
|
||||
throw new Exception('g_a is invalid (1 < g_a < dh_prime - 1 is false).');
|
||||
}
|
||||
|
||||
foreach ($this->range(0, $this->settings['max_tries']['authorization']) as $retry_id) {
|
||||
$b = new \phpseclib\Math\BigInteger(\phpseclib\Crypt\Random::string(256), 256);
|
||||
$g_b = $g->powMod($b, $dh_prime);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check validity of g_b
|
||||
* 1 < g_b < dh_prime - 1
|
||||
*/
|
||||
if ($g_b->compare($one) <= 0 // 1 < g_b or g_b > 1 or ! g_b <= 1
|
||||
|| $g_b->compare($dh_prime->subtract($one)) >= 0 // g_b < dh_prime - 1 or ! g_b >= dh_prime - 1
|
||||
) {
|
||||
throw new Exception('g_b is invalid (1 < g_b < dh_prime - 1 is false).');
|
||||
}
|
||||
|
||||
$g_b_str = $g_b->toBytes();
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* serialize client_DH_inner_data
|
||||
* @method client_DH_inner_data
|
||||
* @param Server_DH_inner_data [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* long $retry_id : First attempt
|
||||
* string $g_b : g^b mod dh_prime
|
||||
* ]
|
||||
*/
|
||||
$data = $this->tl->serialize_obj('client_DH_inner_data',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'retry_id' => $retry_id,
|
||||
'g_b' => $g_b_str,
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* encrypt client_DH_inner_data
|
||||
*/
|
||||
$data_with_sha = sha1($data, true).$data;
|
||||
$data_with_sha_padded = $data_with_sha.\phpseclib\Crypt\Random::string($this->posmod(-strlen($data_with_sha), 16));
|
||||
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Send set_client_DH_params query
|
||||
* @method set_client_DH_params
|
||||
* @param Server_DH_inner_data [
|
||||
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* string $encrypted_data
|
||||
* ]
|
||||
* @return Set_client_DH_params_answer [
|
||||
* string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail
|
||||
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
|
||||
* int128 $new_nonce_hash1 : Return this value If server responds is dh_gen_ok
|
||||
* int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_retry
|
||||
* int128 $new_nonce_hash2 : Return this value If server responds is dh_gen_fail
|
||||
* ]
|
||||
*/
|
||||
$Set_client_DH_params_answer = $this->method_call('set_client_DH_params',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'encrypted_data' => $encrypted_data,
|
||||
]
|
||||
);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Generate auth_key
|
||||
*/
|
||||
$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);
|
||||
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if the client's nonce and the server's nonce are the same
|
||||
*/
|
||||
if ($Set_client_DH_params_answer['nonce'] != $nonce) {
|
||||
throw new Exception('wrong nonce.');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check if server_nonce and new server_nonce are the same
|
||||
*/
|
||||
if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) {
|
||||
throw new Exception('wrong server nonce');
|
||||
}
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Check Set_client_DH_params_answer type
|
||||
*/
|
||||
switch ($Set_client_DH_params_answer['_']) {
|
||||
case 'dh_gen_ok':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) {
|
||||
throw new Exception('wrong new_nonce_hash1');
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Diffie Hellman key exchange processed successfully');
|
||||
|
||||
$res_authorization['server_salt'] = \danog\PHP\Struct::unpack('<q', substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0))[0];
|
||||
$res_authorization['auth_key'] = $auth_key_str;
|
||||
$res_authorization['id'] = substr($auth_key_sha, -8);
|
||||
|
||||
if ($expires_in < 0) { //check if permanent authorization
|
||||
$res_authorization['expires_in'] = $expires_in;
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Auth key generated');
|
||||
|
||||
return $res_authorization;
|
||||
case 'dh_gen_retry':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
|
||||
throw new Exception('wrong new_nonce_hash_2');
|
||||
}
|
||||
|
||||
//repeat foreach
|
||||
\danog\MadelineProto\Logger::log('Retrying Auth');
|
||||
break;
|
||||
case 'dh_gen_fail':
|
||||
if ($Set_client_DH_params_answer['new_nonce_hash3'] != $new_nonce_hash3) {
|
||||
throw new Exception('wrong new_nonce_hash_3');
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Auth Failed');
|
||||
break 2;
|
||||
default:
|
||||
throw new Exception('Response Error');
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
\danog\MadelineProto\Logger::log('An exception occurred while generating the authorization key. Retrying...');
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,12 +464,12 @@ class AuthKeyHandler extends AckHandler
|
||||
|
||||
public function bind_temp_auth_key($expires_in)
|
||||
{
|
||||
\danog\MadelineProto\Logging::log('Binding authorization keys...');
|
||||
\danog\MadelineProto\Logger::log('Binding authorization keys...');
|
||||
$nonce = \danog\PHP\Struct::unpack('<q', \phpseclib\Crypt\Random::string(8))[0];
|
||||
$expires_at = time() + $expires_in;
|
||||
$temp_auth_key_id = \danog\PHP\Struct::unpack('<q', $this->settings['authorization']['temp_auth_key']['id'])[0];
|
||||
$perm_auth_key_id = \danog\PHP\Struct::unpack('<q', $this->settings['authorization']['auth_key']['id'])[0];
|
||||
$temp_session_id = \danog\PHP\Struct::unpack('<q', $this->settings['authorization']['session_id'])[0];
|
||||
$temp_auth_key_id = \danog\PHP\Struct::unpack('<q', $this->datacenter->temp_auth_key['id'])[0];
|
||||
$perm_auth_key_id = \danog\PHP\Struct::unpack('<q', $this->datacenter->auth_key['id'])[0];
|
||||
$temp_session_id = \danog\PHP\Struct::unpack('<q', $this->datacenter->session_id)[0];
|
||||
$message_data = $this->tl->serialize_obj('bind_auth_key_inner',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
@ -476,18 +480,18 @@ class AuthKeyHandler extends AckHandler
|
||||
]
|
||||
);
|
||||
$int_message_id = $this->generate_message_id();
|
||||
$this->check_message_id($int_message_id, true);
|
||||
|
||||
$message_id = \danog\PHP\Struct::pack('<Q', $int_message_id);
|
||||
$seq_no = 0;
|
||||
$encrypted_data = \phpseclib\Crypt\Random::string(16).$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
|
||||
$message_key = substr(sha1($encrypted_data, true), -16);
|
||||
$padding = \phpseclib\Crypt\Random::string($this->posmod(-strlen($encrypted_data), 16));
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->settings['authorization']['auth_key']['auth_key']);
|
||||
$encrypted_message = $this->settings['authorization']['auth_key']['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->auth_key['auth_key']);
|
||||
$encrypted_message = $this->datacenter->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
|
||||
if ($this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], $int_message_id)) {
|
||||
\danog\MadelineProto\Logging::log('Successfully binded temporary and permanent authorization keys.');
|
||||
$this->write_client_info();
|
||||
|
||||
\danog\MadelineProto\Logger::log('Successfully binded temporary and permanent authorization keys.');
|
||||
return true;
|
||||
}
|
||||
throw new Exception('An error occurred while binding temporary and permanent authorization keys.');
|
||||
|
@ -25,7 +25,7 @@ class CallHandler extends AuthKeyHandler
|
||||
$response = null;
|
||||
$count = 0;
|
||||
while ($response == null && $count++ < $this->settings['max_tries']['response']) {
|
||||
\danog\MadelineProto\Logging::log('Getting response (try number '.$count.' for '.$optional_name.')...');
|
||||
\danog\MadelineProto\Logger::log('Getting response (try number '.$count.' for '.$optional_name.')...');
|
||||
$last_received = $this->recv_message();
|
||||
$this->handle_message($last_sent, $last_received);
|
||||
if (isset($this->outgoing_messages[$last_sent]['response']) && isset($this->incoming_messages[$this->outgoing_messages[$last_sent]['response']]['content'])) {
|
||||
@ -37,7 +37,17 @@ class CallHandler extends AuthKeyHandler
|
||||
}
|
||||
switch ($response['_']) {
|
||||
case 'rpc_error':
|
||||
throw new Exception('Got rpc error '.$response['error_code'].': '.$response['error_message']);
|
||||
switch ($response['error_code']) {
|
||||
case 303:
|
||||
$dc = preg_replace('/[^0-9]+/', '', $response['error_message']);
|
||||
$this->switch_dc($dc);
|
||||
return $this->method_call($this->outgoing_messages[$last_sent]['content']['method'], $this->outgoing_messages[$last_sent]['content']['args']);
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Got rpc error '.$response['error_code'].': '.$response['error_message']);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return $response;
|
||||
@ -47,6 +57,9 @@ class CallHandler extends AuthKeyHandler
|
||||
|
||||
public function method_call($method, $args, $message_id = null)
|
||||
{
|
||||
if (!is_array($args)) {
|
||||
throw new Exception("Arguments aren't an array.");
|
||||
}
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $i) {
|
||||
try {
|
||||
$args = $this->tl->get_named_method_args($method, $args);
|
||||
@ -54,9 +67,8 @@ class CallHandler extends AuthKeyHandler
|
||||
$this->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$server_answer = $this->wait_for_response($int_message_id, $method);
|
||||
} catch (Exception $e) {
|
||||
\danog\MadelineProto\Logging::log('An error occurred while calling method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Recreating connection and retrying to call method...');
|
||||
unset($this->connection);
|
||||
$this->connection = new \danog\MadelineProto\DataCenter($this->settings['connection'], $this->settings['connection_settings']);
|
||||
\danog\MadelineProto\Logger::log('An error occurred while calling method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Recreating connection and retrying to call method...');
|
||||
$this->datacenter->close_and_reopen();
|
||||
continue;
|
||||
}
|
||||
if ($server_answer == null) {
|
||||
@ -70,15 +82,18 @@ class CallHandler extends AuthKeyHandler
|
||||
|
||||
public function object_call($object, $args)
|
||||
{
|
||||
if (!is_array($args)) {
|
||||
throw new Exception("Arguments aren't an array.");
|
||||
}
|
||||
|
||||
foreach (range(1, $this->settings['max_tries']['query']) as $i) {
|
||||
try {
|
||||
$int_message_id = $this->send_message($this->tl->serialize_obj($object, $args), $this->tl->content_related($object));
|
||||
$this->outgoing_messages[$int_message_id]['content'] = ['object' => $object, 'args' => $args];
|
||||
// $server_answer = $this->wait_for_response($int_message_id);
|
||||
} catch (Exception $e) {
|
||||
\danog\MadelineProto\Logging::log('An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...');
|
||||
unset($this->connection);
|
||||
$this->connection = new \danog\MadelineProto\DataCenter($this->settings['connection'], $this->settings['connection_settings']);
|
||||
\danog\MadelineProto\Logger::log('An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...');
|
||||
$this->datacenter->close_and_reopen();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -26,22 +26,21 @@ class MessageHandler extends Crypt
|
||||
if ($int_message_id == null) {
|
||||
$int_message_id = $this->generate_message_id();
|
||||
}
|
||||
if (!is_int($int_message_id)) {
|
||||
throw new Exception("Given message id isn't an integer!");
|
||||
}
|
||||
$this->check_message_id($int_message_id, true);
|
||||
|
||||
$message_id = \danog\PHP\Struct::pack('<Q', $int_message_id);
|
||||
if (($this->settings['authorization']['temp_auth_key']['auth_key'] == null) || ($this->settings['authorization']['temp_auth_key']['server_salt'] == null)) {
|
||||
if ($this->datacenter->temp_auth_key['auth_key'] == null || $this->datacenter->temp_auth_key['server_salt'] == null) {
|
||||
$message = $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
|
||||
} else {
|
||||
$seq_no = $this->generate_seq_no($content_related);
|
||||
$encrypted_data = \danog\PHP\Struct::pack('<q', $this->settings['authorization']['temp_auth_key']['server_salt']).$this->settings['authorization']['session_id'].$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
|
||||
$encrypted_data = \danog\PHP\Struct::pack('<q', $this->datacenter->temp_auth_key['server_salt']).$this->datacenter->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
|
||||
$message_key = substr(sha1($encrypted_data, true), -16);
|
||||
$padding = \phpseclib\Crypt\Random::string($this->posmod(-strlen($encrypted_data), 16));
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->settings['authorization']['temp_auth_key']['auth_key']);
|
||||
$message = $this->settings['authorization']['temp_auth_key']['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key']);
|
||||
$message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
$this->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
|
||||
}
|
||||
$this->connection->send_message($message);
|
||||
$this->datacenter->send_message($message);
|
||||
|
||||
return $int_message_id;
|
||||
}
|
||||
@ -51,7 +50,7 @@ class MessageHandler extends Crypt
|
||||
*/
|
||||
public function recv_message()
|
||||
{
|
||||
$payload = $this->connection->read_message();
|
||||
$payload = $this->datacenter->read_message();
|
||||
if (fstat($payload)['size'] == 4) {
|
||||
throw new Exception('Server response error: '.abs(\danog\PHP\Struct::unpack('<i', fread($payload, 4))[0]));
|
||||
}
|
||||
@ -60,19 +59,19 @@ class MessageHandler extends Crypt
|
||||
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', fread($payload, 12));
|
||||
$this->check_message_id($message_id, false);
|
||||
$message_data = fread($payload, $message_length);
|
||||
} elseif ($auth_key_id == $this->settings['authorization']['temp_auth_key']['id']) {
|
||||
} elseif ($auth_key_id == $this->datacenter->temp_auth_key['id']) {
|
||||
$message_key = fread($payload, 16);
|
||||
$encrypted_data = stream_get_contents($payload);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->settings['authorization']['temp_auth_key']['auth_key'], 'from server');
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server');
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
|
||||
$server_salt = \danog\PHP\Struct::unpack('<q', substr($decrypted_data, 0, 8))[0];
|
||||
if ($server_salt != $this->settings['authorization']['temp_auth_key']['server_salt']) {
|
||||
// throw new Exception('Server salt mismatch (my server salt '.$this->settings['authorization']['temp_auth_key']['server_salt'].' is not equal to server server salt '.$server_salt.').');
|
||||
if ($server_salt != $this->datacenter->temp_auth_key['server_salt']) {
|
||||
throw new Exception('Server salt mismatch (my server salt '.$this->datacenter->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').');
|
||||
}
|
||||
|
||||
$session_id = substr($decrypted_data, 8, 8);
|
||||
if ($session_id != $this->settings['authorization']['session_id']) {
|
||||
if ($session_id != $this->datacenter->session_id) {
|
||||
throw new Exception('Session id mismatch.');
|
||||
}
|
||||
|
||||
|
@ -19,10 +19,10 @@ class MsgIdHandler extends MessageHandler
|
||||
{
|
||||
public function check_message_id($new_message_id, $outgoing, $container = false)
|
||||
{
|
||||
if (((int) ((time() + $this->connection->get_time_delta() - 300) * pow(2, 30)) * 4) > $new_message_id) {
|
||||
if (((int) ((time() + $this->datacenter->get_time_delta() - 300) * pow(2, 30)) * 4) > $new_message_id) {
|
||||
throw new Exception('Given message id ('.$new_message_id.') is too old.');
|
||||
}
|
||||
if (((int) ((time() + $this->connection->get_time_delta() + 30) * pow(2, 30)) * 4) < $new_message_id) {
|
||||
if (((int) ((time() + $this->datacenter->get_time_delta() + 30) * pow(2, 30)) * 4) < $new_message_id) {
|
||||
throw new Exception('Given message id ('.$new_message_id.') is too new.');
|
||||
}
|
||||
if ($outgoing) {
|
||||
@ -66,7 +66,7 @@ class MsgIdHandler extends MessageHandler
|
||||
|
||||
public function generate_message_id()
|
||||
{
|
||||
$int_message_id = (int) ((time() + $this->connection->get_time_delta()) << 32);
|
||||
$int_message_id = (int) ((time() + $this->datacenter->get_time_delta()) << 32);
|
||||
/* $int_message_id = (int) (
|
||||
((int) ($ms_time / 1000) << 32) |
|
||||
($this->posmod($ms_time, 1000) << 22) |
|
||||
@ -78,7 +78,6 @@ class MsgIdHandler extends MessageHandler
|
||||
if ($int_message_id <= $keys) {
|
||||
$int_message_id = $keys + 4;
|
||||
}
|
||||
$this->check_message_id($int_message_id, true);
|
||||
|
||||
return $int_message_id;
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class ResponseHandler extends MsgIdHandler
|
||||
throw new Exception('Received bad_msg_notification for '.$response['bad_msg_id'].': '.$error_codes[$response['error_code']]);
|
||||
break;
|
||||
case 'bad_server_salt':
|
||||
$this->settings['authorization']['temp_auth_key']['server_salt'] = $response['new_server_salt'];
|
||||
$this->datacenter->temp_auth_key['server_salt'] = $response['new_server_salt'];
|
||||
$this->ack_outgoing_message_id($response['bad_msg_id']); // Acknowledge that the server received my request
|
||||
$this->outgoing_messages[$response['bad_msg_id']]['response'] = $last_received;
|
||||
$this->incoming_messages[$last_received]['content'] = $response;
|
||||
@ -73,15 +73,15 @@ class ResponseHandler extends MsgIdHandler
|
||||
}
|
||||
break;
|
||||
case 'new_session_created':
|
||||
$this->settings['authorization']['temp_auth_key']['server_salt'] = $response['server_salt'];
|
||||
$this->datacenter->temp_auth_key['server_salt'] = $response['server_salt'];
|
||||
$this->ack_incoming_message_id($last_received); // Acknowledge that I received the server's response
|
||||
\danog\MadelineProto\Logging::log('new session created');
|
||||
\danog\MadelineProto\Logging::log($response);
|
||||
\danog\MadelineProto\Logger::log('new session created');
|
||||
\danog\MadelineProto\Logger::log($response);
|
||||
break;
|
||||
case 'msg_container':
|
||||
$responses = [];
|
||||
\danog\MadelineProto\Logging::log('Received container.');
|
||||
\danog\MadelineProto\Logging::log($response['messages']);
|
||||
\danog\MadelineProto\Logger::log('Received container.');
|
||||
\danog\MadelineProto\Logger::log($response['messages']);
|
||||
foreach ($response['messages'] as $message) {
|
||||
$this->check_message_id($message['msg_id'], false, true);
|
||||
$this->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']];
|
||||
@ -100,8 +100,8 @@ class ResponseHandler extends MsgIdHandler
|
||||
return end($responses);
|
||||
break;
|
||||
default:
|
||||
\danog\MadelineProto\Logging::log('Received multiple responses, returning last one');
|
||||
\danog\MadelineProto\Logging::log($responses);
|
||||
\danog\MadelineProto\Logger::log('Received multiple responses, returning last one');
|
||||
\danog\MadelineProto\Logger::log($responses);
|
||||
|
||||
return end($responses);
|
||||
break;
|
||||
@ -119,8 +119,8 @@ class ResponseHandler extends MsgIdHandler
|
||||
}
|
||||
break;
|
||||
case 'http_wait':
|
||||
\danog\MadelineProto\Logging::log('Received http wait.');
|
||||
\danog\MadelineProto\Logging::log($response);
|
||||
\danog\MadelineProto\Logger::log('Received http wait.');
|
||||
\danog\MadelineProto\Logger::log($response);
|
||||
break;
|
||||
case 'gzip_packed':
|
||||
$this->incoming_messages[$last_received]['content'] = gzdecode($response);
|
||||
|
@ -26,8 +26,8 @@ class SaltHandler extends ResponseHandler
|
||||
|
||||
public function addsalt($valid_since, $valid_until, $salt)
|
||||
{
|
||||
if (!isset($this->settings['authorization']['temp_auth_key']['salts'][$salt])) {
|
||||
$settings['authorization']['temp_auth_key']['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
|
||||
if (!isset($this->datacenter->temp_auth_key['salts'][$salt])) {
|
||||
$this->datacenter->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,34 @@ class RSA extends TL\TL
|
||||
|
||||
public function __construct($key)
|
||||
{
|
||||
\danog\MadelineProto\Logger::log('Istantiating \phpseclib\Crypt\RSA...');
|
||||
$this->key = new \phpseclib\Crypt\RSA();
|
||||
|
||||
\danog\MadelineProto\Logger::log('Loading key...');
|
||||
$this->key->loadKey($key);
|
||||
$this->n = $this->key->modulus;
|
||||
$this->e = $this->key->exponent;
|
||||
$this->fp_bytes = substr(sha1($this->serialize_param('bytes', null, $this->n->toBytes()).$this->serialize_param('bytes', null, $this->e->toBytes()), true), -8);
|
||||
|
||||
\danog\MadelineProto\Logger::log('Computing fingerprint...');
|
||||
$this->fp_bytes = substr(
|
||||
sha1(
|
||||
$this->serialize_param(
|
||||
'bytes',
|
||||
null,
|
||||
$this->n->toBytes()
|
||||
)
|
||||
.
|
||||
$this->serialize_param(
|
||||
'bytes',
|
||||
null,
|
||||
$this->e->toBytes()
|
||||
),
|
||||
true
|
||||
),
|
||||
-8
|
||||
);
|
||||
|
||||
\danog\MadelineProto\Logger::log('Generating BigInteger object for fingerprint...');
|
||||
$this->fp = new \phpseclib\Math\BigInteger(strrev($this->fp_bytes), -256);
|
||||
}
|
||||
|
||||
|
@ -23,98 +23,101 @@ class TL extends \danog\MadelineProto\Tools
|
||||
$TL_dict['methods'] = array_merge(json_decode(file_get_contents($file), true)['methods'], $TL_dict['methods']);
|
||||
}
|
||||
} else {
|
||||
$TL_dict = json_decode(file_get_contents($file), true);
|
||||
$TL_dict = json_decode(file_get_contents($filename), true);
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Translating objects...');
|
||||
$this->constructors = $TL_dict['constructors'];
|
||||
$this->constructor_id = [];
|
||||
$this->constructor_type = [];
|
||||
foreach ($this->constructors as $elem) {
|
||||
$z = new TLConstructor($elem);
|
||||
$z = new \danog\MadelineProto\TL\TLConstructor($elem);
|
||||
$this->constructor_id[$z->id] = $z;
|
||||
$this->constructor_type[$z->predicate] = $z;
|
||||
}
|
||||
|
||||
\danog\MadelineProto\Logger::log('Translating methods...');
|
||||
$this->methods = $TL_dict['methods'];
|
||||
$this->method_id = [];
|
||||
$this->method_name = [];
|
||||
$this->method_name_namespaced = [];
|
||||
foreach ($this->methods as $elem) {
|
||||
$z = new TLMethod($elem);
|
||||
$z = new \danog\MadelineProto\TL\TLMethod($elem);
|
||||
$this->method_id[$z->id] = $z;
|
||||
$this->method_name[$z->method] = $z;
|
||||
$this->method_name_namespaced[$z->method] = explode('.', $z->method);
|
||||
}
|
||||
}
|
||||
|
||||
public function serialize_obj($type_, $kwargs)
|
||||
public function get_named_method_args($method, $arguments)
|
||||
{
|
||||
$bytes_io = '';
|
||||
if (isset($this->constructor_type[$type_])) {
|
||||
$tl_constructor = $this->constructor_type[$type_];
|
||||
} else {
|
||||
throw new Exception('Could not extract type: '.$type_);
|
||||
}
|
||||
$bytes_io .= \danog\PHP\Struct::pack('<i', $tl_constructor->id);
|
||||
foreach ($tl_constructor->params as $arg) {
|
||||
$bytes_io .= $this->serialize_param($arg['type'], $arg['subtype'], $kwargs[$arg['name']]);
|
||||
if (!isset($this->method_name[$method])) {
|
||||
throw new Exception('Could not extract type: '.$method);
|
||||
}
|
||||
$tl_method = $this->method_name[$method];
|
||||
|
||||
return $bytes_io;
|
||||
}
|
||||
|
||||
public function get_named_method_args($type_, $kwargs)
|
||||
{
|
||||
if (isset($this->method_name[$type_])) {
|
||||
$tl_method = $this->method_name[$type_];
|
||||
} else {
|
||||
throw new Exception('Could not extract type: '.$type_);
|
||||
}
|
||||
|
||||
if (count(array_filter(array_keys($kwargs), 'is_string')) == 0) {
|
||||
if (count(array_filter(array_keys($arguments), 'is_string')) == 0) {
|
||||
$argcount = 0;
|
||||
$newargs = [];
|
||||
foreach ($tl_method->params as $arg) {
|
||||
$newargs[$arg['name']] = $kwargs[$argcount++];
|
||||
foreach ($tl_method->params as $current_argument) {
|
||||
$newargs[$current_argument['name']] = $arguments[$argcount++];
|
||||
}
|
||||
$kwargs = $newargs;
|
||||
$arguments = $newargs;
|
||||
}
|
||||
|
||||
return $kwargs;
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
public function serialize_method($type_, $kwargs)
|
||||
public function serialize_obj($object, $arguments)
|
||||
{
|
||||
$bytes_io = '';
|
||||
if (isset($this->method_name[$type_])) {
|
||||
$tl_method = $this->method_name[$type_];
|
||||
} else {
|
||||
throw new Exception('Could not extract type: '.$type_);
|
||||
if (!isset($this->constructor_type[$object])) {
|
||||
throw new Exception('Could not extract type: '.$object);
|
||||
}
|
||||
$bytes_io .= \danog\PHP\Struct::pack('<i', $tl_method->id);
|
||||
foreach ($tl_method->params as $arg) {
|
||||
if (!isset($kwargs[$arg['name']])) {
|
||||
if ($arg['name'] == 'flags') {
|
||||
$kwargs['flags'] = 0;
|
||||
|
||||
$tl_method = $this->constructor_type[$object];
|
||||
$serialized = \danog\PHP\Struct::pack('<i', $tl_constructor->id);
|
||||
|
||||
foreach ($tl_constructor->params as $current_argument) {
|
||||
$serialized .= $this->serialize_param($current_argument['type'], $current_argument['subtype'], $arguments[$current_argument['name']]);
|
||||
}
|
||||
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
public function serialize_method($method, $arguments)
|
||||
{
|
||||
if (!isset($this->method_name[$method])) {
|
||||
throw new Exception('Could not extract type: '.$method);
|
||||
}
|
||||
|
||||
$tl_method = $this->method_name[$method];
|
||||
$serialized = \danog\PHP\Struct::pack('<i', $tl_method->id);
|
||||
|
||||
foreach ($tl_method->params as $current_argument) {
|
||||
if (!isset($arguments[$current_argument['name']])) {
|
||||
if ($current_argument['name'] == 'flags') {
|
||||
$arguments['flags'] = 0;
|
||||
} else {
|
||||
if ($arg['opt']) {
|
||||
if ($current_argument['opt']) {
|
||||
continue;
|
||||
}
|
||||
throw new Exception('Missing required parameter ('.$arg['name'].')');
|
||||
throw new Exception('Missing required parameter ('.$current_argument['name'].')');
|
||||
}
|
||||
}
|
||||
$bytes_io .= $this->serialize_param($arg['type'], $arg['subtype'], $kwargs[$arg['name']]);
|
||||
$serialized .= $this->serialize_param($current_argument['type'], $current_argument['subtype'], $arguments[$current_argument['name']]);
|
||||
}
|
||||
|
||||
return $bytes_io;
|
||||
return $serialized;
|
||||
}
|
||||
|
||||
public function serialize_param($type_, $subtype, $value)
|
||||
public function serialize_param($type, $subtype, $value)
|
||||
{
|
||||
switch ($type_) {
|
||||
switch ($type) {
|
||||
case 'int':
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception("serialize_param: given value isn't numeric");
|
||||
}
|
||||
if (!(strlen(decbin($value)) <= 32)) {
|
||||
throw new Exception('Given value is too long.');
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<i', $value);
|
||||
break;
|
||||
@ -122,9 +125,6 @@ class TL extends \danog\MadelineProto\Tools
|
||||
if (!is_numeric($value)) {
|
||||
throw new Exception("serialize_param: given value isn't numeric");
|
||||
}
|
||||
if (!(strlen(decbin($value)) <= 32)) {
|
||||
throw new Exception('Given value is too long.');
|
||||
}
|
||||
|
||||
return \danog\PHP\Struct::pack('<I', $value);
|
||||
break;
|
||||
@ -137,11 +137,7 @@ class TL extends \danog\MadelineProto\Tools
|
||||
break;
|
||||
case 'int128':
|
||||
case 'int256':
|
||||
if (!is_string($value)) {
|
||||
throw new Exception("serialize_param: given value isn't a string");
|
||||
}
|
||||
|
||||
return $value;
|
||||
return (string)$value;
|
||||
break;
|
||||
case 'double':
|
||||
return \danog\PHP\Struct::pack('<d', $value);
|
||||
@ -175,14 +171,14 @@ class TL extends \danog\MadelineProto\Tools
|
||||
|
||||
return $concat;
|
||||
default:
|
||||
throw new Exception("Couldn't serialize param with type ".$type_);
|
||||
throw new Exception("Couldn't serialize param with type ".$type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_length($bytes_io, $type_ = null, $subtype = null)
|
||||
public function get_length($bytes_io, $type = null, $subtype = null)
|
||||
{
|
||||
$this->deserialize($bytes_io, $type_, $subtype);
|
||||
$this->deserialize($bytes_io, $type, $subtype);
|
||||
|
||||
return ftell($bytes_io);
|
||||
}
|
||||
@ -190,12 +186,12 @@ class TL extends \danog\MadelineProto\Tools
|
||||
/**
|
||||
* :type bytes_io: io.BytesIO object.
|
||||
*/
|
||||
public function deserialize($bytes_io, $type_ = null, $subtype = null)
|
||||
public function deserialize($bytes_io, $type = null, $subtype = null)
|
||||
{
|
||||
if (!(get_resource_type($bytes_io) == 'file' || get_resource_type($bytes_io) == 'stream')) {
|
||||
throw new Exception('An invalid bytes_io handle provided.');
|
||||
throw new Exception('An invalid bytes_io handle was provided.');
|
||||
}
|
||||
switch ($type_) {
|
||||
switch ($type) {
|
||||
case 'int':
|
||||
$x = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
break;
|
||||
@ -249,16 +245,14 @@ class TL extends \danog\MadelineProto\Tools
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (isset($this->constructor_type[$type_])) {
|
||||
$tl_elem = $this->constructor_type[$type_];
|
||||
if (isset($this->constructor_type[$type])) {
|
||||
$tl_elem = $this->constructor_type[$type];
|
||||
} else {
|
||||
$Idata = fread($bytes_io, 4);
|
||||
$i = \danog\PHP\Struct::unpack('<i', $Idata) [0];
|
||||
if (isset($this->constructor_id[$i])) {
|
||||
$tl_elem = $this->constructor_id[$i];
|
||||
} else {
|
||||
throw new Exception('Could not extract type: '.$type_);
|
||||
$i = \danog\PHP\Struct::unpack('<i', fread($bytes_io, 4)) [0];
|
||||
if (!isset($this->constructor_id[$i])) {
|
||||
throw new Exception('Could not extract type: '.$type);
|
||||
}
|
||||
$tl_elem = $this->constructor_id[$i];
|
||||
}
|
||||
|
||||
$base_boxed_types = ['Vector t', 'Int', 'Long', 'Double', 'String', 'Int128', 'Int256'];
|
||||
@ -278,37 +272,40 @@ class TL extends \danog\MadelineProto\Tools
|
||||
|
||||
public function content_related($method)
|
||||
{
|
||||
return !in_array($method, [
|
||||
'rpc_result',
|
||||
'rpc_error',
|
||||
'rpc_drop_answer',
|
||||
'rpc_answer_unknown',
|
||||
'rpc_answer_dropped_running',
|
||||
'rpc_answer_dropped',
|
||||
'get_future_salts',
|
||||
'future_salt',
|
||||
'future_salts',
|
||||
'ping',
|
||||
'pong',
|
||||
'ping_delay_disconnect',
|
||||
'destroy_session',
|
||||
'destroy_session_ok',
|
||||
'destroy_session_none',
|
||||
'new_session_created',
|
||||
'msg_container',
|
||||
'msg_copy',
|
||||
'gzip_packed',
|
||||
'http_wait',
|
||||
'msgs_ack',
|
||||
'bad_msg_notification',
|
||||
'bad_server_salt',
|
||||
'msgs_state_req',
|
||||
'msgs_state_info',
|
||||
'msgs_all_info',
|
||||
'msg_detailed_info',
|
||||
'msg_new_detailed_info',
|
||||
'msg_resend_req',
|
||||
'msg_resend_ans_req',
|
||||
]);
|
||||
return !in_array(
|
||||
$method,
|
||||
[
|
||||
'rpc_result',
|
||||
'rpc_error',
|
||||
'rpc_drop_answer',
|
||||
'rpc_answer_unknown',
|
||||
'rpc_answer_dropped_running',
|
||||
'rpc_answer_dropped',
|
||||
'get_future_salts',
|
||||
'future_salt',
|
||||
'future_salts',
|
||||
'ping',
|
||||
'pong',
|
||||
'ping_delay_disconnect',
|
||||
'destroy_session',
|
||||
'destroy_session_ok',
|
||||
'destroy_session_none',
|
||||
'new_session_created',
|
||||
'msg_container',
|
||||
'msg_copy',
|
||||
'gzip_packed',
|
||||
'http_wait',
|
||||
'msgs_ack',
|
||||
'bad_msg_notification',
|
||||
'bad_server_salt',
|
||||
'msgs_state_req',
|
||||
'msgs_state_info',
|
||||
'msgs_all_info',
|
||||
'msg_detailed_info',
|
||||
'msg_new_detailed_info',
|
||||
'msg_resend_req',
|
||||
'msg_resend_ans_req',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
21
testing.php
21
testing.php
@ -2,10 +2,19 @@
|
||||
<?php
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
$config = parse_ini_file('credentials', true);
|
||||
if (!$config) {
|
||||
pyjslib_printnl("File 'credentials' seems to not exist.");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
$MadelineProto = new \danog\MadelineProto\API($config);
|
||||
$MadelineProto = new \danog\MadelineProto\API();
|
||||
|
||||
if (file_exists('number.php')) {
|
||||
include_once 'number.php';
|
||||
$sendCode = $MadelineProto->auth->sendCode(
|
||||
[
|
||||
'phone_number' => $number,
|
||||
'sms_type' => 5,
|
||||
'api_id' => $this->settings['app_info']['api_id'],
|
||||
'api_hash' => $this->settings['app_info']['api_hash'],
|
||||
'lang_code' => $this->settings['app_info']['lang_code'],
|
||||
]
|
||||
);
|
||||
var_dump($sendCode);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user