Added support for 32 bit systems, imimplemented threading.
This commit is contained in:
parent
289006323f
commit
c235afb30f
@ -1,6 +1,2 @@
|
||||
MTPROTO_NUMBER=+39373739
|
||||
MTPROTO_SETTINGS={"app_info":{"api_id":6,"api_hash":"eb06d4abfb49dc3eeb1aeb98ae0f581e"}, "connection_settings":{"all":{"test_mode":true}}}
|
||||
TEST_USERNAME=@danogentili
|
||||
TEST_DESTINATION_GROUPS=["@danogentili"]
|
||||
BOT_TOKEN=299050432:AAGMdcyfqGTpwCo0bJkmlomHwMUieXRs8GY
|
||||
|
||||
MTPROTO_NUMBER=+393924682693
|
||||
MTPROTO_SETTINGS={"app_info":{"api_id":6,"api_hash":"eb06d4abfb49dc3eeb1aeb98ae0f581e"},"connection_settings":{"all":{"test_mode":true}}}
|
||||
|
@ -336,7 +336,7 @@ Before sending any message, you must check if the secret chat was accepted by th
|
||||
|
||||
|
||||
```
|
||||
$status = $MadelineProto->secret_chat_info($chat);
|
||||
$status = $MadelineProto->secret_chat_status($chat);
|
||||
```
|
||||
|
||||
Returns 0 if the chat cannot be found in the local database, 1 if the chat was requested but not yet accepted, and 2 if it is a valid accepted secret chat.
|
||||
@ -455,7 +455,7 @@ The same operation should be done when serializing to another destination manual
|
||||
|
||||
### Exceptions
|
||||
|
||||
MadelineProto can throw three different exceptions:
|
||||
MadelineProto can throw lots of different exceptions:
|
||||
* \danog\MadelineProto\Exception - Default exception, thrown when a php error occures and in a lot of other cases
|
||||
|
||||
* \danog\MadelineProto\RPCErrorException - Thrown when an RPC error occurres (an error received via the mtproto API)
|
||||
@ -466,7 +466,7 @@ MadelineProto can throw three different exceptions:
|
||||
|
||||
* \danog\MadelineProto\SecurityException - Thrown on security problems (invalid params during generation of auth key or similar)
|
||||
|
||||
* \danog\MadelineProto\Conversion\Exception - Thrown if some param/object can't be converted to/from bot API/TD/TD-CLI format (this includes markdown/html parsing)
|
||||
* \danog\MadelineProto\TL\Conversion\Exception - Thrown if some param/object can't be converted to/from bot API/TD/TD-CLI format (this includes markdown/html parsing)
|
||||
|
||||
|
||||
|
||||
|
3
bot.php
3
bot.php
@ -22,6 +22,9 @@ try {
|
||||
$MadelineProto = new \danog\MadelineProto\API($settings);
|
||||
$authorization = $MadelineProto->bot_login($token);
|
||||
\danog\MadelineProto\Logger::log([$authorization], \danog\MadelineProto\Logger::NOTICE);
|
||||
} else {
|
||||
echo 'token.php does not exist';
|
||||
die;
|
||||
}
|
||||
}
|
||||
$offset = 0;
|
||||
|
@ -68,4 +68,4 @@ for ($x = 0; $x < $argv[2]; $x++) {
|
||||
$MadelineProto->get_updates_difference();
|
||||
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('session.madeline', $MadelineProto).' bytes'.PHP_EOL;
|
||||
}
|
||||
$MadelineProto->messages->sendMessage(['peer' => $argv[1], 'message' => '[Powered by MadelineProto](https://github.com/danog/MadelineProto)', 'parse_mode' => 'markdown']);
|
||||
$MadelineProto->messages->sendMessage(['peer' => $argv[1], 'message' => '<a href="https://github.com/danog/MadelineProto">Powered by MadelineProto</a>', 'parse_mode' => 'markdown']);
|
||||
|
@ -22,11 +22,6 @@ class API extends APIFactory
|
||||
|
||||
public function __construct($params = [])
|
||||
{
|
||||
// Detect 64 bit
|
||||
if (PHP_INT_SIZE < 8) {
|
||||
throw new Exception('MadelineProto supports only 64 bit systems ATM');
|
||||
}
|
||||
|
||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
||||
$this->API = new MTProto($params);
|
||||
|
||||
@ -41,18 +36,19 @@ class API extends APIFactory
|
||||
$this->API->v = $this->API->getV();
|
||||
\danog\MadelineProto\Logger::log(['MadelineProto is ready!'], Logger::NOTICE);
|
||||
}
|
||||
|
||||
/*
|
||||
public function __sleep()
|
||||
{
|
||||
//$this->API->reset_session(false);
|
||||
|
||||
return ['API'];
|
||||
}
|
||||
*/
|
||||
|
||||
public function __wakeup()
|
||||
{
|
||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
||||
$this->APIFactory();
|
||||
//$this->APIFactory();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
|
@ -94,6 +94,6 @@ class APIFactory
|
||||
{
|
||||
$this->API->get_config();
|
||||
|
||||
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : [], (isset($arguments[1]) && is_array($arguments[1])) ? $arguments[1] : []);
|
||||
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : [], (isset($arguments[1]) && is_array($arguments[1])) ? $arguments[1] : ['datacenter' => $this->API->datacenter->curdc]);
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,8 @@ class Connection
|
||||
public $temp_auth_key;
|
||||
public $auth_key;
|
||||
public $session_id;
|
||||
public $seq_no = 0;
|
||||
public $authorized = false;
|
||||
public $authorization = null;
|
||||
public $login_temp_status = 'none';
|
||||
public $session_out_seq_no = 0;
|
||||
public $session_in_seq_no = 0;
|
||||
|
||||
public $incoming_messages = [];
|
||||
public $outgoing_messages = [];
|
||||
@ -105,7 +103,9 @@ class Connection
|
||||
case 'tcp_full':
|
||||
case 'http':
|
||||
case 'https':
|
||||
fclose($this->sock);
|
||||
try {
|
||||
fclose($this->sock);
|
||||
} catch (\danog\MadelineProto\Exception $e) { ; }
|
||||
break;
|
||||
case 'udp':
|
||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||
@ -167,8 +167,8 @@ class Connection
|
||||
throw new Exception("Connection: couldn't connect to socket.");
|
||||
}
|
||||
$packet = stream_get_contents($this->sock, $length);
|
||||
if ($packet === false) {
|
||||
throw new NothingInTheSocketException('Nothing in the socket!');
|
||||
if ($packet === false || strlen($packet) === 0) {
|
||||
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!');
|
||||
}
|
||||
if (strlen($packet) != $length) {
|
||||
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
|
||||
|
@ -24,6 +24,9 @@ class DataCenter
|
||||
public $dclist = [];
|
||||
public $settings = [];
|
||||
|
||||
public function __sleep() {
|
||||
return ['sockets', 'curdc', 'dclist', 'settings'];
|
||||
}
|
||||
public function __construct(&$dclist, &$settings)
|
||||
{
|
||||
$this->dclist = &$dclist;
|
||||
@ -44,38 +47,37 @@ class DataCenter
|
||||
}
|
||||
}
|
||||
|
||||
public function dc_connect($dc_number, $settings = [])
|
||||
public function dc_connect($dc_number)
|
||||
{
|
||||
$this->curdc = $dc_number;
|
||||
if (isset($this->sockets[$dc_number])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($settings === []) {
|
||||
$settings = $this->settings[$dc_number];
|
||||
}
|
||||
$test = $settings['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $settings['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
$test = $this->settings[$dc_number]['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings[$dc_number]['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
|
||||
$address = $settings['ipv6'] ? '['.$address.']' : $address;
|
||||
$address = $this->settings[$dc_number]['ipv6'] ? '['.$address.']' : $address;
|
||||
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
|
||||
if ($settings['protocol'] === 'https') {
|
||||
if ($this->settings[$dc_number]['protocol'] === 'https') {
|
||||
$subdomain = $this->dclist['ssl_subdomains'][$dc_number];
|
||||
$path = $settings['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
$address = $settings['protocol'].'://'.$subdomain.'.web.telegram.org/'.$path;
|
||||
$path = $this->settings[$dc_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
|
||||
$address = $this->settings[$dc_number]['protocol'].'://'.$subdomain.'.web.telegram.org/'.$path;
|
||||
}
|
||||
|
||||
if ($settings['protocol'] === 'http') {
|
||||
$address = $settings['protocol'].'://'.$address.'/api';
|
||||
if ($this->settings[$dc_number]['protocol'] === 'http') {
|
||||
$address = $this->settings[$dc_number]['protocol'].'://'.$address.'/api';
|
||||
$port = 80;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.', '.$settings['protocol'].')...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
$this->sockets[$dc_number] = new Connection($address, $port, $settings['protocol'], $settings['timeout']);
|
||||
\danog\MadelineProto\Logger::log(['Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.', '.$this->settings[$dc_number]['protocol'].')...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
$this->sockets[$dc_number] = new Connection($address, $port, $this->settings[$dc_number]['protocol'], $this->settings[$dc_number]['timeout']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_dcs() {
|
||||
$test = $this->settings[2]['test_mode'] ? 'test' : 'main';
|
||||
$ipv6 = $this->settings[2]['ipv6'] ? 'ipv6' : 'ipv4';
|
||||
return array_keys($this->dclist[$test][$ipv6]);
|
||||
}
|
||||
public function &__get($name)
|
||||
{
|
||||
return $this->sockets[$this->curdc]->{$name};
|
||||
|
@ -14,6 +14,13 @@ namespace danog\MadelineProto;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
public function __construct($message = null, $code = 0, Exception $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
if (\danog\MadelineProto\Logger::$constructed && $this->file !== __FILE__) {
|
||||
\danog\MadelineProto\Logger::log([$message.' in '.basename($this->file).':'.$this->line], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* ExceptionErrorHandler.
|
||||
*
|
||||
|
@ -22,25 +22,14 @@ class Logger
|
||||
public static $constructed = false;
|
||||
public static $prefix = '';
|
||||
public static $level = 3;
|
||||
const ULTRA_VERBOSE = 'ULTRA_VERBOSE';
|
||||
const VERBOSE = 'VERBOSE';
|
||||
const NOTICE = 'NOTICE';
|
||||
const WARNING = 'WARNING';
|
||||
const ERROR = 'ERROR';
|
||||
const FATAL_ERROR = 'FATAL ERROR';
|
||||
const ULTRA_VERBOSE = 5;
|
||||
const VERBOSE = 4;
|
||||
const NOTICE = 3;
|
||||
const WARNING = 2;
|
||||
const ERROR = 1;
|
||||
const FATAL_ERROR = 0;
|
||||
|
||||
public static function level2num($level)
|
||||
{
|
||||
switch ($level) {
|
||||
case self::ULTRA_VERBOSE: return 5;
|
||||
case self::VERBOSE: return 4;
|
||||
case self::NOTICE: return 3;
|
||||
case self::WARNING: return 2;
|
||||
case self::ERROR: return 1;
|
||||
case self::FATAL_ERROR: return 0;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Constructor function
|
||||
@ -59,23 +48,27 @@ class Logger
|
||||
self::$optional = &$optional;
|
||||
self::$constructed = true;
|
||||
self::$prefix = $prefix === '' ? '' : ', '.$prefix;
|
||||
self::$level = self::level2num($level);
|
||||
self::$level = $level;
|
||||
}
|
||||
|
||||
public static function log($params, $level = self::NOTICE)
|
||||
{
|
||||
|
||||
if (!self::$constructed) {
|
||||
throw new Exception("The constructor function wasn't called! Please call the constructor function before using this method.");
|
||||
}
|
||||
$level = self::level2num($level);
|
||||
if ($level > self::$level) {
|
||||
return false;
|
||||
}
|
||||
$prefix = self::$prefix;
|
||||
if (class_exists('\Thread') && is_object(\Thread::getCurrentThread())) {
|
||||
$prefix .= ' (t)';
|
||||
}
|
||||
foreach (is_array($params) ? $params : [$params] as $param) {
|
||||
if (!is_string($param)) {
|
||||
$param = var_export($param, true);
|
||||
}
|
||||
$param = str_pad(basename(debug_backtrace()[0]['file'], '.php').self::$prefix.': ', 16 + strlen(self::$prefix))."\t".$param;
|
||||
$param = str_pad(basename(debug_backtrace()[0]['file'], '.php').$prefix.': ', 16 + strlen($prefix))."\t".$param;
|
||||
switch (self::$mode) {
|
||||
case 1:
|
||||
error_log($param);
|
||||
|
@ -61,7 +61,7 @@ class Lua
|
||||
if ($params === 0) {
|
||||
return 0;
|
||||
}
|
||||
$result = $this->MadelineProto->API->method_call($params['_'], $params);
|
||||
$result = $this->MadelineProto->API->method_call($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]);
|
||||
if (is_callable($cb)) {
|
||||
$cb($this->MadelineProto->mtproto_to_td($result), $cb_extra);
|
||||
}
|
||||
@ -71,7 +71,7 @@ class Lua
|
||||
|
||||
public function madeline_function($params, $cb = null, $cb_extra = null)
|
||||
{
|
||||
$result = $this->MadelineProto->API->method_call($params['_'], $params);
|
||||
$result = $this->MadelineProto->API->method_call($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]);
|
||||
if (is_callable($cb)) {
|
||||
$cb($result, $cb_extra);
|
||||
}
|
||||
|
@ -25,27 +25,37 @@ class MTProto
|
||||
use \danog\MadelineProto\MTProtoTools\MsgIdHandler;
|
||||
use \danog\MadelineProto\MTProtoTools\PeerHandler;
|
||||
use \danog\MadelineProto\MTProtoTools\ResponseHandler;
|
||||
use \danog\MadelineProto\MTProtoTools\SaltHandler;
|
||||
//use \danog\MadelineProto\MTProtoTools\SaltHandler;
|
||||
use \danog\MadelineProto\MTProtoTools\SeqNoHandler;
|
||||
use \danog\MadelineProto\MTProtoTools\UpdateHandler;
|
||||
use \danog\MadelineProto\MTProtoTools\Files;
|
||||
use \danog\MadelineProto\SecretChats\AuthKeyHandler;
|
||||
use \danog\MadelineProto\SecretChats\MessageHandler;
|
||||
use \danog\MadelineProto\TL\TL;
|
||||
use \danog\MadelineProto\Conversion\BotAPI;
|
||||
use \danog\MadelineProto\Conversion\BotAPIFiles;
|
||||
use \danog\MadelineProto\Conversion\Extension;
|
||||
use \danog\MadelineProto\Conversion\TD;
|
||||
use \danog\MadelineProto\TL\Conversion\BotAPI;
|
||||
use \danog\MadelineProto\TL\Conversion\BotAPIFiles;
|
||||
use \danog\MadelineProto\TL\Conversion\Extension;
|
||||
use \danog\MadelineProto\TL\Conversion\TD;
|
||||
use \danog\MadelineProto\Tools;
|
||||
|
||||
public $settings = [];
|
||||
public $config = ['expires' => -1];
|
||||
public $ipv6 = false;
|
||||
public $should_serialize = true;
|
||||
public $authorization = null;
|
||||
public $authorized = false;
|
||||
public $login_temp_status = 'none';
|
||||
public $bigint = false;
|
||||
public $run_workers = false;
|
||||
public $threads = false;
|
||||
public $readers = [];
|
||||
|
||||
public function __construct($settings = [])
|
||||
{
|
||||
$this->bigint = PHP_INT_SIZE < 8;
|
||||
// Parse settings
|
||||
$this->parse_settings($settings);
|
||||
|
||||
|
||||
// Connect to servers
|
||||
\danog\MadelineProto\Logger::log(['Istantiating DataCenter...'], Logger::ULTRA_VERBOSE);
|
||||
if (isset($this->datacenter)) {
|
||||
@ -60,37 +70,90 @@ class MTProto
|
||||
// Istantiate TL class
|
||||
\danog\MadelineProto\Logger::log(['Translating tl schemas...'], Logger::ULTRA_VERBOSE);
|
||||
$this->construct_TL($this->settings['tl_schema']['src']);
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Define some needed numbers for BigInteger
|
||||
*/
|
||||
/*
|
||||
* ***********************************************************************
|
||||
* Define some needed numbers for BigInteger
|
||||
*/
|
||||
\danog\MadelineProto\Logger::log(['Executing dh_prime checks (0/3)...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
|
||||
$this->zero = new \phpseclib\Math\BigInteger(0);
|
||||
$this->one = new \phpseclib\Math\BigInteger(1);
|
||||
//$two = new \phpseclib\Math\BigInteger(2);
|
||||
$this->three = new \phpseclib\Math\BigInteger(3);
|
||||
$this->four = new \phpseclib\Math\BigInteger(4);
|
||||
$this->twoe1984 = new \phpseclib\Math\BigInteger('1751908409537131537220509645351687597690304110853111572994449976845956819751541616602568796259317428464425605223064365804210081422215355425149431390635151955247955156636234741221447435733643262808668929902091770092492911737768377135426590363166295684370498604708288556044687341394398676292971255828404734517580702346564613427770683056761383955397564338690628093211465848244049196353703022640400205739093118270803778352768276670202698397214556629204420309965547056893233608758387329699097930255380715679250799950923553703740673620901978370802540218870279314810722790539899334271514365444369275682816');
|
||||
$this->twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328');
|
||||
$this->twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656');
|
||||
|
||||
$this->switch_dc(2, true);
|
||||
$this->get_config();
|
||||
}
|
||||
$this->connect_to_all_dcs();
|
||||
$this->datacenter->curdc = 2;
|
||||
|
||||
$nearest_dc = $this->method_call('help.getNearestDc', [], ['datacenter' => $this->datacenter->curdc]);
|
||||
\danog\MadelineProto\Logger::log(["We're in ".$nearest_dc['country'].', current dc is '.$nearest_dc['this_dc'].', nearest dc is '.$nearest_dc['nearest_dc'].'.'], Logger::NOTICE);
|
||||
|
||||
if ($nearest_dc['nearest_dc'] != $nearest_dc['this_dc']) {
|
||||
$this->datacenter->curdc = $nearest_dc['nearest_dc'];
|
||||
$this->settings['connection_settings']['default_dc'] = $nearest_dc['nearest_dc'];
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
|
||||
$this->get_config([], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->v = $this->getV();
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
public function setup_threads() {
|
||||
if ($this->threads = $this->run_workers = class_exists('\Pool') && php_sapi_name() == "cli" && $this->settings['threading']['allow_threading']) {
|
||||
\danog\MadelineProto\Logger::log(['THREADING IS ENABLED'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->start_threads();
|
||||
}
|
||||
|
||||
}
|
||||
public function start_threads() {
|
||||
if ($this->threads) {
|
||||
$dcs = $this->datacenter->get_dcs();
|
||||
if (!isset($this->reader_pool)) $this->reader_pool = new \Pool(count($dcs));
|
||||
foreach ($dcs as $dc) {
|
||||
if (!isset($this->readers[$dc])) {
|
||||
$this->readers [$dc] = new \danog\MadelineProto\Threads\SocketReader($this, $dc);
|
||||
}
|
||||
if (!$this->readers[$dc]->isRunning()) {
|
||||
$this->readers[$dc]->garbage = false;
|
||||
$this->reader_pool->submit($this->readers[$dc]);
|
||||
Logger::log(['Socket reader on DC '.$dc.': RESTARTED'], Logger::WARNING);
|
||||
} else {
|
||||
Logger::log(['Socket reader on DC '.$dc.': WORKING'], Logger::NOTICE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public function __sleep() {
|
||||
$t = get_object_vars($this);
|
||||
if (isset($t['reader_pool'])) unset($t['reader_pool']);
|
||||
return array_keys($t);
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
if (debug_backtrace()[0]['file'] === __DIR__.'/Threads/SocketReader.php' || (debug_backtrace()[0]['file'] === __FILE__ && debug_backtrace()[0]['line'] === 117)) return;
|
||||
$this->bigint = PHP_INT_SIZE < 8;
|
||||
$this->setup_logger();
|
||||
if (!isset($this->v) || $this->v !== $this->getV()) {
|
||||
\danog\MadelineProto\Logger::log(['Serialization is out of date, reconstructing object!'], Logger::WARNING);
|
||||
$this->__construct($this->settings);
|
||||
$this->v = $this->getV();
|
||||
}
|
||||
$this->setup_threads();
|
||||
$this->datacenter->__construct($this->settings['connection'], $this->settings['connection_settings']);
|
||||
$this->reset_session();
|
||||
if ($this->datacenter->authorized && $this->settings['updates']['handle_updates']) {
|
||||
if ($this->authorized && $this->settings['updates']['handle_updates']) {
|
||||
\danog\MadelineProto\Logger::log(['Getting updates after deserialization...'], Logger::NOTICE);
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if (isset($this->reader_pool)) {
|
||||
$this->run_workers = false;
|
||||
\danog\MadelineProto\Logger::log(['Shutting down reader pool...'], Logger::NOTICE);
|
||||
$this->reader_pool->shutdown();
|
||||
}
|
||||
}
|
||||
public function parse_settings($settings)
|
||||
{
|
||||
// Detect ipv6
|
||||
@ -100,11 +163,11 @@ class MTProto
|
||||
'timeout' => 1,
|
||||
],
|
||||
]);
|
||||
|
||||
$google = file_get_contents('https://ipv6.google.com', false, $ctx);
|
||||
$google = file_get_contents('http://ipv6.test-ipv6.com/', false, $ctx);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
$this->ipv6 = strlen($google) > 0;
|
||||
|
||||
// Detect device model
|
||||
try {
|
||||
$device_model = php_uname('s');
|
||||
@ -182,7 +245,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
'protocol' => 'tcp_full', // can be tcp_full, tcp_abridged, tcp_intermediate, http, https, udp (unsupported)
|
||||
'test_mode' => false, // decides whether to connect to the main telegram servers or to the testing servers (deep telegram)
|
||||
'ipv6' => $this->ipv6, // decides whether to use ipv6, ipv6 attribute of API attribute of API class contains autodetected boolean
|
||||
'timeout' => 3, // timeout for sockets
|
||||
'timeout' => 2, // timeout for sockets
|
||||
],
|
||||
],
|
||||
'app_info' => [ // obtained in https://my.telegram.org
|
||||
@ -190,8 +253,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
'api_hash' => '4251a2777e179232705e2462706f4143',
|
||||
'device_model' => $device_model,
|
||||
'system_version' => $system_version,
|
||||
// 'app_version' => 'Unicorn', // 🌚
|
||||
'app_version' => $this->getV(),
|
||||
'app_version' => 'Unicorn', // 🌚
|
||||
// 'app_version' => $this->getV(),
|
||||
'lang_code' => 'en',
|
||||
],
|
||||
'tl_schema' => [ // TL scheme files
|
||||
@ -236,6 +299,13 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
'secret_chats' => [
|
||||
'accept_chats' => true, // Should I accept secret chats? Can be true, false or on array of user ids from which to accept chats
|
||||
],
|
||||
'calls' => [
|
||||
'accept_calls' => true, // Should I accept calls? Can be true, false or on array of user ids from which to accept calls
|
||||
],
|
||||
'threading' => [
|
||||
'allow_threading' => false, // Should I use threading, if it is enabled?
|
||||
'handler_workers' => 10 // How many workers should every message handler pool of each socket reader have
|
||||
],
|
||||
'pwr' => ['pwr' => false, 'db_token' => false, 'strict' => false],
|
||||
];
|
||||
$settings = array_replace_recursive($default_settings, $settings);
|
||||
@ -247,15 +317,26 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
}
|
||||
unset($settings['connection_settings']['all']);
|
||||
}
|
||||
switch ($settings['logger']['logger_level']) {
|
||||
case 'ULTRA_VERBOSE': $settings['logger']['logger_level'] = 5; break;
|
||||
case 'VERBOSE': $settings['logger']['logger_level'] = 4; break;
|
||||
case 'NOTICE': $settings['logger']['logger_level'] = 3; break;
|
||||
case 'WARNING': $settings['logger']['logger_level'] = 2; break;
|
||||
case 'ERROR': $settings['logger']['logger_level'] = 1; break;
|
||||
case 'FATAL ERROR': $settings['logger']['logger_level'] = 0; break;
|
||||
}
|
||||
|
||||
$this->settings = $settings;
|
||||
|
||||
// Setup logger
|
||||
$this->setup_logger();
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
|
||||
|
||||
public function setup_logger()
|
||||
{
|
||||
\danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param'], isset($this->datacenter->authorization['user']) ? (isset($this->datacenter->authorization['user']['username']) ? $this->datacenter->authorization['user']['username'] : $this->datacenter->authorization['user']['id']) : '', isset($this->settings['logger']['logger_level']) ? $this->settings['logger']['logger_level'] : Logger::VERBOSE);
|
||||
\danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param'], isset($this->authorization['user']) ? (isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id']) : '', isset($this->settings['logger']['logger_level']) ? $this->settings['logger']['logger_level'] : Logger::VERBOSE);
|
||||
}
|
||||
|
||||
public function reset_session($de = true)
|
||||
@ -264,67 +345,66 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
if ($de) {
|
||||
\danog\MadelineProto\Logger::log(['Resetting session id and seq_no in DC '.$id.'...'], Logger::VERBOSE);
|
||||
$socket->session_id = $this->random(8);
|
||||
$socket->seq_no = 0;
|
||||
$socket->session_in_seq_no = 0;
|
||||
$socket->session_out_seq_no = 0;
|
||||
}
|
||||
$socket->incoming_messages = [];
|
||||
$socket->outgoing_messages = [];
|
||||
$socket->new_outgoing = [];
|
||||
$socket->new_incoming = [];
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Switches to a new datacenter and if necessary creates authorization keys, binds them and writes client info
|
||||
public function switch_dc($new_dc, $allow_nearest_dc_switch = false)
|
||||
// Connects to all datacenters and if necessary creates authorization keys, binds them and writes client info
|
||||
public function connect_to_all_dcs()
|
||||
{
|
||||
$old_dc = $this->datacenter->curdc;
|
||||
\danog\MadelineProto\Logger::log(['Switching from DC '.$old_dc.' to DC '.$new_dc.'...'], Logger::NOTICE);
|
||||
if (!isset($this->datacenter->sockets[$new_dc])) {
|
||||
$this->datacenter->dc_connect($new_dc);
|
||||
$this->init_authorization();
|
||||
$this->get_config($this->write_client_info('help.getConfig'));
|
||||
$this->get_nearest_dc($allow_nearest_dc_switch);
|
||||
foreach ($old = $this->datacenter->get_dcs() as $new_dc) {
|
||||
if (!isset($this->datacenter->sockets[$new_dc])) $this->datacenter->dc_connect($new_dc);
|
||||
}
|
||||
$this->datacenter->curdc = $new_dc;
|
||||
if (
|
||||
(isset($this->datacenter->sockets[$old_dc]->authorized) && $this->datacenter->sockets[$old_dc]->authorized) &&
|
||||
!(isset($this->datacenter->sockets[$new_dc]->authorized) && $this->datacenter->sockets[$new_dc]->authorized && $this->datacenter->sockets[$new_dc]->authorization['user']['id'] === $this->datacenter->sockets[$old_dc]->authorization['user']['id'])
|
||||
) {
|
||||
\danog\MadelineProto\Logger::log(['Copying authorization...'], Logger::VERBOSE);
|
||||
$this->should_serialize = true;
|
||||
$this->datacenter->curdc = $old_dc;
|
||||
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => $new_dc]);
|
||||
$this->datacenter->curdc = $new_dc;
|
||||
if (isset($this->datacenter->sockets[$new_dc]->authorized) && $this->datacenter->sockets[$new_dc]->authorized && $this->datacenter->sockets[$new_dc]->authorization['user']['id'] !== $this->datacenter->sockets[$old_dc]->authorization['user']['id']) {
|
||||
$this->method_call('auth.logOut');
|
||||
}
|
||||
$this->datacenter->authorization = $this->method_call('auth.importAuthorization', $exported_authorization);
|
||||
$this->datacenter->authorized = true;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Done! Current DC is '.$this->datacenter->curdc], Logger::NOTICE);
|
||||
$this->setup_threads();
|
||||
$this->init_authorization();
|
||||
if ($old !== $this->datacenter->get_dcs()) $this->connect_to_all_dcs();
|
||||
}
|
||||
|
||||
// Creates authorization keys
|
||||
public function init_authorization()
|
||||
{
|
||||
if ($this->datacenter->session_id === null) {
|
||||
$this->datacenter->session_id = $this->random(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...'], Logger::NOTICE);
|
||||
$this->datacenter->auth_key = $this->create_auth_key(-1);
|
||||
foreach ($this->datacenter->sockets as $id => &$socket) {
|
||||
if ($socket->session_id === null) {
|
||||
$socket->session_id = $this->random(8);
|
||||
$socket->session_in_seq_no = 0;
|
||||
$socket->session_out_seq_no = 0;
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Generating temporary authorization key...'], Logger::NOTICE);
|
||||
$this->datacenter->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']);
|
||||
$this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in']);
|
||||
if (in_array($this->datacenter->protocol, ['http', 'https'])) {
|
||||
$this->method_call('http_wait', ['max_wait' => 0, 'wait_after' => 0, 'max_delay' => 0]);
|
||||
if ($socket->temp_auth_key === null || $socket->auth_key === null) {
|
||||
if ($socket->auth_key === null) {
|
||||
\danog\MadelineProto\Logger::log(['Generating permanent authorization key for DC '.$id.'...'], Logger::NOTICE);
|
||||
$socket->auth_key = $this->create_auth_key(-1, $id);
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Generating temporary authorization key for DC '.$id.'...'], Logger::NOTICE);
|
||||
$socket->temp_auth_key = $this->create_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
$this->bind_temp_auth_key($this->settings['authorization']['default_temp_auth_key_expires_in'], $id);
|
||||
$this->get_config($this->write_client_info('help.getConfig', [], ['datacenter' => $id]));
|
||||
if (in_array($socket->protocol, ['http', 'https'])) {
|
||||
$this->method_call('http_wait', ['max_wait' => 0, 'wait_after' => 0, 'max_delay' => 0], ['datacenter' => $id]);
|
||||
}
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function sync_authorization($authorized_dc) {
|
||||
foreach ($this->datacenter->sockets as $new_dc => &$socket) {
|
||||
if ($new_dc === $authorized_dc) continue;
|
||||
\danog\MadelineProto\Logger::log(['Copying authorization from dc '.$authorized_dc.' to dc '.$new_dc.'...'], Logger::VERBOSE);
|
||||
$this->should_serialize = true;
|
||||
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => $new_dc], ['datacenter' => $authorized_dc]);
|
||||
$this->method_call('auth.logOut', [], ['datacenter' => $new_dc]);
|
||||
$this->method_call('auth.importAuthorization', $exported_authorization, ['datacenter' => $new_dc]);
|
||||
}
|
||||
}
|
||||
|
||||
public function write_client_info($method, $arguments = [])
|
||||
public function write_client_info($method, $arguments = [], $options = [])
|
||||
{
|
||||
\danog\MadelineProto\Logger::log(['Writing client info (also executing '.$method.')...'], Logger::NOTICE);
|
||||
|
||||
@ -338,28 +418,17 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
['query' => $this->serialize_method($method, $arguments)]
|
||||
)
|
||||
),
|
||||
]
|
||||
],
|
||||
$options
|
||||
);
|
||||
}
|
||||
|
||||
public function get_nearest_dc($allow_switch)
|
||||
{
|
||||
$nearest_dc = $this->method_call('help.getNearestDc');
|
||||
\danog\MadelineProto\Logger::log(["We're in ".$nearest_dc['country'].', current dc is '.$nearest_dc['this_dc'].', nearest dc is '.$nearest_dc['nearest_dc'].'.'], Logger::NOTICE);
|
||||
|
||||
if ($nearest_dc['nearest_dc'] != $nearest_dc['this_dc'] && $allow_switch) {
|
||||
$this->switch_dc($nearest_dc['nearest_dc']);
|
||||
$this->settings['connection_settings']['default_dc'] = $nearest_dc['nearest_dc'];
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function get_config($config = [])
|
||||
public function get_config($config = [], $options = [])
|
||||
{
|
||||
if ($this->config['expires'] > time()) {
|
||||
return;
|
||||
}
|
||||
$this->config = empty($config) ? $this->method_call('help.getConfig') : $config;
|
||||
$this->config = empty($config) ? $this->method_call('help.getConfig', $config, $options) : $config;
|
||||
$this->should_serialize = true;
|
||||
$this->parse_config();
|
||||
}
|
||||
@ -380,15 +449,16 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
||||
$test .= (isset($this->settings['connection'][$test][$ipv6][$id]) && $this->settings['connection'][$test][$ipv6][$id]['ip_address'] != $dc['ip_address']) ? '_bk' : '';
|
||||
$this->settings['connection'][$test][$ipv6][$id] = $dc;
|
||||
}
|
||||
$this->should_serialize = true;
|
||||
}
|
||||
|
||||
public function getV()
|
||||
{
|
||||
return 2;
|
||||
return 4;
|
||||
}
|
||||
|
||||
public function get_self()
|
||||
{
|
||||
return $this->datacenter->authorization['user'];
|
||||
return $this->authorization['user'];
|
||||
}
|
||||
}
|
||||
|
@ -17,31 +17,32 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
trait AckHandler
|
||||
{
|
||||
public function ack_outgoing_message_id($message_id)
|
||||
public function ack_outgoing_message_id($message_id, $datacenter)
|
||||
{
|
||||
// The server acknowledges that it received my message
|
||||
if (!isset($this->datacenter->outgoing_messages[$message_id])) {
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) {
|
||||
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of outgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
|
||||
|
||||
var_dump($message_id);
|
||||
var_dump(debug_backtrace()[0]['file'], debug_backtrace()[0]['line']);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->datacenter->outgoing_messages[$message_id]['ack'] = true;
|
||||
return $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
|
||||
public function ack_incoming_message_id($message_id)
|
||||
public function ack_incoming_message_id($message_id, $datacenter)
|
||||
{
|
||||
// I let the server know that I received its message
|
||||
if (!isset($this->datacenter->incoming_messages[$message_id])) {
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) {
|
||||
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
|
||||
//throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
|
||||
}
|
||||
if ($this->datacenter->temp_auth_key['id'] === null || $this->datacenter->temp_auth_key['id'] === str_repeat(chr(0), 8) || (isset($this->datacenter->incoming_messages[$message_id]['ack']) && $this->datacenter->incoming_messages[$message_id]['ack'])) {
|
||||
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === str_repeat(chr(0), 8) || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->object_call('msgs_ack', ['msg_ids' => [$message_id]]);
|
||||
$this->object_call('msgs_ack', ['msg_ids' => [$message_id]], ['datacenter' => $datacenter]);
|
||||
|
||||
return $this->datacenter->incoming_messages[$message_id]['ack'] = true;
|
||||
return $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ trait AuthKeyHandler
|
||||
{
|
||||
public $dh_config = ['version' => 0];
|
||||
|
||||
public function create_auth_key($expires_in = -1)
|
||||
public function create_auth_key($expires_in, $datacenter)
|
||||
{
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
@ -49,7 +49,8 @@ trait AuthKeyHandler
|
||||
$ResPQ = $this->method_call('req_pq',
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
]
|
||||
],
|
||||
['datacenter' => $datacenter]
|
||||
);
|
||||
|
||||
/*
|
||||
@ -67,16 +68,9 @@ trait AuthKeyHandler
|
||||
if (!isset($this->key->keydata['fp'])) {
|
||||
$this->key = new \danog\MadelineProto\RSA($this->settings['authorization']['rsa_key']);
|
||||
}
|
||||
foreach ($ResPQ['server_public_key_fingerprints'] as $curfp) {
|
||||
if ($this->key->keydata['fp'] === $curfp) {
|
||||
$public_key_fingerprint = $curfp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (in_array($this->key->keydata['fp'], $ResPQ['server_public_key_fingerprints'])) throw new \danog\MadelineProto\SecurityException("Couldn't find our key in the server_public_key_fingerprints vector.");
|
||||
|
||||
if (!isset($public_key_fingerprint)) {
|
||||
throw new \danog\MadelineProto\SecurityException("Couldn't find our key in the server_public_key_fingerprints vector.");
|
||||
}
|
||||
|
||||
|
||||
$pq_bytes = $ResPQ['pq'];
|
||||
$server_nonce = $ResPQ['server_nonce'];
|
||||
@ -85,14 +79,14 @@ trait AuthKeyHandler
|
||||
* ***********************************************************************
|
||||
* Compute p and q
|
||||
*/
|
||||
$pq = \danog\PHP\Struct::unpack('>Q', $pq_bytes)[0];
|
||||
$p = \danog\PrimeModule::auto_single($pq);
|
||||
$q = $pq / $p;
|
||||
if ($p > $q) {
|
||||
$pq = new \phpseclib\Math\BigInteger($pq_bytes, 256);
|
||||
$p = new \phpseclib\Math\BigInteger(\danog\PrimeModule::auto_single($pq->__toString()));
|
||||
$q = $pq->divide($p)[0];
|
||||
if ($p->compare($q) > 0) {
|
||||
list($p, $q) = [$q, $p];
|
||||
}
|
||||
|
||||
if ($pq !== $p * $q) {
|
||||
if (!$pq->equals($p->multiply($q))) {
|
||||
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q.");
|
||||
}
|
||||
|
||||
@ -102,8 +96,8 @@ trait AuthKeyHandler
|
||||
* ***********************************************************************
|
||||
* Serialize object for req_DH_params
|
||||
*/
|
||||
$p_bytes = \danog\PHP\Struct::pack('>I', $p);
|
||||
$q_bytes = \danog\PHP\Struct::pack('>I', $q);
|
||||
$p_bytes = $p->toBytes();
|
||||
$q_bytes = $q->toBytes();
|
||||
|
||||
$new_nonce = $this->random(32);
|
||||
|
||||
@ -154,9 +148,11 @@ trait AuthKeyHandler
|
||||
'server_nonce' => $server_nonce,
|
||||
'p' => $p_bytes,
|
||||
'q' => $q_bytes,
|
||||
'public_key_fingerprint' => $public_key_fingerprint,
|
||||
'public_key_fingerprint' => $this->key->keydata['fp'],
|
||||
'encrypted_data' => $encrypted_data,
|
||||
]
|
||||
],
|
||||
['datacenter' => $datacenter]
|
||||
|
||||
);
|
||||
|
||||
/*
|
||||
@ -213,7 +209,7 @@ trait AuthKeyHandler
|
||||
* int $server_time
|
||||
* ]
|
||||
*/
|
||||
$server_DH_inner_data = $this->deserialize($answer);
|
||||
$server_DH_inner_data = $this->deserialize($answer, ['type' => '']);
|
||||
|
||||
/*
|
||||
* ***********************************************************************
|
||||
@ -241,9 +237,9 @@ trait AuthKeyHandler
|
||||
* Time delta
|
||||
*/
|
||||
$server_time = $server_DH_inner_data['server_time'];
|
||||
$this->datacenter->time_delta = $server_time - time();
|
||||
$this->datacenter->sockets[$datacenter]->time_delta = $server_time - time();
|
||||
|
||||
\danog\MadelineProto\Logger::log([sprintf('Server-client time delta = %.1f s', $this->datacenter->time_delta)], \danog\MadelineProto\Logger::VERBOSE);
|
||||
\danog\MadelineProto\Logger::log([sprintf('Server-client time delta = %.1f s', $this->datacenter->sockets[$datacenter]->time_delta)], \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
$this->check_p_g($dh_prime, $g);
|
||||
$this->check_G($g_a, $dh_prime);
|
||||
@ -324,7 +320,9 @@ trait AuthKeyHandler
|
||||
'nonce' => $nonce,
|
||||
'server_nonce' => $server_nonce,
|
||||
'encrypted_data' => $encrypted_data,
|
||||
]
|
||||
],
|
||||
['datacenter' => $datacenter]
|
||||
|
||||
);
|
||||
|
||||
/*
|
||||
@ -368,7 +366,7 @@ trait AuthKeyHandler
|
||||
|
||||
\danog\MadelineProto\Logger::log(['Diffie Hellman key exchange processed successfully!'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
$res_authorization['server_salt'] = \danog\PHP\Struct::unpack('<q', substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0))[0];
|
||||
$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);
|
||||
|
||||
@ -407,8 +405,8 @@ trait AuthKeyHandler
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
|
||||
} finally {
|
||||
$this->datacenter->new_outgoing = [];
|
||||
$this->datacenter->new_incoming = [];
|
||||
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming = [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -497,7 +495,7 @@ trait AuthKeyHandler
|
||||
public function get_dh_config()
|
||||
{
|
||||
$this->getting_state = true;
|
||||
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0]);
|
||||
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->getting_state = false;
|
||||
if ($dh_config['_'] === 'messages.dhConfigNotModified') {
|
||||
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Logger::VERBOSE, ['DH configuration not modified']);
|
||||
@ -511,13 +509,11 @@ trait AuthKeyHandler
|
||||
return $this->dh_config = $dh_config;
|
||||
}
|
||||
|
||||
private $temp_requested_secret_chats = [];
|
||||
private $secret_chats = [];
|
||||
|
||||
private $temp_requested_calls = [];
|
||||
private $calls = [];
|
||||
|
||||
public function accept_secret_chat($params)
|
||||
|
||||
public function accept_call($params)
|
||||
{
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -525,34 +521,15 @@ trait AuthKeyHandler
|
||||
$params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256);
|
||||
$this->check_G($params['g_a'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
$key['fingerprint'] = \danog\PHP\Struct::unpack('<q', substr(sha1($key['auth_key'], true), -8))[0];
|
||||
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
|
||||
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputEncryptedChat' => ['_' => 'inputEncryptedChat', 'chat_id' => $params['id'], 'access_hash' => $params['access_hash']], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => 8, 'ttl' => PHP_INT_MAX, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0]];
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 65, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0], 'protocol' => $params['protocol'], 'connection' => $params['connection'], 'alternative_connections' => $params['alternative_connections']];
|
||||
//$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputEncryptedChat' => ['_' => 'inputEncryptedChat', 'chat_id' => $params['id'], 'access_hash' => $params['access_hash']], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => 8, 'ttl' => PHP_INT_MAX, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0]];
|
||||
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
|
||||
$this->check_G($g_b, $dh_config['p']);
|
||||
$this->notify_layer($params['id']);
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
|
||||
public function request_secret_chat($user)
|
||||
{
|
||||
$user = $this->get_info($user)['InputUser'];
|
||||
\danog\MadelineProto\Logger::log(['Creating secret chat with '.$user['user_id'].'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
\danog\MadelineProto\Logger::log(['Generating g_a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$g_a = $dh_config['g']->powMod($a, $dh_config['p']);
|
||||
$this->check_G($g_a, $dh_config['p']);
|
||||
$res = $this->method_call('messages.requestEncryption', ['user_id' => $user, 'g_a' => $g_a->toBytes()]);
|
||||
$this->temp_requested_secret_chats[$res['id']] = $a;
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
|
||||
return $res['id'];
|
||||
}
|
||||
|
||||
public function request_call($user)
|
||||
{
|
||||
$user = $this->get_info($user)['InputUser'];
|
||||
@ -563,8 +540,8 @@ trait AuthKeyHandler
|
||||
\danog\MadelineProto\Logger::log(['Generating g_a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$g_a = $dh_config['g']->powMod($a, $dh_config['p']);
|
||||
$this->check_G($g_a, $dh_config['p']);
|
||||
// $res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a' => $g_a->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'min_layer' => $this->settings['tl_schema']['layer'], 'max_layer' => $this->settings['tl_schema']['layer']]]);
|
||||
$res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a' => $g_a->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'min_layer' => 65, 'max_layer' => 65, 'udp_reflector' => true]]);
|
||||
// $res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a' => $g_a->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'min_layer' => $this->settings['tl_schema']['layer'], 'max_layer' => $this->settings['tl_schema']['layer']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a' => $g_a->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'min_layer' => 65, 'max_layer' => 65, 'udp_reflector' => true]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->temp_requested_calls[$res['phone_call']['id']] = $a;
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
@ -572,9 +549,11 @@ trait AuthKeyHandler
|
||||
return $res['phone_call']['id'];
|
||||
}
|
||||
|
||||
public function complete_secret_chat($params)
|
||||
|
||||
|
||||
public function complete_call($params)
|
||||
{
|
||||
if ($this->secret_chat_status($params['id']) !== 1) {
|
||||
if ($this->call_status($params['id']) !== 1) {
|
||||
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat '.$params['id']]);
|
||||
|
||||
return false;
|
||||
@ -583,133 +562,24 @@ trait AuthKeyHandler
|
||||
$params['g_a_or_b'] = new \phpseclib\Math\BigInteger($params['g_a_or_b'], 256);
|
||||
$this->check_G($params['g_a_or_b'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_a_or_b']->powMod($this->temp_requested_secret_chats[$params['id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
unset($this->temp_requested_secret_chats[$params['id']]);
|
||||
$key['fingerprint'] = \danog\PHP\Struct::unpack('<q', substr(sha1($key['auth_key'], true), -8))[0];
|
||||
unset($this->temp_requested_calls[$params['id']]);
|
||||
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
|
||||
if ($key['fingerprint'] !== $params['key_fingerprint']) {
|
||||
$this->method_call('messages.discardEncryption', ['chat_id' => $params['id']]);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputEncryptedChat' => ['chat_id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputEncryptedChat'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 8, 'ttl' => PHP_INT_MAX, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0]];
|
||||
$this->notify_layer($params['id']);
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputPhoneCall' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 65, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0], 'protocol' => $params['protocol'], 'connection' => $params['connection'], 'alternative_connections' => $params['alternative_connections']];
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
|
||||
public function notify_layer($chat)
|
||||
|
||||
public function call_status($id)
|
||||
{
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]]);
|
||||
}
|
||||
|
||||
private $temp_rekeyed_secret_chats = [];
|
||||
|
||||
public function rekey($chat)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Rekeying secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
\danog\MadelineProto\Logger::log(['Generating g_a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$g_a = $dh_config['g']->powMod($a, $dh_config['p']);
|
||||
$this->check_G($g_a, $dh_config['p']);
|
||||
$e = \danog\PHP\Struct::unpack('<q', $this->random(8))[0];
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionRequestKey', 'g_a' => $g_a->toBytes(), 'exchange_id' => $e]]]);
|
||||
$this->temp_rekeyed_secret_chats[$e] = $a;
|
||||
$this->secret_chats[$chat]['rekeying'] = [1, $e];
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
public function accept_rekey($chat, $params)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
|
||||
$my = $this->temp_rekeyed_secret_chats[$this->secret_chats[$chat]['rekeying'][1]];
|
||||
if ($my['exchange_id'] > $params['exchange_id']) {
|
||||
return;
|
||||
}
|
||||
if ($my['exchange_id'] === $params['exchange_id']) {
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
$this->rekey($chat);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Accepting rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
$params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256);
|
||||
$this->check_G($params['g_a'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
$key['fingerprint'] = \danog\PHP\Struct::unpack('<q', substr(sha1($key['auth_key'], true), -8))[0];
|
||||
$key['visualization_orig'] = $this->secret_chats[$chat]['key']['visualization_orig'];
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->temp_rekeyed_secret_chats[$params['exchange_id']] = $key;
|
||||
$this->secret_chats[$chat]['rekeying'] = [2, $params['exchange_id']];
|
||||
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
|
||||
$this->check_G($g_b, $dh_config['p']);
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAcceptKey', 'g_b' => $g_b->toBytes(), 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]]);
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
|
||||
public function commit_rekey($chat, $params)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 1) {
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Committing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
$params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256);
|
||||
$this->check_G($params['g_b'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_b']->powMod($this->temp_rekeyed_secret_chats[$params['exchange_id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
$key['fingerprint'] = \danog\PHP\Struct::unpack('<q', substr(sha1($key['auth_key'], true), -8))[0];
|
||||
$key['visualization_orig'] = $this->secret_chats[$chat]['key']['visualization_orig'];
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
if ($key['fingerprint'] !== $params['key_fingerprint']) {
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]]);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionCommitKey', 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]]);
|
||||
unset($this->temp_rekeyed_secret_chats[$chat]);
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
$this->secret_chats[$chat]['key'] = $key;
|
||||
$this->secret_chats[$chat]['ttr'] = 100;
|
||||
$this->secret_chats[$chat]['updated'] = time();
|
||||
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
|
||||
public function complete_rekey($chat, $params)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 2) {
|
||||
return;
|
||||
}
|
||||
if ($this->temp_rekeyed_secret_chats['fingerprint'] !== $params['key_fingerprint']) {
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]]);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Completing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
$this->secret_chats[$chat]['key'] = $this->temp_rekeyed_secret_chats;
|
||||
$this->secret_chats[$chat]['ttr'] = 100;
|
||||
$this->secret_chats[$chat]['updated'] = time();
|
||||
unset($this->temp_rekeyed_secret_chats[$params['exchange_id']]);
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNoop']]]);
|
||||
}
|
||||
|
||||
public function secret_chat_status($chat)
|
||||
{
|
||||
if (isset($this->secret_chats[$chat])) {
|
||||
if (isset($this->calls[$id])) {
|
||||
return 2;
|
||||
}
|
||||
if (isset($this->temp_requested_secret_chats[$chat])) {
|
||||
if (isset($this->temp_requested_calls[$id])) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -721,16 +591,16 @@ trait AuthKeyHandler
|
||||
return $this->secret_chats[$chat];
|
||||
}
|
||||
|
||||
public function bind_temp_auth_key($expires_in)
|
||||
public function bind_temp_auth_key($expires_in, $datacenter)
|
||||
{
|
||||
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log(['Binding authorization keys...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$nonce = \danog\PHP\Struct::unpack('<q', $this->random(8))[0];
|
||||
$nonce = $this->random(8);
|
||||
$expires_at = time() + $expires_in;
|
||||
$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];
|
||||
$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;
|
||||
$message_data = $this->serialize_object(['type' => 'bind_auth_key_inner'],
|
||||
[
|
||||
'nonce' => $nonce,
|
||||
@ -740,16 +610,15 @@ trait AuthKeyHandler
|
||||
'expires_at' => $expires_at,
|
||||
]
|
||||
);
|
||||
$int_message_id = $this->generate_message_id();
|
||||
$message_id = $this->generate_message_id($datacenter);
|
||||
|
||||
$message_id = \danog\PHP\Struct::pack('<Q', $int_message_id);
|
||||
$seq_no = 0;
|
||||
$encrypted_data = $this->random(16).$message_id.\danog\PHP\Struct::pack('<II', $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->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);
|
||||
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['message_id' => $int_message_id]);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);
|
||||
$encrypted_message = $this->datacenter->sockets[$datacenter]->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
|
||||
$res = $this->method_call('auth.bindTempAuthKey', ['perm_auth_key_id' => $perm_auth_key_id, 'nonce' => $nonce, 'expires_at' => $expires_at, 'encrypted_message' => $encrypted_message], ['message_id' => $message_id, 'datacenter' => $datacenter]);
|
||||
if ($res === true) {
|
||||
\danog\MadelineProto\Logger::log(['Successfully binded temporary and permanent authorization keys.'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
@ -762,8 +631,8 @@ trait AuthKeyHandler
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
|
||||
} finally {
|
||||
$this->datacenter->new_outgoing = [];
|
||||
$this->datacenter->new_incoming = [];
|
||||
$this->datacenter->sockets[$datacenter]->new_outgoing = [];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming = [];
|
||||
}
|
||||
}
|
||||
throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.');
|
||||
|
@ -25,9 +25,11 @@ trait CallHandler
|
||||
if (!is_array($aargs)) {
|
||||
throw new \danog\MadelineProto\Exception("Additonal arguments aren't an array.");
|
||||
}
|
||||
if (!isset($aargs['datacenter'])) throw new \danog\MadelineProto\Exception("No datacenter provided");
|
||||
$args = $this->botAPI_to_MTProto($args);
|
||||
$serialized = $this->serialize_method($method, $args);
|
||||
$content_related = $this->content_related($method);
|
||||
$type = $this->methods->find_by_method($method)['type'];
|
||||
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -36,93 +38,73 @@ trait CallHandler
|
||||
if ($method === 'http_wait') {
|
||||
return true;
|
||||
}
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$this->datacenter->new_outgoing[$int_message_id] = ['msg_id' => $int_message_id, 'method' => $method, 'type' => $this->methods->find_by_method($method)['type']];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id] = ['msg_id' => $int_message_id, 'method' => $method, 'type' => $type];
|
||||
$res_count = 0;
|
||||
$server_answer = null;
|
||||
$update_count = 0;
|
||||
while ($server_answer === null && $res_count++ < $this->settings['max_tries']['response']) { // Loop until we get a response, loop for a max of $this->settings['max_tries']['response'] times
|
||||
$only_updates = false;
|
||||
while ($server_answer === null && $res_count++ < $this->settings['max_tries']['response']+1) { // Loop until we get a response, loop for a max of $this->settings['max_tries']['response'] times
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log(['Getting response (try number '.$res_count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->recv_message(); // This method receives data from the socket, and parses stuff
|
||||
|
||||
if (!isset($this->datacenter->outgoing_messages[$int_message_id]['response']) || !isset($this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$int_message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping
|
||||
if ($this->only_updates) {
|
||||
$this->start_threads();
|
||||
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping
|
||||
if ($only_updates) {
|
||||
if ($update_count > 50) {
|
||||
$update_count = 0;
|
||||
continue;
|
||||
} else {
|
||||
$res_count--;
|
||||
$update_count++;
|
||||
}
|
||||
$res_count--;
|
||||
$update_count++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$server_answer = $this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$int_message_id]['response']]['content']; // continue was not called, so I got a response
|
||||
if (isset($aargs['heavy']) && $aargs['heavy']) {
|
||||
$this->datacenter->incoming_messages[$this->datacenter->outgoing_messages[$int_message_id]['response']]['content'] = [];
|
||||
} else {
|
||||
$server_answer = $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'] = [];
|
||||
break;
|
||||
}
|
||||
//if (!$this->threads && !$this->run_workers) {
|
||||
$this->recv_message($aargs['datacenter']); // This method receives data from the socket, and parses stuff
|
||||
$only_updates = $this->handle_messages($aargs['datacenter']); // This method receives data from the socket, and parses stuff
|
||||
//}
|
||||
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
if ($e->getMessage() === 'I had to recreate the temporary authorization key') {
|
||||
continue 2;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['An error getting response of method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
\danog\MadelineProto\Logger::log(['An error getting response of method '.$method.': '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($server_answer === null) {
|
||||
throw new \danog\MadelineProto\Exception("Couldn't get response");
|
||||
}
|
||||
if (!isset($server_answer['_'])) {
|
||||
\danog\MadelineProto\Logger::log(['Response does not have a type!', $server_answer], \danog\MadelineProto\Logger::FATAL_ERROR);
|
||||
}
|
||||
switch ($server_answer['_']) {
|
||||
case 'rpc_error':
|
||||
switch ($server_answer['error_code']) {
|
||||
case 303:
|
||||
$dc = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
|
||||
\danog\MadelineProto\Logger::log(['Received request to switch to DC '.$dc], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->switch_dc($dc);
|
||||
continue 3;
|
||||
case 401:
|
||||
switch ($server_answer['error_message']) {
|
||||
case 'AUTH_KEY_UNREGISTERED':
|
||||
case 'AUTH_KEY_INVALID':
|
||||
case 'USER_DEACTIVATED':
|
||||
case 'SESSION_REVOKED':
|
||||
case 'SESSION_EXPIRED':
|
||||
unset($this->datacenter->temp_auth_key);
|
||||
unset($this->datacenter->auth_key);
|
||||
$this->datacenter->authorized = false;
|
||||
$this->datacenter->authorization = null;
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
break;
|
||||
}
|
||||
|
||||
case 420:
|
||||
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
|
||||
if (is_numeric($seconds) && isset($this->settings['flood_timeout']['wait_if_lt']) && $seconds < $this->settings['flood_timeout']['wait_if_lt']) {
|
||||
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
sleep($seconds);
|
||||
throw new \danog\MadelineProto\Exception('Re-executing query...');
|
||||
}
|
||||
default:
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
break;
|
||||
}
|
||||
$this->handle_rpc_error($server_answer, $aargs['datacenter']);
|
||||
break;
|
||||
case 'bad_server_salt':
|
||||
case 'bad_msg_notification':
|
||||
switch ($server_answer['error_code']) {
|
||||
case 48:
|
||||
$this->datacenter->temp_auth_key['server_salt'] = $server_answer['new_server_salt'];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'] = $server_answer['new_server_salt'];
|
||||
continue 3;
|
||||
case 16:
|
||||
case 17:
|
||||
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: '.$this->bad_msg_error_codes[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->timedelta = ($this->datacenter->outgoing_messages[$int_message_id]['response'] >> 32) - time();
|
||||
\danog\MadelineProto\Logger::log(['Set time delta to '.$this->datacenter->timedelta], \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->timedelta = (int)((new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString());
|
||||
\danog\MadelineProto\Logger::log(['Set time delta to '.$this->datacenter->sockets[$aargs['datacenter']]->timedelta], \danog\MadelineProto\Logger::WARNING);
|
||||
$this->reset_session();
|
||||
$this->datacenter->temp_auth_key = null;
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
continue 3;
|
||||
}
|
||||
var_dump($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
|
||||
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: '.$this->bad_msg_error_codes[$server_answer['error_code']], $server_answer['error_code']);
|
||||
break;
|
||||
case 'boolTrue':
|
||||
@ -136,22 +118,25 @@ trait CallHandler
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
|
||||
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
|
||||
if (in_array($this->datacenter->protocol, ['http', 'https']) && $method !== 'http_wait') {
|
||||
//$this->method_call('http_wait', ['max_wait' => $this->datacenter->timeout, 'wait_after' => 0, 'max_delay' => 0]);
|
||||
if (in_array($this->datacenter->sockets[$aargs['datacenter']]->protocol, ['http', 'https']) && $method !== 'http_wait') {
|
||||
//$this->method_call('http_wait', ['max_wait' => $this->datacenter->sockets[$aargs['datacenter']]->timeout, 'wait_after' => 0, 'max_delay' => 0], ['datacenter' => $aargs['datacenter']]);
|
||||
} else {
|
||||
$this->datacenter->close_and_reopen();
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->close_and_reopen();
|
||||
}
|
||||
//sleep(1); // To avoid flooding
|
||||
continue;
|
||||
} finally {
|
||||
if (isset($aargs['heavy']) && $aargs['heavy'] && isset($int_message_id)) {
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['args'] = [];
|
||||
if (((isset($aargs['heavy']) && $aargs['heavy']) || $method === 'req_pq') && isset($int_message_id)) {
|
||||
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['args'] = [];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id] = [];
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id]);
|
||||
}
|
||||
}
|
||||
if ($server_answer === null) {
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Got response for method '.$method.' @ try '.$count.' (response try '.$res_count.')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id] = [];
|
||||
|
||||
return $server_answer;
|
||||
}
|
||||
@ -164,15 +149,16 @@ trait CallHandler
|
||||
if (!is_array($args)) {
|
||||
throw new \danog\MadelineProto\Exception("Arguments aren't an array.");
|
||||
}
|
||||
if (!isset($aargs['datacenter'])) throw new \danog\MadelineProto\Exception("No datacenter provided");
|
||||
|
||||
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
|
||||
try {
|
||||
\danog\MadelineProto\Logger::log([$object === 'msgs_ack' ? 'ack '.$args['msg_ids'][0] : 'Sending object (try number '.$count.' for '.$object.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
|
||||
$int_message_id = $this->send_message($this->serialize_object(['type' => $object], $args), $this->content_related($object));
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['content'] = ['method' => $object, 'args' => $args];
|
||||
$int_message_id = $this->send_message($this->serialize_object(['type' => $object], $args), $this->content_related($object), $aargs);
|
||||
if ($object !== 'msgs_ack') $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['content'] = ['method' => $object, 'args' => $args];
|
||||
} catch (Exception $e) {
|
||||
\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...'], \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->close_and_reopen();
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->close_and_reopen();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
trait Files
|
||||
{
|
||||
public function upload($file, $file_name = '', $cb = null, $encrypted = false)
|
||||
public function upload($file, $file_name = '', $cb = null, $encrypted = false, $datacenter = null)
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
throw new \danog\MadelineProto\Exception('Given file does not exist!');
|
||||
@ -25,6 +25,7 @@ trait Files
|
||||
if (empty($file_name)) {
|
||||
$file_name = basename($file);
|
||||
}
|
||||
$datacenter = is_null($datacenter) ? $this->datacenter->curdc : $datacenter;
|
||||
$file_size = filesize($file);
|
||||
if ($file_size > 1500 * 1024 * 1024) {
|
||||
throw new \danog\MadelineProto\Exception('Given file is too big!');
|
||||
@ -39,7 +40,7 @@ trait Files
|
||||
$part_num = 0;
|
||||
$method = $file_size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart';
|
||||
$constructor = 'input'.($encrypted === true ? 'Encrypted' : '').($file_size > 10 * 1024 * 1024 ? 'FileBig' : 'File').($encrypted === true ? 'Uploaded' : '');
|
||||
$file_id = \danog\PHP\Struct::unpack('<q', $this->random(8))[0];
|
||||
$file_id = $this->random(8);
|
||||
$f = fopen($file, 'r');
|
||||
fseek($f, 0);
|
||||
if ($encrypted === true) {
|
||||
@ -57,7 +58,7 @@ trait Files
|
||||
if ($encrypted === true) {
|
||||
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
|
||||
}
|
||||
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true])) {
|
||||
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => $datacenter])) {
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
|
||||
}
|
||||
$cb(ftell($f) * 100 / $file_size);
|
||||
@ -246,9 +247,7 @@ trait Files
|
||||
$size = $end - $offset;
|
||||
$part_size = 512 * 1024;
|
||||
$percent = 0;
|
||||
if (isset($info['InputFileLocation']['dc_id'])) {
|
||||
$this->switch_dc($info['InputFileLocation']['dc_id']);
|
||||
}
|
||||
$datacenter = isset($info['InputFileLocation']['dc_id']) ? $info['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
|
||||
if (isset($info['key'])) {
|
||||
$digest = hash('md5', $info['key'].$info['iv'], true);
|
||||
$fingerprint = \danog\PHP\Struct::unpack('<i', substr($digest, 0, 4) ^ substr($digest, 4, 4))[0];
|
||||
@ -264,7 +263,7 @@ trait Files
|
||||
while (true) {
|
||||
//$real_part_size = (($offset + $part_size > $end) && $end !== -1) ? $part_size - (($offset + $part_size) - $end) : $part_size;
|
||||
try {
|
||||
$res = $this->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true]);
|
||||
$res = $this->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->getMessage() === 'OFFSET_INVALID') {
|
||||
break;
|
||||
@ -274,10 +273,9 @@ trait Files
|
||||
}
|
||||
|
||||
while ($res['type']['_'] === 'storage.fileUnknown' && $res['bytes'] === '') {
|
||||
$dc = 1;
|
||||
$this->switch_dc($dc);
|
||||
$res = $this->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], ['heavy' => true]);
|
||||
$dc++;
|
||||
$datacenter = 1;
|
||||
$res = $this->method_call('upload.getFile', ['location' => $info['InputFileLocation'], 'offset' => $offset, 'limit' => $real_part_size], ['heavy' => true, 'datacenter' => $datacenter]);
|
||||
$datacenter++;
|
||||
}
|
||||
if ($res['bytes'] === '') {
|
||||
break;
|
||||
@ -285,7 +283,7 @@ trait Files
|
||||
if (isset($info['key'])) {
|
||||
$res['bytes'] = $ige->decrypt($res['bytes']);
|
||||
}
|
||||
if ($end !== -1 && strlen($res['bytes']) + $downloaded_size > $size) {
|
||||
if ($end !== -1 && strlen($res['bytes']) + $downloaded_size >= $size) {
|
||||
$res['bytes'] = substr($res['bytes'], 0, (strlen($res['bytes']) + $downloaded_size) - $size);
|
||||
$theend = true;
|
||||
}
|
||||
|
@ -24,47 +24,44 @@ trait MessageHandler
|
||||
public function send_message($message_data, $content_related, $aargs = [])
|
||||
{
|
||||
if (!isset($aargs['message_id']) || $aargs['message_id'] === null) {
|
||||
$int_message_id = $this->generate_message_id();
|
||||
$message_id = $this->generate_message_id($aargs['datacenter']);
|
||||
} else {
|
||||
$int_message_id = $aargs['message_id'];
|
||||
$message_id = $aargs['message_id'];
|
||||
}
|
||||
if (!is_int($int_message_id)) {
|
||||
throw new \danog\MadelineProto\Exception("Specified message id isn't an integer");
|
||||
if (!is_string($message_id)) {
|
||||
throw new \danog\MadelineProto\Exception("Specified message id isn't a string");
|
||||
}
|
||||
|
||||
$message_id = \danog\PHP\Struct::pack('<Q', $int_message_id);
|
||||
if ($this->datacenter->temp_auth_key['auth_key'] === null || $this->datacenter->temp_auth_key['server_salt'] === null) {
|
||||
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key'] === null || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'] === null) {
|
||||
$message = str_repeat(chr(0), 8).$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
|
||||
} else {
|
||||
$seq_no = $this->generate_seq_no($content_related);
|
||||
$data2enc = \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;
|
||||
$seq_no = $this->generate_out_seq_no($aargs['datacenter'], $content_related);
|
||||
$data2enc = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'].$this->datacenter->sockets[$aargs['datacenter']]->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
|
||||
$padding = $this->random($this->posmod(-strlen($data2enc), 16));
|
||||
$message_key = substr(sha1($data2enc, true), -16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key']);
|
||||
$message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($data2enc.$padding, $aes_key, $aes_iv);
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key']);
|
||||
$message = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['id'].$message_key.$this->ige_encrypt($data2enc.$padding, $aes_key, $aes_iv);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['seq_no'] = $seq_no;
|
||||
}
|
||||
$this->datacenter->outgoing_messages[$int_message_id]['response'] = -1;
|
||||
$this->datacenter->send_message($message);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response'] = -1;
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->send_message($message);
|
||||
|
||||
return $int_message_id;
|
||||
return $message_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading connection and receiving message from server.
|
||||
*/
|
||||
public function recv_message()
|
||||
public function recv_message($datacenter)
|
||||
{
|
||||
$payload = $this->datacenter->read_message();
|
||||
$payload = $this->datacenter->sockets[$datacenter]->read_message();
|
||||
if (strlen($payload) === 4) {
|
||||
$error = \danog\PHP\Struct::unpack('<i', $payload)[0];
|
||||
if ($error === -404) {
|
||||
if ($this->datacenter->temp_auth_key != null) {
|
||||
if ($this->datacenter->sockets[$datacenter]->temp_auth_key != null) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
|
||||
$this->datacenter->temp_auth_key = null;
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
$this->init_authorization();
|
||||
$this->config = $this->write_client_info('help.getConfig');
|
||||
$this->parse_config();
|
||||
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
}
|
||||
}
|
||||
@ -72,27 +69,28 @@ trait MessageHandler
|
||||
}
|
||||
$auth_key_id = substr($payload, 0, 8);
|
||||
if ($auth_key_id === str_repeat(chr(0), 8)) {
|
||||
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', substr($payload, 8, 12));
|
||||
$this->check_message_id($message_id, false);
|
||||
$message_id = substr($payload, 8, 8);
|
||||
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
|
||||
$message_length = \danog\PHP\Struct::unpack('<I', substr($payload, 16, 4))[0];
|
||||
$message_data = substr($payload, 20, $message_length);
|
||||
} elseif ($auth_key_id === $this->datacenter->temp_auth_key['id']) {
|
||||
} elseif ($auth_key_id === $this->datacenter->sockets[$datacenter]->temp_auth_key['id']) {
|
||||
$message_key = substr($payload, 8, 16);
|
||||
$encrypted_data = substr($payload, 24);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server');
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$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->datacenter->temp_auth_key['server_salt']) {
|
||||
//\danog\MadelineProto\Logger::log(['WARNING: Server salt mismatch (my server salt '.$this->datacenter->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
/*
|
||||
$server_salt = substr($decrypted_data, 0, 8);
|
||||
if ($server_salt != $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt']) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Server salt mismatch (my server salt '.$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
|
||||
*/
|
||||
$session_id = substr($decrypted_data, 8, 8);
|
||||
if ($session_id != $this->datacenter->session_id) {
|
||||
if ($session_id != $this->datacenter->sockets[$datacenter]->session_id) {
|
||||
throw new \danog\MadelineProto\Exception('Session id mismatch.');
|
||||
}
|
||||
|
||||
$message_id = \danog\PHP\Struct::unpack('<Q', substr($decrypted_data, 16, 8))[0];
|
||||
$this->check_message_id($message_id, false);
|
||||
$message_id = substr($decrypted_data, 16, 8);
|
||||
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
|
||||
|
||||
$seq_no = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 24, 4))[0];
|
||||
// Dunno how to handle any incorrect sequence numbers
|
||||
@ -119,81 +117,13 @@ trait MessageHandler
|
||||
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
|
||||
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
|
||||
}
|
||||
$this->datacenter->incoming_messages[$message_id]['seq_no'] = $seq_no;
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['seq_no'] = $seq_no;
|
||||
} else {
|
||||
throw new \danog\MadelineProto\SecurityException('Got unknown auth_key id');
|
||||
}
|
||||
$deserialized = $this->deserialize($message_data);
|
||||
$this->datacenter->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$this->datacenter->incoming_messages[$message_id]['response'] = -1;
|
||||
$this->datacenter->new_incoming[$message_id] = $message_id;
|
||||
$this->handle_messages();
|
||||
}
|
||||
|
||||
public function encrypt_secret_message($chat_id, $message)
|
||||
{
|
||||
if (!isset($this->secret_chats[$chat_id])) {
|
||||
\danog\MadelineProto\Logger::log('I do not have the secret chat '.$chat_id.' in the database, skipping message...');
|
||||
|
||||
return false;
|
||||
}
|
||||
$message = $this->serialize_object(['type' => $message['_']], $message, $this->secret_chats[$chat_id]['layer']);
|
||||
$this->secret_chats[$chat_id]['outgoing'][] = $message;
|
||||
$this->secret_chats[$chat_id]['ttr']--;
|
||||
if (($this->secret_chats[$chat_id]['ttr'] <= 0 || time() - $this->secret_chats[$chat_id]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$chat_id]['rekeying'] === 0) {
|
||||
$this->rekey($chat_id);
|
||||
}
|
||||
|
||||
$message = \danog\PHP\Struct::pack('<I', strlen($message)).$message;
|
||||
$message_key = substr(sha1($message, true), -16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server');
|
||||
$padding = $this->random($this->posmod(-strlen($message), 16));
|
||||
$message = $this->secret_chats[$chat_id]['key']['fingerprint'].$message_key.$this->ige_encrypt($message.$padding, $aes_key, $aes_iv);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function handle_encrypted_update($message)
|
||||
{
|
||||
if (!isset($this->secret_chats[$message['message']['chat_id']])) {
|
||||
\danog\MadelineProto\Logger::log('I do not have the secret chat '.$message['message']['chat_id'].' in the database, skipping message...');
|
||||
|
||||
return false;
|
||||
}
|
||||
$auth_key_id = \danog\PHP\Struct::unpack('<q', substr($message['message']['bytes'], 0, 8))[0];
|
||||
if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['key']['fingerprint']) {
|
||||
throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch');
|
||||
}
|
||||
$message_key = substr($message['message']['bytes'], 8, 16);
|
||||
$encrypted_data = substr($message['message']['bytes'], 24);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$message['message']['chat_id']]['key']['auth_key'], 'to server');
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 0, 4))[0];
|
||||
if ($message_data_length > strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
|
||||
}
|
||||
|
||||
if ((strlen($decrypted_data) - 4) - $message_data_length > 15) {
|
||||
throw new \danog\MadelineProto\SecurityException('difference between message_data_length and the length of the remaining decrypted buffer is too big');
|
||||
}
|
||||
|
||||
if ($message_data_length % 4 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
|
||||
}
|
||||
$message_data = substr($decrypted_data, 4, $message_data_length);
|
||||
if ($message_key != substr(sha1(substr($decrypted_data, 0, 4 + $message_data_length), true), -16)) {
|
||||
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
|
||||
}
|
||||
$deserialized = $this->deserialize($message_data);
|
||||
if (strlen($deserialized['random_bytes']) < 15) {
|
||||
throw new \danog\MadelineProto\SecurityException('random_bytes is too short');
|
||||
}
|
||||
$this->secret_chats[$message['message']['chat_id']]['ttr']--;
|
||||
if (($this->secret_chats[$message['message']['chat_id']]['ttr'] <= 0 || time() - $this->secret_chats[$message['message']['chat_id']]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$message['message']['chat_id']]['rekeying'] === 0) {
|
||||
$this->rekey($message['message']['chat_id']);
|
||||
}
|
||||
unset($message['message']['bytes']);
|
||||
$message['message']['decrypted_message'] = $deserialized;
|
||||
$this->handle_decrypted_update($message);
|
||||
$deserialized = $this->deserialize($message_data, ['type' => '']);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1;
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id;
|
||||
}
|
||||
}
|
||||
|
@ -17,61 +17,60 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
trait MsgIdHandler
|
||||
{
|
||||
public function check_message_id($new_message_id, $outgoing, $container = false)
|
||||
public function check_message_id($new_message_id, $aargs)
|
||||
{
|
||||
$min_message_id = ((int) ((time() + $this->datacenter->time_delta - 300) << 32));
|
||||
if ($min_message_id > $new_message_id) {
|
||||
if (!is_object($new_message_id)) {
|
||||
$new_message_id = new \phpseclib\Math\BigInteger(strrev($new_message_id), 256);
|
||||
}
|
||||
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32);
|
||||
if ($min_message_id->compare($new_message_id) > 0) {
|
||||
\danog\MadelineProto\Logger::log(['Given message id ('.$new_message_id.') is too old compared to the min value ('.$min_message_id.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
/*
|
||||
if (((int) ((time() + $this->datacenter->time_delta + 30) << 32)) < $new_message_id) {
|
||||
if (((int) ((time() + $this->datacenter->sockets[$datacenter]->time_delta + 30) << 32)) < $new_message_id) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too new.');
|
||||
}
|
||||
*/
|
||||
if ($outgoing) {
|
||||
if ($new_message_id % 4 != 0) {
|
||||
if ($aargs['outgoing']) {
|
||||
if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4.');
|
||||
}
|
||||
$keys = array_keys($this->datacenter->outgoing_messages);
|
||||
asort($keys);
|
||||
if ($new_message_id <= end($keys)) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.end($keys).').', 1);
|
||||
$keys = array_keys($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
|
||||
$key = $this->get_max_id($aargs['datacenter'], false);
|
||||
if ($new_message_id->compare($key) <= 0) {
|
||||
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$key.').', 1);
|
||||
}
|
||||
if (count($this->datacenter->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
|
||||
reset($this->datacenter->outgoing_messages);
|
||||
unset($this->datacenter->outgoing_messages[key($this->datacenter->outgoing_messages)]);
|
||||
if (count($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
|
||||
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[key($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages)]);
|
||||
}
|
||||
$this->datacenter->outgoing_messages[$new_message_id] = [];
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[strrev($new_message_id->toBytes())] = [];
|
||||
} else {
|
||||
if ($new_message_id % 4 != 1 && $new_message_id % 4 != 3) {
|
||||
if (!$new_message_id->divide($this->four)[1]->equals($this->one) && !$new_message_id->divide($this->four)[1]->equals($this->three)) {
|
||||
throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3');
|
||||
}
|
||||
$keys = array_keys($this->datacenter->incoming_messages);
|
||||
if ($container) {
|
||||
asort($keys);
|
||||
if ($new_message_id >= end($keys)) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is bigger than or equal than the current limit ('.end($keys).').'], \danog\MadelineProto\Logger::WARNING);
|
||||
$key = $this->get_max_id($aargs['datacenter'], true);
|
||||
if ($aargs['container']) {
|
||||
if ($new_message_id->compare($key) >= 0) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is bigger than or equal than the current limit ('.$key.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
} else {
|
||||
asort($keys);
|
||||
foreach ($keys as $message_id) {
|
||||
if ($new_message_id <= $message_id) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$message_id.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
if ($new_message_id->compare($key) <= 0) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$message_id.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
}
|
||||
if (count($this->datacenter->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
|
||||
reset($this->datacenter->incoming_messages);
|
||||
unset($this->datacenter->incoming_messages[key($this->datacenter->incoming_messages)]);
|
||||
|
||||
if (count($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
|
||||
reset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
|
||||
unset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[key($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages)]);
|
||||
}
|
||||
$this->datacenter->incoming_messages[$new_message_id] = [];
|
||||
ksort($this->datacenter->incoming_messages);
|
||||
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = [];
|
||||
}
|
||||
}
|
||||
|
||||
public function generate_message_id()
|
||||
public function generate_message_id($datacenter)
|
||||
{
|
||||
$int_message_id = (int) ((time() + $this->datacenter->time_delta) << 32);
|
||||
$message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta))->bitwise_leftShift(32);
|
||||
/*
|
||||
$int_message_id = (int) (
|
||||
((int) ($ms_time / 1000) << 32) |
|
||||
@ -79,15 +78,18 @@ trait MsgIdHandler
|
||||
rand(0, 524288) << 2
|
||||
);
|
||||
*/
|
||||
|
||||
$keys = array_keys($this->datacenter->outgoing_messages);
|
||||
asort($keys);
|
||||
$keys = end($keys);
|
||||
if ($int_message_id <= $keys) {
|
||||
$int_message_id = $keys + 4;
|
||||
$key = $this->get_max_id($datacenter, false);
|
||||
if ($message_id->compare($key) <= 0) {
|
||||
$message_id = $key->add($this->four);
|
||||
}
|
||||
$this->check_message_id($int_message_id, true);
|
||||
$this->check_message_id($message_id, ['outgoing' => true, 'datacenter' => $datacenter, 'container' => false]);
|
||||
|
||||
return $int_message_id;
|
||||
return strrev($message_id->toBytes());
|
||||
}
|
||||
public function get_max_id($datacenter, $incoming) {
|
||||
$keys = array_keys($this->datacenter->sockets[$datacenter]->{$incoming ? 'incoming_messages' : 'outgoing_messages'});
|
||||
if (empty($keys)) return $this->zero;
|
||||
array_walk($keys, function (&$value, $key) { $value = is_integer($value) ? new \phpseclib\Math\BigInteger($value) : new \phpseclib\Math\BigInteger(strrev($value), 256); });
|
||||
return \phpseclib\Math\BigInteger::max(...$keys);
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ trait PeerHandler
|
||||
switch ($id['_']) {
|
||||
case 'inputUserSelf':
|
||||
case 'inputPeerSelf':
|
||||
$id = $this->datacenter->authorization['user']['id'];
|
||||
$id = $this->authorization['user']['id'];
|
||||
break;
|
||||
case 'user':
|
||||
$id = $id['id'];
|
||||
@ -197,7 +197,7 @@ trait PeerHandler
|
||||
return $this->gen_all($this->chats[$id]);
|
||||
}
|
||||
if ($id < 0 && !preg_match('/^-100/', $id)) {
|
||||
$this->method_call('messages.getFullChat', ['chat_id' => -$id]);
|
||||
$this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]);
|
||||
if (isset($this->chats[$id])) {
|
||||
return $this->gen_all($this->chats[$id]);
|
||||
}
|
||||
@ -280,16 +280,16 @@ trait PeerHandler
|
||||
switch ($partial['type']) {
|
||||
case 'user':
|
||||
case 'bot':
|
||||
$full = $this->method_call('users.getFullUser', ['id' => $partial['InputUser']]);
|
||||
$full = $this->method_call('users.getFullUser', ['id' => $partial['InputUser']], ['datacenter' => $this->datacenter->curdc]);
|
||||
break;
|
||||
|
||||
case 'chat':
|
||||
$full = $this->method_call('messages.getFullChat', $partial)['full_chat'];
|
||||
$full = $this->method_call('messages.getFullChat', $partial, ['datacenter' => $this->datacenter->curdc])['full_chat'];
|
||||
break;
|
||||
|
||||
case 'channel':
|
||||
case 'supergroup':
|
||||
$full = $this->method_call('channels.getFullChannel', ['channel' => $partial['InputChannel']])['full_chat'];
|
||||
$full = $this->method_call('channels.getFullChannel', ['channel' => $partial['InputChannel']], ['datacenter' => $this->datacenter->curdc])['full_chat'];
|
||||
break;
|
||||
}
|
||||
$partial['full'] = $full;
|
||||
@ -403,7 +403,7 @@ trait PeerHandler
|
||||
$res['participants'] = [];
|
||||
$limit = 400;
|
||||
$offset = -$limit;
|
||||
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => 200]);
|
||||
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => 200], ['datacenter' => $this->datacenter->curdc]);
|
||||
$count = $gres['count'];
|
||||
$key = -1;
|
||||
while ($offset <= $count) {
|
||||
@ -441,7 +441,7 @@ trait PeerHandler
|
||||
}
|
||||
$res['participants'][$key] = $newres;
|
||||
}
|
||||
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => $limit]);
|
||||
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => 'channelParticipantsRecent'], 'offset' => $offset += $limit, 'limit' => $limit], ['datacenter' => $this->datacenter->curdc]);
|
||||
if (empty($gres['participants'])) {
|
||||
break;
|
||||
}
|
||||
@ -484,7 +484,7 @@ trait PeerHandler
|
||||
$payload = json_encode($this->qres);
|
||||
$path = '/tmp/ids'.hash('sha256', $payload);
|
||||
file_put_contents($path, $payload);
|
||||
$id = isset($this->datacenter->authorization['user']['username']) ? $this->datacenter->authorization['user']['username'] : $this->datacenter->authorization['user']['id'];
|
||||
$id = isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'];
|
||||
$result = shell_exec('curl '.escapeshellarg('https://id.pwrtelegram.xyz/db'.$this->settings['pwr']['db_token'].'/addnewmadeline?d=pls&from='.$id).' -d '.escapeshellarg('@'.$path).' -s -o '.escapeshellarg($path.'.log').' >/dev/null 2>/dev/null & ');
|
||||
\danog\MadelineProto\Logger::log([$result], \danog\MadelineProto\Logger::VERBOSE);
|
||||
} catch (\danog\MadelineProto\Exception $e) {
|
||||
@ -496,7 +496,7 @@ trait PeerHandler
|
||||
|
||||
public function resolve_username($username)
|
||||
{
|
||||
$res = $this->method_call('contacts.resolveUsername', ['username' => str_replace('@', '', $username)]);
|
||||
$res = $this->method_call('contacts.resolveUsername', ['username' => str_replace('@', '', $username)], ['datacenter' => $this->datacenter->curdc]);
|
||||
if ($res['_'] === 'contacts.resolvedPeer') {
|
||||
return $res;
|
||||
}
|
||||
|
@ -43,129 +43,147 @@ trait ResponseHandler
|
||||
128 => ' and other party knows for a fact that message is already received',
|
||||
];
|
||||
|
||||
public function send_msgs_state_info($req_msg_id, $msg_ids)
|
||||
public function send_msgs_state_info($req_msg_id, $msg_ids, $datacenter)
|
||||
{
|
||||
$info = '';
|
||||
foreach ($msg_ids as $msg_id) {
|
||||
$cur_info = 0;
|
||||
if (!in_array($msg_id, $this->datacenter->incoming_messages)) {
|
||||
if (((int) ((time() + $this->datacenter->time_delta + 30) << 32)) < $msg_id) {
|
||||
if (!in_array($msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages)) {
|
||||
|
||||
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
|
||||
if ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) {
|
||||
$cur_info |= 3;
|
||||
} elseif (((int) ((time() + $this->datacenter->time_delta - 300) << 32)) > $msg_id) {
|
||||
} elseif ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) {
|
||||
$cur_info |= 1;
|
||||
} else {
|
||||
$cur_info |= 2;
|
||||
}
|
||||
} else {
|
||||
$cur_info |= 4;
|
||||
if ($this->datacenter->incoming_messages[$msg_id]['ack']) {
|
||||
if ($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['ack']) {
|
||||
$cur_info |= 8;
|
||||
}
|
||||
}
|
||||
$info .= chr($cur_info);
|
||||
}
|
||||
$this->datacenter->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info])]['response'] = $req_msg_id;
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id;
|
||||
}
|
||||
|
||||
public function handle_messages()
|
||||
public function handle_messages($datacenter)
|
||||
{
|
||||
foreach ($this->datacenter->new_incoming as $current_msg_id) {
|
||||
$this->only_updates = false;
|
||||
$response = $this->datacenter->incoming_messages[$current_msg_id]['content'];
|
||||
\danog\MadelineProto\Logger::log(['Received '.$response['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$only_updates = false;
|
||||
$first = true;
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
|
||||
$last_was_updates = false;
|
||||
$unset = false;
|
||||
\danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
|
||||
switch ($response['_']) {
|
||||
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
|
||||
case 'msgs_ack':
|
||||
foreach ($response['msg_ids'] as $msg_id) {
|
||||
$this->ack_outgoing_message_id($msg_id); // Acknowledge that the server received my message
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
$this->ack_outgoing_message_id($msg_id, $datacenter); // Acknowledge that the server received my message
|
||||
}
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
break;
|
||||
|
||||
case 'rpc_result':
|
||||
$this->ack_incoming_message_id($current_msg_id); // Acknowledge that I received the server's response
|
||||
$this->datacenter->incoming_messages[$current_msg_id]['content'] = $response['result'];
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result'];
|
||||
break;
|
||||
case 'future_salts':
|
||||
$this->ack_outgoing_message_id($response['req_msg_id']); // Acknowledge that the server received my request
|
||||
$this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->new_outgoing[$response['req_msg_id']]);
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
break;
|
||||
case 'rpc_error':
|
||||
$this->handle_rpc_error($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], $datacenter);
|
||||
break;
|
||||
|
||||
case 'bad_server_salt':
|
||||
case 'bad_msg_notification':
|
||||
$this->ack_outgoing_message_id($response['bad_msg_id']); // Acknowledge that the server received my request
|
||||
$this->datacenter->outgoing_messages[$response['bad_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->new_outgoing[$response['bad_msg_id']]);
|
||||
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id'], $datacenter); // Acknowledge that the server received my request
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id']]);
|
||||
break;
|
||||
|
||||
case 'pong':
|
||||
foreach ($this->datacenter->outgoing_messages as $msg_id => &$omessage) {
|
||||
if (isset($omessage['content']['args']['ping_id']) && $omessage['content']['args']['ping_id'] === $response['ping_id']) {
|
||||
$this->ack_outgoing_message_id($msg_id);
|
||||
$omessage['response'] = $response['msg_id'];
|
||||
$this->datacenter->incoming_messages[$response['msg_id']]['content'] = $response;
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->new_outgoing[$msg_id]);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->outgoing_messages as $msg_id => &$omessage) {
|
||||
if (isset($omessage['content']['args']['ping_id']) && $omessage['content']['args']['ping_id'] === $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['ping_id']) {
|
||||
$this->ack_outgoing_message_id($msg_id, $datacenter);
|
||||
$omessage['response'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id'];
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'];
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$msg_id]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'new_session_created':
|
||||
$this->datacenter->temp_auth_key['server_salt'] = $response['server_salt'];
|
||||
$this->ack_incoming_message_id($current_msg_id); // Acknowledge that I received the server's response
|
||||
if ($this->datacenter->authorized) {
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['server_salt'];
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
|
||||
if ($this->authorized) {
|
||||
$this->force_get_updates_difference();
|
||||
}
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
break;
|
||||
case 'msg_container':
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
foreach ($response['messages'] as $message) {
|
||||
$this->check_message_id($message['msg_id'], false, true);
|
||||
$this->datacenter->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']];
|
||||
$this->datacenter->new_incoming[$message['msg_id']] = $message['msg_id'];
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['messages'] as $message) {
|
||||
$this->check_message_id($message['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['msg_id']] = ['seq_no' => $message['seqno'], 'content' => $message['body']];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message['msg_id']] = $message['msg_id'];
|
||||
|
||||
$this->handle_messages();
|
||||
$this->handle_messages($datacenter);
|
||||
}
|
||||
$unset = true;
|
||||
break;
|
||||
case 'msg_copy':
|
||||
$this->ack_incoming_message_id($current_msg_id); // Acknowledge that I received the server's response
|
||||
if (isset($this->datacenter->incoming_messages[$response['orig_message']['msg_id']])) {
|
||||
$this->ack_incoming_message_id($response['orig_message']['msg_id']); // Acknowledge that I received the server's response
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id']])) {
|
||||
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id'], $datacenter); // Acknowledge that I received the server's response
|
||||
} else {
|
||||
$this->check_message_id($message['orig_message']['msg_id'], false, true);
|
||||
$this->datacenter->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $response['orig_message']];
|
||||
$this->datacenter->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id'];
|
||||
$this->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]);
|
||||
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']];
|
||||
$this->datacenter->sockets[$datacenter]->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id'];
|
||||
|
||||
$this->handle_messages();
|
||||
$this->handle_messages($datacenter);
|
||||
}
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
break;
|
||||
case 'http_wait':
|
||||
|
||||
\danog\MadelineProto\Logger::log([$response], \danog\MadelineProto\Logger::NOTICE);
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
\danog\MadelineProto\Logger::log([$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']], \danog\MadelineProto\Logger::NOTICE);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
break;
|
||||
case 'msgs_state_info':
|
||||
$this->datacenter->outgoing_messages[$response['req_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->new_outgoing[$response['req_msg_id']]);
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
|
||||
$unset = true;
|
||||
break;
|
||||
case 'msgs_state_req':
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
$this->send_msgs_state_info($current_msg_id, $response['msg_ids']);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->send_msgs_state_info($current_msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter);
|
||||
break;
|
||||
case 'msgs_all_info':
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
|
||||
foreach ($response['msg_ids'] as $key => $msg_id) {
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
|
||||
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
|
||||
$status = 'Status for message id '.$msg_id.': ';
|
||||
if (($response['info'][$key] & 4) === 1) {
|
||||
$this->ack_outgoing_message_id($msg_id);
|
||||
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) === 1) {
|
||||
$this->ack_outgoing_message_id($msg_id, $datacenter);
|
||||
}
|
||||
foreach ($this->msgs_info_flags as $flag => $description) {
|
||||
if (($response['info'][$key] & $flag) === 1) {
|
||||
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & $flag) === 1) {
|
||||
$status .= $description;
|
||||
}
|
||||
}
|
||||
@ -173,94 +191,140 @@ trait ResponseHandler
|
||||
}
|
||||
break;
|
||||
case 'msg_detailed_info':
|
||||
if (isset($this->datacenter->outgoing_messages[$response['msg_id']])) {
|
||||
if (isset($this->datacenter->incoming_messages[$response['answer_msg_id']])) {
|
||||
$this->datacenter->outgoing_messages[$response['msg_id']]['response'] = $response['answer_msg_id'];
|
||||
unset($this->datacenter->new_outgoing[$response['msg_id']]);
|
||||
if (isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']])) {
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]['response'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id'];
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]);
|
||||
}
|
||||
}
|
||||
case 'msg_new_detailed_info':
|
||||
if (isset($this->datacenter->incoming_messages[$response['answer_msg_id']])) {
|
||||
$this->ack_incoming_message_id($response['answer_msg_id']);
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id']])) {
|
||||
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['answer_msg_id'], $datacenter);
|
||||
}
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
break;
|
||||
case 'msg_resend_req':
|
||||
$ok = true;
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
foreach ($response['msg_ids'] as $msg_id) {
|
||||
if (!isset($this->datacenter->outgoing_messages[$msg_id]) || isset($this->datacenter->incoming_messages[$msg_id])) {
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$msg_id]) || isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id])) {
|
||||
$ok = false;
|
||||
}
|
||||
}
|
||||
if ($ok) {
|
||||
foreach ($response['msg_ids'] as $msg_id) {
|
||||
$this->object_call($this->datacenter->outgoing_messages[$msg_id]['content']['method'], $this->datacenter->outgoing_messages[$msg_id]['content']['args']);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
$this->object_call($this->datacenter->sockets[$datacenter]->outgoing_messages[$msg_id]['content']['method'], $this->datacenter->sockets[$datacenter]->outgoing_messages[$msg_id]['content']['args'], ['datacenter' => $datacenter]);
|
||||
}
|
||||
} else {
|
||||
$this->send_msgs_state_info($current_msg_id, $response['msg_ids']);
|
||||
$this->send_msgs_state_info($current_msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter);
|
||||
}
|
||||
break;
|
||||
case 'msg_resend_ans_req':
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
$this->send_msgs_state_info($response['msg_ids']);
|
||||
foreach ($response['msg_ids'] as $msg_id) {
|
||||
if (isset($this->datacenter->incoming_messages[$msg_id]) && isset($this->datacenter->outgoing_messages[$this->datacenter->incoming_messages[$msg_id]['response']])) {
|
||||
$this->object_call($this->datacenter->outgoing_messages[$this->datacenter->incoming_messages[$msg_id]['response']]['method'], $this->datacenter->outgoing_messages[$this->datacenter->incoming_messages[$msg_id]['response']]['args']);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$this->send_msgs_state_info($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'], $datacenter);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]) && isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']])) {
|
||||
$this->object_call($this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']]['method'], $this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['response']]['args'], ['datacenter' => $datacenter]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$this->ack_incoming_message_id($current_msg_id); // Acknowledge that I received the server's response
|
||||
$response_type = $this->constructors->find_by_predicate($response['_'])['type'];
|
||||
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
|
||||
$response_type = $this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'])['type'];
|
||||
switch ($response_type) {
|
||||
case 'Updates':
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
$this->handle_updates($response);
|
||||
$this->only_updates = true;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
$unset = true;
|
||||
$this->handle_updates($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']);
|
||||
$last_was_updates = true;
|
||||
break;
|
||||
default:
|
||||
\danog\MadelineProto\Logger::log(['Trying to assign a response of type '.$response_type.' to its request...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
foreach ($this->datacenter->new_outgoing as $key => $expecting) {
|
||||
\danog\MadelineProto\Logger::log(['Does the request of return type '.$expecting['type'].' and msg_id '.$expecting['msg_id'].' match?'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $key => $expecting) {
|
||||
\danog\MadelineProto\Logger::log(['Does the request of return type '.$expecting['type'].' match?'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
if ($response_type === $expecting['type']) {
|
||||
\danog\MadelineProto\Logger::log(['Yes'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->datacenter->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->new_outgoing[$key]);
|
||||
unset($this->datacenter->new_incoming[$current_msg_id]);
|
||||
$this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
|
||||
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$key]);
|
||||
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
|
||||
|
||||
return;
|
||||
break 2;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['No'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
}
|
||||
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($response, true));
|
||||
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], true));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (isset($response['users'])) {
|
||||
$this->add_users($response['users']);
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users'])) {
|
||||
$this->add_users($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['users']);
|
||||
}
|
||||
if (isset($response['chats'])) {
|
||||
$this->add_chats($response['chats']);
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats'])) {
|
||||
$this->add_chats($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['chats']);
|
||||
}
|
||||
if (isset($response['result']['users'])) {
|
||||
$this->add_users($response['result']['users']);
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['users'])) {
|
||||
$this->add_users($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['users']);
|
||||
}
|
||||
if (isset($response['result']['chats'])) {
|
||||
$this->add_chats($response['result']['chats']);
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['chats'])) {
|
||||
$this->add_chats($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['chats']);
|
||||
}
|
||||
if (isset($response['result']['_'])) {
|
||||
switch ($this->constructors->find_by_predicate($response['result']['_'])['type']) {
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['_'])) {
|
||||
switch ($this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['_'])['type']) {
|
||||
case 'Update':
|
||||
$this->handle_update($response['result']);
|
||||
$this->handle_update($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
$only_updates = ($only_updates || $first) && $last_was_updates;
|
||||
$first = false;
|
||||
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) && ($seq_no = $this->generate_in_seq_no($datacenter, $this->content_related($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']))) !== $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) {
|
||||
throw new \danog\MadelineProto\SecurityException('Seqno mismatch (should be '.$seq_no.', is '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']);
|
||||
}
|
||||
if ($unset) {
|
||||
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
|
||||
}
|
||||
}
|
||||
return $only_updates;
|
||||
}
|
||||
public function handle_rpc_error($server_answer, &$datacenter) {
|
||||
|
||||
switch ($server_answer['error_code']) {
|
||||
case 303:
|
||||
$this->datacenter->curdc = $datacenter = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
|
||||
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
|
||||
|
||||
case 401:
|
||||
switch ($server_answer['error_message']) {
|
||||
case 'USER_DEACTIVATED':
|
||||
case 'SESSION_REVOKED':
|
||||
case 'SESSION_EXPIRED':
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
$this->datacenter->sockets[$datacenter]->auth_key = null;
|
||||
$this->authorized = false;
|
||||
$this->authorization = null;
|
||||
$this->init_authorization(); // idk
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
case 'AUTH_KEY_UNREGISTERED':
|
||||
case 'AUTH_KEY_INVALID':
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key = null;
|
||||
$this->init_authorization(); // idk
|
||||
throw new \danog\MadelineProto\Exception($server_answer['error_message'], $server_answer['error_code']);
|
||||
}
|
||||
|
||||
case 420:
|
||||
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
|
||||
if (is_numeric($seconds) && isset($this->settings['flood_timeout']['wait_if_lt']) && $seconds < $this->settings['flood_timeout']['wait_if_lt']) {
|
||||
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
sleep($seconds);
|
||||
throw new \danog\MadelineProto\Exception('Re-executing query...');
|
||||
}
|
||||
default:
|
||||
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function handle_pending_updates()
|
||||
{
|
||||
\danog\MadelineProto\Logger::log(['Parsing pending updates...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -277,7 +341,7 @@ trait ResponseHandler
|
||||
\danog\MadelineProto\Logger::log(['Parsing updates received via the socket...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
if ($this->getting_state) {
|
||||
\danog\MadelineProto\Logger::log(['Getting state, handle later'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->pending_updates[] = $updates;
|
||||
$this->pending_updates []= $updates;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -299,10 +363,10 @@ trait ResponseHandler
|
||||
break;
|
||||
case 'updateShortMessage':
|
||||
case 'updateShortChatMessage':
|
||||
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->datacenter->authorization['user']['id'] : $updates['user_id']);
|
||||
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
|
||||
$to_id = isset($updates['chat_id'])
|
||||
? -$updates['chat_id']
|
||||
: ($updates['out'] ? $updates['user_id'] : $this->datacenter->authorization['user']['id']);
|
||||
: ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
|
||||
|
||||
if (!$this->peer_isset($from_id) ||
|
||||
!$this->peer_isset($to_id) ||
|
||||
|
@ -26,8 +26,8 @@ trait SaltHandler
|
||||
|
||||
public function add_salt($valid_since, $valid_until, $salt)
|
||||
{
|
||||
if (!isset($this->datacenter->temp_auth_key['salts'][$salt])) {
|
||||
$this->datacenter->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
|
||||
if (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt])) {
|
||||
$this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,22 +17,58 @@ namespace danog\MadelineProto\MTProtoTools;
|
||||
*/
|
||||
trait SeqNoHandler
|
||||
{
|
||||
public function generate_seq_no($content_related = true)
|
||||
public function generate_out_seq_no($datacenter, $content_related)
|
||||
{
|
||||
$in = $content_related ? 1 : 0;
|
||||
$value = $this->datacenter->seq_no;
|
||||
$this->datacenter->seq_no += $in;
|
||||
|
||||
$value = $this->datacenter->sockets[$datacenter]->session_out_seq_no;
|
||||
$this->datacenter->sockets[$datacenter]->session_out_seq_no += $in;
|
||||
return ($value * 2) + $in;
|
||||
}
|
||||
|
||||
public function get_in_seq_no($chat)
|
||||
public function generate_in_seq_no($datacenter, $content_related)
|
||||
{
|
||||
return count($this->secret_chats[$chat]['incoming']);
|
||||
$in = $content_related ? 1 : 0;
|
||||
$value = $this->datacenter->sockets[$datacenter]->session_in_seq_no;
|
||||
$this->datacenter->sockets[$datacenter]->session_in_seq_no += $in;
|
||||
return ($value * 2) + $in;
|
||||
}
|
||||
|
||||
public function get_out_seq_no($chat)
|
||||
public function content_related($method)
|
||||
{
|
||||
return count($this->secret_chats[$chat]['outgoing']);
|
||||
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',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ trait UpdateHandler
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
return false;
|
||||
}
|
||||
$difference = $this->method_call('updates.getChannelDifference', ['channel' => $input, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $this->get_channel_state($channel)['pts'], 'limit' => 30]);
|
||||
$difference = $this->method_call('updates.getChannelDifference', ['channel' => $input, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $this->get_channel_state($channel)['pts'], 'limit' => 30], ['datacenter' => $this->datacenter->curdc]);
|
||||
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->get_channel_state($channel)['sync_loading'] = false;
|
||||
switch ($difference['_']) {
|
||||
@ -219,7 +219,7 @@ trait UpdateHandler
|
||||
$this->get_update_state()['pending_pts_updates'] = [];
|
||||
$this->get_update_state()['pending_seq_updates'] = [];
|
||||
}
|
||||
$difference = $this->method_call('updates.getDifference', ['pts' => $this->get_update_state()['pts'], 'date' => $this->get_update_state()['date'], 'qts' => $this->get_update_state()['qts']]);
|
||||
$difference = $this->method_call('updates.getDifference', ['pts' => $this->get_update_state()['pts'], 'date' => $this->get_update_state()['date'], 'qts' => $this->get_update_state()['qts']], ['datacenter' => $this->datacenter->curdc]);
|
||||
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->get_update_state()['sync_loading'] = false;
|
||||
|
||||
@ -252,7 +252,7 @@ trait UpdateHandler
|
||||
{
|
||||
$this->updates_state['sync_loading'] = false;
|
||||
$this->getting_state = true;
|
||||
$this->set_update_state($this->method_call('updates.getState'));
|
||||
$this->set_update_state($this->method_call('updates.getState', [], ['datacenter' => $this->datacenter->curdc]));
|
||||
$this->getting_state = false;
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
@ -345,7 +345,7 @@ trait UpdateHandler
|
||||
if ($update['pts'] > $new_pts) {
|
||||
\danog\MadelineProto\Logger::log(['Pts hole. current pts: '.$cur_state['pts'].', pts count: '.(isset($update['pts_count']) ? $update['pts_count'] : 0).', new pts: '.$new_pts.' < update pts: '.$update['pts'].', channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$this->cur_state['pending_pts_updates'][] = $update;
|
||||
$this->cur_state['pending_pts_updates'] = arrray_merge($this->cur_state['pending_pts_updates'], [$update]);
|
||||
|
||||
if ($channel_id !== false && $this->peer_isset('-100'.$channel_id)) {
|
||||
$this->get_channel_difference($channel_id);
|
||||
@ -490,6 +490,14 @@ trait UpdateHandler
|
||||
|
||||
return;
|
||||
}
|
||||
if ($update['_'] === 'updatePhoneCall') {
|
||||
switch ($update['phone_call']['_']) {
|
||||
case 'phoneCallRequested':
|
||||
return $this->accept_call($update['phone_call']);
|
||||
case 'phoneCall':
|
||||
return $this->complete_call($update['phone_call']);
|
||||
}
|
||||
}
|
||||
if ($update['_'] === 'updateNewEncryptedMessage' && !isset($update['message']['decrypted_message'])) {
|
||||
$cur_state = $this->get_update_state();
|
||||
if ($cur_state['qts'] === -1) {
|
||||
@ -503,7 +511,6 @@ trait UpdateHandler
|
||||
}
|
||||
if ($update['qts'] > $cur_state['qts'] + 1) {
|
||||
\danog\MadelineProto\Logger::log(['Qts hole. update qts: '.$update['qts'].' > current qts '.$cur_state['qts'].'+1, chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
|
||||
|
||||
$this->get_updates_difference();
|
||||
|
||||
return false;
|
||||
@ -554,7 +561,7 @@ trait UpdateHandler
|
||||
if (isset($update['message']['_']) && $update['message']['_'] === 'messageEmpty') {
|
||||
return;
|
||||
}
|
||||
if (isset($update['message']['from_id']) && $update['message']['from_id'] === $this->datacenter->authorization['user']['id']) {
|
||||
if (isset($update['message']['from_id']) && $update['message']['from_id'] === $this->authorization['user']['id']) {
|
||||
$update['message']['out'] = true;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Saving an update of type '.$update['_'].'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
@ -595,7 +602,7 @@ trait UpdateHandler
|
||||
\danog\MadelineProto\Logger::log(['Result of webhook query is '.$result], \danog\MadelineProto\Logger::NOTICE);
|
||||
$result = json_decode($result, true);
|
||||
if (is_array($result) && isset($result['method']) && $result['method'] != '' && is_string($result['method'])) {
|
||||
\danog\MadelineProto\Logger::log(['Reverse webhook command returned', $this->method_call($result['method'], $result)]);
|
||||
\danog\MadelineProto\Logger::log(['Reverse webhook command returned', $this->method_call($result['method'], $result, ['datacenter' => $this->datacenter->curdc])]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ class RSA
|
||||
$this->keydata = ['n' => \phpseclib\Common\Functions\Objects::getVar($key, 'modulus'), 'e' => \phpseclib\Common\Functions\Objects::getVar($key, 'exponent')];
|
||||
|
||||
\danog\MadelineProto\Logger::log(['Computing fingerprint...'], Logger::ULTRA_VERBOSE);
|
||||
$this->keydata['fp'] = \danog\PHP\Struct::unpack('<q', substr(
|
||||
$this->keydata['fp'] = substr(
|
||||
sha1(
|
||||
$this->serialize_object(
|
||||
['type' => 'bytes'],
|
||||
@ -43,7 +43,7 @@ class RSA
|
||||
true
|
||||
),
|
||||
-8
|
||||
))[0];
|
||||
);
|
||||
|
||||
return $this->keydata;
|
||||
}
|
||||
|
206
src/danog/MadelineProto/SecretChats/AuthKeyHandler.php
Normal file
206
src/danog/MadelineProto/SecretChats/AuthKeyHandler.php
Normal file
@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2017 Daniil Gentili
|
||||
(https://daniil.it)
|
||||
This file is part of MadelineProto.
|
||||
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
/**
|
||||
* Manages secret chats.
|
||||
*
|
||||
* https://core.telegram.org/api/end-to-end
|
||||
*/
|
||||
trait AuthKeyHandler
|
||||
{
|
||||
private $temp_requested_secret_chats = [];
|
||||
private $secret_chats = [];
|
||||
|
||||
public function accept_secret_chat($params)
|
||||
{
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
$params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256);
|
||||
$this->check_G($params['g_a'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
|
||||
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => false, 'user_id' => $params['admin_id'], 'InputEncryptedChat' => ['_' => 'inputEncryptedChat', 'chat_id' => $params['id'], 'access_hash' => $params['access_hash']], 'in_seq_no_x' => 1, 'out_seq_no_x' => 0, 'layer' => 8, 'ttl' => PHP_INT_MAX, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0]];
|
||||
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
|
||||
$this->check_G($g_b, $dh_config['p']);
|
||||
$this->notify_layer($params['id']);
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
public function request_secret_chat($user)
|
||||
{
|
||||
$user = $this->get_info($user)['InputUser'];
|
||||
\danog\MadelineProto\Logger::log(['Creating secret chat with '.$user['user_id'].'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
\danog\MadelineProto\Logger::log(['Generating g_a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$g_a = $dh_config['g']->powMod($a, $dh_config['p']);
|
||||
$this->check_G($g_a, $dh_config['p']);
|
||||
$res = $this->method_call('messages.requestEncryption', ['user_id' => $user, 'g_a' => $g_a->toBytes()], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->temp_requested_secret_chats[$res['id']] = $a;
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
|
||||
return $res['id'];
|
||||
}
|
||||
|
||||
public function complete_secret_chat($params)
|
||||
{
|
||||
if ($this->secret_chat_status($params['id']) !== 1) {
|
||||
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat '.$params['id']]);
|
||||
|
||||
return false;
|
||||
}
|
||||
$dh_config = $this->get_dh_config();
|
||||
$params['g_a_or_b'] = new \phpseclib\Math\BigInteger($params['g_a_or_b'], 256);
|
||||
$this->check_G($params['g_a_or_b'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_a_or_b']->powMod($this->temp_requested_secret_chats[$params['id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
unset($this->temp_requested_secret_chats[$params['id']]);
|
||||
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
|
||||
|
||||
if ($key['fingerprint'] !== $params['key_fingerprint']) {
|
||||
$this->method_call('messages.discardEncryption', ['chat_id' => $params['id']], ['datacenter' => $this->datacenter->curdc]);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->secret_chats[$params['id']] = ['key' => $key, 'admin' => true, 'user_id' => $params['participant_id'], 'InputEncryptedChat' => ['chat_id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputEncryptedChat'], 'in_seq_no_x' => 0, 'out_seq_no_x' => 1, 'layer' => 8, 'ttl' => PHP_INT_MAX, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0]];
|
||||
$this->notify_layer($params['id']);
|
||||
$this->handle_pending_updates();
|
||||
}
|
||||
|
||||
|
||||
public function notify_layer($chat)
|
||||
{
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
|
||||
private $temp_rekeyed_secret_chats = [];
|
||||
|
||||
public function rekey($chat)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Rekeying secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$a = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
\danog\MadelineProto\Logger::log(['Generating g_a...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$g_a = $dh_config['g']->powMod($a, $dh_config['p']);
|
||||
$this->check_G($g_a, $dh_config['p']);
|
||||
$e = $this->random(8);
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionRequestKey', 'g_a' => $g_a->toBytes(), 'exchange_id' => $e]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->temp_rekeyed_secret_chats[$e] = $a;
|
||||
$this->secret_chats[$chat]['rekeying'] = [1, $e];
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
|
||||
return $e;
|
||||
}
|
||||
|
||||
public function accept_rekey($chat, $params)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
|
||||
$my = $this->temp_rekeyed_secret_chats[$this->secret_chats[$chat]['rekeying'][1]];
|
||||
if ($my['exchange_id'] > $params['exchange_id']) {
|
||||
return;
|
||||
}
|
||||
if ($my['exchange_id'] === $params['exchange_id']) {
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
$this->rekey($chat);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Accepting rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
|
||||
$params['g_a'] = new \phpseclib\Math\BigInteger($params['g_a'], 256);
|
||||
$this->check_G($params['g_a'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_a']->powMod($b, $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
|
||||
|
||||
$key['visualization_orig'] = $this->secret_chats[$chat]['key']['visualization_orig'];
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
$this->temp_rekeyed_secret_chats[$params['exchange_id']] = $key;
|
||||
$this->secret_chats[$chat]['rekeying'] = [2, $params['exchange_id']];
|
||||
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
|
||||
$this->check_G($g_b, $dh_config['p']);
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAcceptKey', 'g_b' => $g_b->toBytes(), 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
|
||||
public function commit_rekey($chat, $params)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 1) {
|
||||
return;
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Committing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$dh_config = $this->get_dh_config();
|
||||
$params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256);
|
||||
$this->check_G($params['g_b'], $dh_config['p']);
|
||||
$key = ['auth_key' => str_pad($params['g_b']->powMod($this->temp_rekeyed_secret_chats[$params['exchange_id']], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
|
||||
$key['fingerprint'] = substr(sha1($key['auth_key'], true), -8);
|
||||
$key['visualization_orig'] = $this->secret_chats[$chat]['key']['visualization_orig'];
|
||||
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
|
||||
if ($key['fingerprint'] !== $params['key_fingerprint']) {
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionCommitKey', 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
unset($this->temp_rekeyed_secret_chats[$chat]);
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
$this->secret_chats[$chat]['key'] = $key;
|
||||
$this->secret_chats[$chat]['ttr'] = 100;
|
||||
$this->secret_chats[$chat]['updated'] = time();
|
||||
|
||||
$this->handle_pending_updates();
|
||||
$this->get_updates_difference();
|
||||
}
|
||||
|
||||
public function complete_rekey($chat, $params)
|
||||
{
|
||||
if ($this->secret_chats[$chat]['rekeying'][0] !== 2) {
|
||||
return;
|
||||
}
|
||||
if ($this->temp_rekeyed_secret_chats['fingerprint'] !== $params['key_fingerprint']) {
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionAbortKey', 'exchange_id' => $params['exchange_id']]]], ['datacenter' => $this->datacenter->curdc]);
|
||||
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Completing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
|
||||
$this->secret_chats[$chat]['rekeying'] = [0];
|
||||
$this->secret_chats[$chat]['key'] = $this->temp_rekeyed_secret_chats;
|
||||
$this->secret_chats[$chat]['ttr'] = 100;
|
||||
$this->secret_chats[$chat]['updated'] = time();
|
||||
unset($this->temp_rekeyed_secret_chats[$params['exchange_id']]);
|
||||
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNoop']]], ['datacenter' => $this->datacenter->curdc]);
|
||||
}
|
||||
|
||||
public function secret_chat_status($chat)
|
||||
{
|
||||
if (isset($this->secret_chats[$chat])) {
|
||||
return 2;
|
||||
}
|
||||
if (isset($this->temp_requested_secret_chats[$chat])) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
86
src/danog/MadelineProto/SecretChats/MessageHandler.php
Normal file
86
src/danog/MadelineProto/SecretChats/MessageHandler.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/*
|
||||
Copyright 2016-2017 Daniil Gentili
|
||||
(https://daniil.it)
|
||||
This file is part of MadelineProto.
|
||||
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License along with MadelineProto.
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\SecretChats;
|
||||
|
||||
/**
|
||||
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
||||
*/
|
||||
trait MessageHandler
|
||||
{
|
||||
public function encrypt_secret_message($chat_id, $message)
|
||||
{
|
||||
if (!isset($this->secret_chats[$chat_id])) {
|
||||
\danog\MadelineProto\Logger::log('I do not have the secret chat '.$chat_id.' in the database, skipping message...');
|
||||
|
||||
return false;
|
||||
}
|
||||
$message = $this->serialize_object(['type' => $message['_']], $message, $this->secret_chats[$chat_id]['layer']);
|
||||
$this->secret_chats[$chat_id]['outgoing'] []= $message;
|
||||
$this->secret_chats[$chat_id]['ttr']--;
|
||||
if (($this->secret_chats[$chat_id]['ttr'] <= 0 || time() - $this->secret_chats[$chat_id]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$chat_id]['rekeying'] === 0) {
|
||||
$this->rekey($chat_id);
|
||||
}
|
||||
|
||||
$message = \danog\PHP\Struct::pack('<I', strlen($message)).$message;
|
||||
$message_key = substr(sha1($message, true), -16);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server');
|
||||
$padding = $this->random($this->posmod(-strlen($message), 16));
|
||||
$message = $this->secret_chats[$chat_id]['key']['fingerprint'].$message_key.$this->ige_encrypt($message.$padding, $aes_key, $aes_iv);
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function handle_encrypted_update($message)
|
||||
{
|
||||
if (!isset($this->secret_chats[$message['message']['chat_id']])) {
|
||||
\danog\MadelineProto\Logger::log('I do not have the secret chat '.$message['message']['chat_id'].' in the database, skipping message...');
|
||||
|
||||
return false;
|
||||
}
|
||||
$auth_key_id = substr($message['message']['bytes'], 0, 8);
|
||||
if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['key']['fingerprint']) {
|
||||
throw new \danog\MadelineProto\SecurityException('Key fingerprint mismatch');
|
||||
}
|
||||
$message_key = substr($message['message']['bytes'], 8, 16);
|
||||
$encrypted_data = substr($message['message']['bytes'], 24);
|
||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$message['message']['chat_id']]['key']['auth_key'], 'to server');
|
||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 0, 4))[0];
|
||||
if ($message_data_length > strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
|
||||
}
|
||||
|
||||
if ((strlen($decrypted_data) - 4) - $message_data_length > 15) {
|
||||
throw new \danog\MadelineProto\SecurityException('difference between message_data_length and the length of the remaining decrypted buffer is too big');
|
||||
}
|
||||
|
||||
if ($message_data_length % 4 != 0) {
|
||||
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
|
||||
}
|
||||
$message_data = substr($decrypted_data, 4, $message_data_length);
|
||||
if ($message_key != substr(sha1(substr($decrypted_data, 0, 4 + $message_data_length), true), -16)) {
|
||||
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
|
||||
}
|
||||
$deserialized = $this->deserialize($message_data, ['type' => '']);
|
||||
if (strlen($deserialized['random_bytes']) < 15) {
|
||||
throw new \danog\MadelineProto\SecurityException('random_bytes is too short');
|
||||
}
|
||||
$this->secret_chats[$message['message']['chat_id']]['ttr']--;
|
||||
if (($this->secret_chats[$message['message']['chat_id']]['ttr'] <= 0 || time() - $this->secret_chats[$message['message']['chat_id']]['updated'] > 7 * 24 * 60 * 60) && $this->secret_chats[$message['message']['chat_id']]['rekeying'] === 0) {
|
||||
$this->rekey($message['message']['chat_id']);
|
||||
}
|
||||
unset($message['message']['bytes']);
|
||||
$message['message']['decrypted_message'] = $deserialized;
|
||||
$this->handle_decrypted_update($message);
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ You should have received a copy of the GNU General Public License along with Mad
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Conversion;
|
||||
namespace danog\MadelineProto\TL\Conversion;
|
||||
|
||||
trait BotAPI
|
||||
{
|
||||
@ -106,7 +106,7 @@ trait BotAPI
|
||||
$newd['date'] = $data['date'];
|
||||
$newd['text'] = $sent_arguments['message'];
|
||||
if ($data['out']) {
|
||||
$newd['from'] = $this->get_pwr_chat($this->datacenter->authorization['user']);
|
||||
$newd['from'] = $this->get_pwr_chat($this->authorization['user']);
|
||||
}
|
||||
$newd['chat'] = $this->get_pwr_chat($sent_arguments['peer']);
|
||||
if (isset($data['entities'])) {
|
@ -10,7 +10,7 @@ You should have received a copy of the GNU General Public License along with Mad
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Conversion;
|
||||
namespace danog\MadelineProto\TL\Conversion;
|
||||
|
||||
trait BotAPIFiles
|
||||
{
|
@ -10,7 +10,7 @@ You should have received a copy of the GNU General Public License along with Mad
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Conversion;
|
||||
namespace danog\MadelineProto\TL\Conversion;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
@ -12,7 +12,7 @@ You should have received a copy of the GNU General Public License along with Mad
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Conversion;
|
||||
namespace danog\MadelineProto\TL\Conversion;
|
||||
|
||||
/**
|
||||
* Manages generation of extensions for files.
|
@ -10,7 +10,7 @@ You should have received a copy of the GNU General Public License along with Mad
|
||||
If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace danog\MadelineProto\Conversion;
|
||||
namespace danog\MadelineProto\TL\Conversion;
|
||||
|
||||
trait TD
|
||||
{
|
||||
@ -135,7 +135,7 @@ trait TD
|
||||
} else {
|
||||
switch (end($mtproto)) {
|
||||
case 'choose_chat_id_from_botapi':
|
||||
$newparams[$td] = ($this->get_info($params[$mtproto[0]])['bot_api_id'] == $this->datacenter->authorization['user']['id']) ? $params['from_id'] : $this->get_info($params[$mtproto[0]])['bot_api_id'];
|
||||
$newparams[$td] = ($this->get_info($params[$mtproto[0]])['bot_api_id'] == $this->authorization['user']['id']) ? $params['from_id'] : $this->get_info($params[$mtproto[0]])['bot_api_id'];
|
||||
break;
|
||||
case 'choose_incoming_or_sent':
|
||||
$newparams[$td] = ['_' => $params['out'] ? 'messageIsSuccessfullySent' : 'messageIsIncoming'];
|
@ -236,6 +236,17 @@ trait TL
|
||||
|
||||
return \danog\PHP\Struct::pack('<I', $object);
|
||||
case 'long':
|
||||
if (is_object($object)) {
|
||||
return str_pad(strrev($object->toBytes()), 8, chr(0));
|
||||
}
|
||||
|
||||
if (is_string($object)) {
|
||||
if (strlen($object) !== 8) {
|
||||
throw new Exception('Given value is not 8 bytes long');
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
if (!is_numeric($object)) {
|
||||
throw new Exception('given value ('.$object.") isn't numeric");
|
||||
}
|
||||
@ -437,7 +448,7 @@ trait TL
|
||||
case '#':
|
||||
return \danog\PHP\Struct::unpack('<I', $bytes_io->read(4))[0];
|
||||
case 'long':
|
||||
return \danog\PHP\Struct::unpack('<q', $bytes_io->read(8))[0];
|
||||
return $this->bigint || isset($type['strlong']) ? $bytes_io->read(8) : \danog\PHP\Struct::unpack('<q', $bytes_io->read(8))[0];
|
||||
case 'double':
|
||||
return \danog\PHP\Struct::unpack('<d', $bytes_io->read(8))[0];
|
||||
case 'int128':
|
||||
@ -457,13 +468,13 @@ trait TL
|
||||
$x = $bytes_io->read($long_len);
|
||||
$resto = $this->posmod(-$long_len, 4);
|
||||
if ($resto > 0) {
|
||||
$bytes_io->read($resto);
|
||||
$bytes_io->pos += $resto;
|
||||
}
|
||||
} else {
|
||||
$x = $bytes_io->read($l);
|
||||
$resto = $this->posmod(-($l + 1), 4);
|
||||
if ($resto > 0) {
|
||||
$bytes_io->read($resto);
|
||||
$bytes_io->pos += $resto;
|
||||
}
|
||||
}
|
||||
if (!is_string($x)) {
|
||||
@ -491,8 +502,9 @@ trait TL
|
||||
case 'vector':
|
||||
$count = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0];
|
||||
$result = [];
|
||||
$type['type'] = $type['subtype'];
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$result[] = $this->deserialize($bytes_io, ['type' => $type['subtype']]);
|
||||
$result[] = $this->deserialize($bytes_io, $type);
|
||||
}
|
||||
|
||||
return $result;
|
||||
@ -538,6 +550,9 @@ trait TL
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id', 'key_fingerprint', 'server_salt', 'new_server_salt'])) {
|
||||
$arg['strlong'] = true;
|
||||
}
|
||||
$x[$arg['name']] = $this->deserialize($bytes_io, $arg);
|
||||
}
|
||||
if (isset($x['flags'])) { // I don't think we need this anymore
|
||||
@ -547,42 +562,4 @@ trait TL
|
||||
return $x;
|
||||
}
|
||||
|
||||
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',
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,13 +15,12 @@ namespace danog\MadelineProto\Threads;
|
||||
/**
|
||||
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
||||
*/
|
||||
class SocketHandler extends Threaded
|
||||
class SocketHandler extends \Threaded implements \Collectable
|
||||
{
|
||||
public $payloads = [];
|
||||
|
||||
public function __construct(&$me)
|
||||
public function __construct(&$me, $current)
|
||||
{
|
||||
$this->API = $me;
|
||||
$this->current = $current;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -29,86 +28,22 @@ class SocketHandler extends Threaded
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->socket_handler->synchronized(function ($thread) {
|
||||
if (empty($thread->payloads)) {
|
||||
$thread->wait();
|
||||
} else {
|
||||
foreach ($thread->payloads as $payload) {
|
||||
if (fstat($payload)['size'] === 4) {
|
||||
$error = \danog\PHP\Struct::unpack('<i', stream_get_contents($payload, 4))[0];
|
||||
if ($error === -404) {
|
||||
if ($thread->API->datacenter->temp_auth_key != null) {
|
||||
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
|
||||
$thread->API->datacenter->temp_auth_key = null;
|
||||
$thread->API->init_authorization();
|
||||
$thread->API->config = $thread->API->write_client_info('help.getConfig');
|
||||
$thread->API->parse_config();
|
||||
continue;
|
||||
//throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
|
||||
}
|
||||
}
|
||||
throw new \danog\MadelineProto\RPCErrorException($error, $error);
|
||||
}
|
||||
$auth_key_id = stream_get_contents($payload, 8);
|
||||
if ($auth_key_id === str_repeat(chr(0), 8)) {
|
||||
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', stream_get_contents($payload, 12));
|
||||
$thread->API->check_message_id($message_id, false);
|
||||
$message_data = stream_get_contents($payload, $message_length);
|
||||
} elseif ($auth_key_id === $thread->API->datacenter->temp_auth_key['id']) {
|
||||
$message_key = stream_get_contents($payload, 16);
|
||||
$encrypted_data = stream_get_contents($payload);
|
||||
list($aes_key, $aes_iv) = $thread->API->aes_calculate($message_key, $thread->API->datacenter->temp_auth_key['auth_key'], 'from server');
|
||||
$decrypted_data = $thread->API->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||
|
||||
$server_salt = \danog\PHP\Struct::unpack('<q', substr($decrypted_data, 0, 8))[0];
|
||||
if ($server_salt != $thread->API->datacenter->temp_auth_key['server_salt']) {
|
||||
//\danog\MadelineProto\Logger::log(['WARNING: Server salt mismatch (my server salt '.$thread->API->datacenter->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').'], \danog\MadelineProto\Logger::WARNING);
|
||||
}
|
||||
|
||||
$session_id = substr($decrypted_data, 8, 8);
|
||||
if ($session_id != $thread->API->datacenter->session_id) {
|
||||
throw new \danog\MadelineProto\Exception('Session id mismatch.');
|
||||
}
|
||||
|
||||
$message_id = \danog\PHP\Struct::unpack('<Q', substr($decrypted_data, 16, 8))[0];
|
||||
$thread->API->check_message_id($message_id, false);
|
||||
|
||||
$seq_no = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 24, 4))[0];
|
||||
// Dunno how to handle any incorrect sequence numbers
|
||||
|
||||
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 28, 4))[0];
|
||||
|
||||
if ($message_data_length > strlen($decrypted_data)) {
|
||||
throw new \danog\MadelineProto\Exception('message_data_length is too big');
|
||||
}
|
||||
|
||||
if ((strlen($decrypted_data) - 32) - $message_data_length > 15) {
|
||||
throw new \danog\MadelineProto\Exception('difference between message_data_length and the length of the remaining decrypted buffer is too big');
|
||||
}
|
||||
|
||||
if ($message_data_length < 0) {
|
||||
throw new \danog\MadelineProto\Exception('message_data_length not positive');
|
||||
}
|
||||
|
||||
if ($message_data_length % 4 != 0) {
|
||||
throw new \danog\MadelineProto\Exception('message_data_length not divisible by 4');
|
||||
}
|
||||
|
||||
$message_data = substr($decrypted_data, 32, $message_data_length);
|
||||
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
|
||||
throw new \danog\MadelineProto\Exception('msg_key mismatch');
|
||||
}
|
||||
$thread->API->datacenter->incoming_messages[$message_id]['seq_no'] = $seq_no;
|
||||
} else {
|
||||
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
|
||||
}
|
||||
$deserialized = $thread->API->deserialize($message_data);
|
||||
$thread->API->datacenter->incoming_messages[$message_id]['content'] = $deserialized;
|
||||
$thread->API->datacenter->incoming_messages[$message_id]['response'] = -1;
|
||||
$thread->API->datacenter->new_incoming[$message_id] = $message_id;
|
||||
$thread->API->handle_messages();
|
||||
}
|
||||
}
|
||||
}, $this);
|
||||
require_once(__DIR__.'/../SecurityException.php');
|
||||
require_once(__DIR__.'/../RPCErrorException.php');
|
||||
require_once(__DIR__.'/../ResponseException.php');
|
||||
require_once(__DIR__.'/../TL/Conversion/Exception.php');
|
||||
require_once(__DIR__.'/../TL/Exception.php');
|
||||
require_once(__DIR__.'/../NothingInTheSocketException.php');
|
||||
require_once(__DIR__.'/../Exception.php');
|
||||
$this->API->handle_messages($current);
|
||||
$this->setGarbage();
|
||||
}
|
||||
|
||||
private $garbage = false;
|
||||
public function setGarbage():void {
|
||||
$this->garbage = true;
|
||||
}
|
||||
public function isGarbage():bool {
|
||||
return $this->garbage;
|
||||
}
|
||||
}
|
||||
|
@ -15,25 +15,52 @@ namespace danog\MadelineProto\Threads;
|
||||
/**
|
||||
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
||||
*/
|
||||
class SocketReader extends Threaded
|
||||
class SocketReader extends \Threaded implements \Collectable
|
||||
{
|
||||
public function __construct(&$me)
|
||||
public function __construct($me, $current)
|
||||
{
|
||||
$this->API = $me;
|
||||
$this->current = $current;
|
||||
|
||||
}
|
||||
public function __sleep() {
|
||||
return ['current', 'API', 'garbage'];
|
||||
}
|
||||
public function __destruct() {
|
||||
\danog\MadelineProto\Logger::log(['Shutting down handler pool for DC '.$this->current], \danog\MadelineProto\Logger::NOTICE);
|
||||
if (isset($this->handler_pool)) $this->handler_pool->shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading connection and receiving message from server. Check the CRC32.
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
try {
|
||||
$payload = $this->API->datacenter->read_message();
|
||||
$this->socket_handler->synchronized(function ($thread, $payload) {
|
||||
$thread->payloads[] = $payload;
|
||||
$thread->notify();
|
||||
}, $this->API->socket_handler, $payload);
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
|
||||
require_once(__DIR__.'/../SecurityException.php');
|
||||
require_once(__DIR__.'/../RPCErrorException.php');
|
||||
require_once(__DIR__.'/../ResponseException.php');
|
||||
require_once(__DIR__.'/../TL/Conversion/Exception.php');
|
||||
require_once(__DIR__.'/../TL/Exception.php');
|
||||
require_once(__DIR__.'/../NothingInTheSocketException.php');
|
||||
require_once(__DIR__.'/../Exception.php');
|
||||
var_dump($this->API->settings['threading']);
|
||||
if (!isset($this->handler_pool)) $this->handler_pool = new \Pool(2);
|
||||
|
||||
var_dump($this->API->settings['threading']);
|
||||
|
||||
while ($this->API->run_workers) {
|
||||
try {
|
||||
$this->API->recv_message($this->current);
|
||||
$this->handler_pool->submit(new SocketHandler($this->API, $this->current));
|
||||
} catch (\danog\MadelineProto\NothingInTheSocketException $e) { ; }
|
||||
}
|
||||
$this->setGarbage();
|
||||
}
|
||||
|
||||
public $garbage = false;
|
||||
public function setGarbage():void {
|
||||
$this->garbage = true;
|
||||
}
|
||||
public function isGarbage():bool {
|
||||
return $this->garbage;
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,11 @@ trait Login
|
||||
{
|
||||
public function logout()
|
||||
{
|
||||
if (!$this->API->method_call('auth.logOut')) {
|
||||
if (!$this->API->method_call('auth.logOut', [], ['datacenter' => $this->API->datacenter->curdc])) {
|
||||
throw new \danog\MadelineProto\Exception('An error occurred while logging out!');
|
||||
}
|
||||
$this->API->datacenter->authorized = false;
|
||||
$this->API->datacenter->authorization = null;
|
||||
$this->API->authorized = false;
|
||||
$this->API->authorization = null;
|
||||
$this->API->updates = [];
|
||||
\danog\MadelineProto\Logger::log(['Logged out successfully!'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
@ -34,20 +34,21 @@ trait Login
|
||||
|
||||
public function bot_login($token)
|
||||
{
|
||||
if ($this->API->datacenter->authorized) {
|
||||
if ($this->API->authorized) {
|
||||
\danog\MadelineProto\Logger::log(['This instance of MadelineProto is already logged in. Logging out first...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->logout();
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Logging in as a bot...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
$this->API->authorization = $this->API->method_call(
|
||||
'auth.importBotAuthorization',
|
||||
[
|
||||
'bot_auth_token' => $token,
|
||||
'api_id' => $this->API->settings['app_info']['api_id'],
|
||||
'api_hash' => $this->API->settings['app_info']['api_hash'],
|
||||
]
|
||||
], ['datacenter' => $this->API->datacenter->curdc]
|
||||
);
|
||||
$this->API->datacenter->authorized = true;
|
||||
$this->API->authorized = true;
|
||||
$this->API->sync_authorization($this->API->datacenter->cur_dc);
|
||||
$this->API->updates = [];
|
||||
$this->API->updates_key = 0;
|
||||
$this->API->get_updates_state();
|
||||
@ -60,17 +61,17 @@ trait Login
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Logged in successfully!'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $this->API->datacenter->authorization;
|
||||
return $this->API->authorization;
|
||||
}
|
||||
|
||||
public function phone_login($number, $sms_type = 5)
|
||||
{
|
||||
if ($this->API->datacenter->authorized) {
|
||||
if ($this->API->authorized) {
|
||||
\danog\MadelineProto\Logger::log(['This instance of MadelineProto is already logged in. Logging out first...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->logout();
|
||||
}
|
||||
\danog\MadelineProto\Logger::log(['Sending code...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
$this->API->authorization = $this->API->method_call(
|
||||
'auth.sendCode',
|
||||
[
|
||||
'phone_number' => $number,
|
||||
@ -78,107 +79,110 @@ trait Login
|
||||
'api_id' => $this->API->settings['app_info']['api_id'],
|
||||
'api_hash' => $this->API->settings['app_info']['api_hash'],
|
||||
'lang_code' => $this->API->settings['app_info']['lang_code'],
|
||||
]
|
||||
], ['datacenter' => $this->API->datacenter->curdc]
|
||||
);
|
||||
$this->API->datacenter->authorization['phone_number'] = $number;
|
||||
$this->API->datacenter->login_temp_status = 'waiting_code';
|
||||
$this->API->authorization['phone_number'] = $number;
|
||||
$this->API->login_temp_status = 'waiting_code';
|
||||
$this->API->should_serialize = true;
|
||||
$this->API->updates = [];
|
||||
$this->API->updates_key = 0;
|
||||
|
||||
\danog\MadelineProto\Logger::log(['Code sent successfully! Once you receive the code you should use the complete_phone_login function.'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $this->API->datacenter->authorization;
|
||||
return $this->API->authorization;
|
||||
}
|
||||
|
||||
public function complete_phone_login($code)
|
||||
{
|
||||
if ($this->API->datacenter->login_temp_status !== 'waiting_code') {
|
||||
if ($this->API->login_temp_status !== 'waiting_code') {
|
||||
throw new \danog\MadelineProto\Exception("I'm not waiting for the code! Please call the phone_login method first");
|
||||
}
|
||||
$this->API->datacenter->login_temp_status = 'none';
|
||||
$this->API->login_temp_status = 'none';
|
||||
\danog\MadelineProto\Logger::log(['Logging in as a normal user...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
try {
|
||||
$authorization = $this->API->method_call(
|
||||
'auth.signIn',
|
||||
[
|
||||
'phone_number' => $this->API->datacenter->authorization['phone_number'],
|
||||
'phone_code_hash' => $this->API->datacenter->authorization['phone_code_hash'],
|
||||
'phone_number' => $this->API->authorization['phone_number'],
|
||||
'phone_code_hash' => $this->API->authorization['phone_code_hash'],
|
||||
'phone_code' => $code,
|
||||
]
|
||||
], ['datacenter' => $this->API->datacenter->curdc]
|
||||
);
|
||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||
if ($e->getMessage() === 'SESSION_PASSWORD_NEEDED') {
|
||||
\danog\MadelineProto\Logger::log(['2FA enabled, you will have to call the complete_2fa_login function...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->API->datacenter->login_temp_status = 'waiting_password';
|
||||
$this->API->login_temp_status = 'waiting_password';
|
||||
$this->API->should_serialize = true;
|
||||
|
||||
return $this->API->datacenter->authorization = $this->account->getPassword();
|
||||
return $this->API->authorization = $this->account->getPassword();
|
||||
}
|
||||
if ($e->getMessage() === 'PHONE_NUMBER_UNOCCUPIED') {
|
||||
\danog\MadelineProto\Logger::log(['An account has not been created for this number, you will have to call the complete_signup function...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->API->datacenter->login_temp_status = 'waiting_signup';
|
||||
$this->API->login_temp_status = 'waiting_signup';
|
||||
$this->API->should_serialize = true;
|
||||
$this->API->datacenter->authorization['phone_code'] = $code;
|
||||
$this->API->authorization['phone_code'] = $code;
|
||||
|
||||
return ['_' => 'account.needSignup'];
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
$this->API->datacenter->authorization = $authorization;
|
||||
$this->API->datacenter->authorized = true;
|
||||
$this->API->authorization = $authorization;
|
||||
$this->API->authorized = true;
|
||||
$this->API->sync_authorization($this->API->datacenter->cur_dc);
|
||||
$this->API->get_updates_state();
|
||||
$this->API->should_serialize = true;
|
||||
|
||||
\danog\MadelineProto\Logger::log(['Logged in successfully!'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $this->API->datacenter->authorization;
|
||||
return $this->API->authorization;
|
||||
}
|
||||
|
||||
public function complete_signup($first_name, $last_name)
|
||||
{
|
||||
if ($this->API->datacenter->login_temp_status !== 'waiting_signup') {
|
||||
if ($this->API->login_temp_status !== 'waiting_signup') {
|
||||
throw new \danog\MadelineProto\Exception("I'm not waiting for the password! Please call the phone_login and the complete_phone_login methods first!");
|
||||
}
|
||||
$this->API->datacenter->login_temp_status = 'none';
|
||||
$this->API->login_temp_status = 'none';
|
||||
\danog\MadelineProto\Logger::log(['Signing up as a normal user...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
$this->API->authorization = $this->API->method_call(
|
||||
'auth.signUp',
|
||||
[
|
||||
'phone_number' => $this->API->datacenter->authorization['phone_number'],
|
||||
'phone_code_hash' => $this->API->datacenter->authorization['phone_code_hash'],
|
||||
'phone_code' => $this->API->datacenter->authorization['phone_code'],
|
||||
'phone_number' => $this->API->authorization['phone_number'],
|
||||
'phone_code_hash' => $this->API->authorization['phone_code_hash'],
|
||||
'phone_code' => $this->API->authorization['phone_code'],
|
||||
'first_name' => $first_name,
|
||||
'last_name' => $last_name,
|
||||
]
|
||||
], ['datacenter' => $this->API->datacenter->curdc]
|
||||
);
|
||||
$this->API->datacenter->authorized = true;
|
||||
$this->API->authorized = true;
|
||||
$this->API->sync_authorization($this->API->datacenter->cur_dc);
|
||||
$this->API->get_updates_state();
|
||||
$this->API->should_serialize = true;
|
||||
|
||||
\danog\MadelineProto\Logger::log(['Signed up in successfully!'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $this->API->datacenter->authorization;
|
||||
return $this->API->authorization;
|
||||
}
|
||||
|
||||
public function complete_2fa_login($password)
|
||||
{
|
||||
if ($this->API->datacenter->login_temp_status !== 'waiting_password') {
|
||||
if ($this->API->login_temp_status !== 'waiting_password') {
|
||||
throw new \danog\MadelineProto\Exception("I'm not waiting for the password! Please call the phone_login and the complete_phone_login methods first!");
|
||||
}
|
||||
$this->API->datacenter->login_temp_status = 'none';
|
||||
$this->API->login_temp_status = 'none';
|
||||
\danog\MadelineProto\Logger::log(['Logging in as a normal user...'], \danog\MadelineProto\Logger::NOTICE);
|
||||
$this->API->datacenter->authorization = $this->API->method_call(
|
||||
$this->API->authorization = $this->API->method_call(
|
||||
'auth.checkPassword',
|
||||
[
|
||||
'password_hash' => hash('sha256', $this->API->datacenter->authorization['current_salt'].$password.$this->API->datacenter->authorization['current_salt'], true),
|
||||
]
|
||||
'password_hash' => hash('sha256', $this->API->authorization['current_salt'].$password.$this->API->authorization['current_salt'], true),
|
||||
], ['datacenter' => $this->API->datacenter->curdc]
|
||||
);
|
||||
$this->API->datacenter->authorized = true;
|
||||
$this->API->authorized = true;
|
||||
$this->API->sync_authorization($this->API->datacenter->cur_dc);
|
||||
$this->API->get_updates_state();
|
||||
$this->API->should_serialize = true;
|
||||
\danog\MadelineProto\Logger::log(['Logged in successfully!'], \danog\MadelineProto\Logger::NOTICE);
|
||||
|
||||
return $this->API->datacenter->authorization;
|
||||
return $this->API->authorization;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ require_once 'vendor/autoload.php';
|
||||
if (file_exists('web_data.php')) {
|
||||
require_once 'web_data.php';
|
||||
}
|
||||
|
||||
echo 'Deserializing MadelineProto from session.madeline...'.PHP_EOL;
|
||||
$MadelineProto = false;
|
||||
try {
|
||||
@ -65,8 +64,7 @@ if ($MadelineProto === false) {
|
||||
}
|
||||
}
|
||||
$message = (getenv('TRAVIS_COMMIT') == '') ? 'I iz works always (io laborare sembre) (yo lavorar siempre) (mi labori ĉiam) (я всегда работать) (Ik werkuh altijd)' : ('Travis ci tests in progress: commit '.getenv('TRAVIS_COMMIT').', job '.getenv('TRAVIS_JOB_NUMBER').', PHP version: '.getenv('TRAVIS_PHP_VERSION'));
|
||||
$secret = $MadelineProto->API->request_secret_chat(getenv('TEST_USERNAME'));
|
||||
var_dump($MadelineProto->get_pwr_chat('@telegram'));
|
||||
//$secret = $MadelineProto->API->request_secret_chat(getenv('TEST_SECRET_CHAT'));
|
||||
//$MadelineProto->API->request_secret_chat('@Harold_Saxon');
|
||||
echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL;
|
||||
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('session.madeline', $MadelineProto).' bytes'.PHP_EOL;
|
||||
|
Loading…
x
Reference in New Issue
Block a user