Repeat RPC query on internal server errors, improve retry management, improve http client

This commit is contained in:
Daniil Gentili 2018-02-24 16:54:13 +00:00
parent 4d7eab05c4
commit 5519782618
58 changed files with 1811 additions and 3997 deletions

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,20 +10,17 @@ 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;
class API extends APIFactory
{
use \danog\Serializable;
public $session;
public $serialized = 0;
public function __magic_construct($params = [])
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
set_exception_handler(['\danog\MadelineProto\Serialization', 'serialize_all']);
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
set_exception_handler(['\\danog\\MadelineProto\\Serialization', 'serialize_all']);
if (is_string($params)) {
if (file_exists($params)) {
$this->session = $params;
@ -34,29 +32,26 @@ class API extends APIFactory
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
\danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_SH);
try {
$unserialized = file_get_contents($realpaths['file']);
} finally {
flock($realpaths['lockfile'], LOCK_UN);
fclose($realpaths['lockfile']);
}
$tounserialize = str_replace('O:26:"danog\MadelineProto\Button":', 'O:35:"danog\MadelineProto\TL\Types\Button":', $unserialized);
foreach (['RSA', 'TL\TLMethod', 'TL\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\Types\Button', 'TL\Types\Bytes', 'APIFactory'] as $class) {
class_exists('\danog\MadelineProto\\'.$class);
$tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $unserialized);
foreach (['RSA', 'TL\\TLMethod', 'TL\\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\\Types\\Button', 'TL\\Types\\Bytes', 'APIFactory'] as $class) {
class_exists('\\danog\\MadelineProto\\' . $class);
}
class_exists('\Volatile');
class_exists('\\Volatile');
\danog\MadelineProto\Logger::class_exists();
try {
$unserialized = unserialize($tounserialize);
} catch (\danog\MadelineProto\Bug74586Exception $e) {
$unserialized = \danog\Serialization::unserialize($tounserialize);
} catch (\danog\MadelineProto\Exception $e) {
Logger::log([(string) $e], Logger::ERROR);
if (strpos($e->getMessage(), "Erroneous data format for unserializing 'phpseclib\Math\BigInteger'") === 0) {
$tounserialize = str_replace('phpseclib\Math\BigInteger', 'phpseclib\Math\BigIntegor', $unserialized);
if (strpos($e->getMessage(), "Erroneous data format for unserializing 'phpseclib\\Math\\BigInteger'") === 0) {
$tounserialize = str_replace('phpseclib\\Math\\BigInteger', 'phpseclib\\Math\\BigIntegor', $unserialized);
}
$unserialized = \danog\Serialization::unserialize($tounserialize);
}
@ -74,31 +69,25 @@ class API extends APIFactory
$this->APIFactory();
}
Serialization::$instances[spl_object_hash($unserialized)] = $unserialized;
return;
}
$this->API = new MTProto($params);
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['apifactory_start']], Logger::VERBOSE);
$this->APIFactory();
\danog\MadelineProto\Logger::log(['Ping...'], Logger::ULTRA_VERBOSE);
$pong = $this->ping(['ping_id' => 3]);
\danog\MadelineProto\Logger::log(['Pong: '.$pong['ping_id']], Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Pong: ' . $pong['ping_id']], Logger::ULTRA_VERBOSE);
//\danog\MadelineProto\Logger::log(['Getting future salts...'], Logger::ULTRA_VERBOSE);
//$this->future_salts = $this->get_future_salts(['num' => 3]);
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['madelineproto_ready']], Logger::NOTICE);
Serialization::$instances[spl_object_hash($this)] = $this;
}
public function __wakeup()
{
//if (method_exists($this->API, 'wakeup')) $this->API = $this->API->wakeup();
Serialization::$instances[spl_object_hash($this)] = $this;
$this->APIFactory();
}
public function __destruct()
{
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
@ -109,56 +98,45 @@ class API extends APIFactory
}
restore_error_handler();
}
public function __sleep()
{
return ['API'];
}
public function &__get($name)
{
if ($name === 'settings') {
$this->API->setdem = true;
return $this->API->settings;
}
return $this->API->storage[$name];
}
public function __set($name, $value)
{
if ($name === 'settings') {
return $this->API->__construct($value);
}
return $this->API->storage[$name] = $value;
}
public function __isset($name)
{
return isset($this->API->storage[$name]);
}
public function __unset($name)
{
unset($this->API->storage[$name]);
}
public function APIFactory()
{
foreach ($this->API->get_method_namespaces() as $namespace) {
$this->{$namespace} = new APIFactory($namespace, $this->API);
}
}
public function serialize($params = '')
{
if ($params === '') {
$params = $this->session;
}
Logger::log([\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']]);
return Serialization::serialize($params, $this);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
class APIFactory
@ -105,17 +105,14 @@ class APIFactory
*/
public $auth;
use Tools;
public $namespace = '';
public $API;
public $lua = false;
public function __construct($namespace, $API)
{
$this->namespace = $namespace.'.';
$this->namespace = $namespace . '.';
$this->API = $API;
}
public function __call($name, $arguments)
{
if ($this->API->setdem) {
@ -130,13 +127,11 @@ class APIFactory
$this->serialize($this->session);
}
if ($this->lua === false) {
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : [], $aargs);
return method_exists($this->API, $this->namespace . $name) ? $this->API->{$this->namespace . $name}(...$arguments) : $this->API->method_call($this->namespace . $name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
}
try {
$deserialized = method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : [], $aargs);
$deserialized = method_exists($this->API, $this->namespace . $name) ? $this->API->{$this->namespace . $name}(...$arguments) : $this->API->method_call($this->namespace . $name, isset($arguments[0]) && is_array($arguments[0]) ? $arguments[0] : [], $aargs);
Lua::convert_objects($deserialized);
return $deserialized;
} catch (\danog\MadelineProto\Exception $e) {
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
@ -154,4 +149,4 @@ class APIFactory
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,29 +10,24 @@ 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;
use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder
{
use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($settings)
{
$this->construct_TL($settings['tl_schema']);
$this->settings = $settings;
}
public function mk_annotations()
{
\danog\MadelineProto\Logger::log(['Generating annotations...'], \danog\MadelineProto\Logger::NOTICE);
$this->setProperties();
$this->createInternalClasses();
}
/**
* Open file of class APIFactory
* Insert properties
@ -47,33 +43,22 @@ class AnnotationsBuilder
if ($raw_docblock = $property->getDocComment()) {
$docblock = $fixture->create($raw_docblock);
if ($docblock->hasTag('internal')) {
$content = str_replace("\n ".$raw_docblock."\n public $".$property->getName().';', '', $content);
$content = str_replace("\n " . $raw_docblock . "\n public \$" . $property->getName() . ';', '', $content);
}
}
}
foreach ($this->get_method_namespaces() as $namespace) {
$content = preg_replace(
'/(class( \w+[,]?){0,}\n{\n)/',
'${1}'.
" /**\n".
" * @internal this is a internal property generated by build_docs.php, don't change manually\n".
" *\n".
" * @var $namespace\n".
" */\n".
" public \$$namespace;\n",
$content
);
$content = preg_replace('/(class( \\w+[,]?){0,}\\n{\\n)/', '${1}' . " /**\n" . " * @internal this is a internal property generated by build_docs.php, don't change manually\n" . " *\n" . " * @var {$namespace}\n" . " */\n" . " public \${$namespace};\n", $content);
}
file_put_contents($filename, $content);
}
/**
* Create file InternalDoc with all interfaces.
*/
private function createInternalClasses()
{
\danog\MadelineProto\Logger::log(['Creating internal classes...'], \danog\MadelineProto\Logger::NOTICE);
$handle = fopen(dirname(__FILE__).'/InternalDoc.php', 'w');
$handle = fopen(dirname(__FILE__) . '/InternalDoc.php', 'w');
foreach ($this->methods->by_id as $id => $data) {
if (!strpos($data['method'], '.')) {
continue;
@ -110,22 +95,22 @@ class AnnotationsBuilder
fwrite($handle, " * and is used only for autocomplete in multiple IDE\n");
fwrite($handle, " * don't modify manually.\n");
fwrite($handle, " */\n\n");
fwrite($handle, "namespace danog\MadelineProto;\n");
fwrite($handle, "namespace danog\\MadelineProto;\n");
foreach ($internalDoc as $namespace => $methods) {
fwrite($handle, "\ninterface $namespace\n{");
fwrite($handle, "\ninterface {$namespace}\n{");
foreach ($methods as $method => $properties) {
fwrite($handle, "\n /**\n");
if (isset($properties['attr'])) {
fwrite($handle, " * @param array params [\n");
foreach ($properties['attr'] as $name => $type) {
fwrite($handle, " * $type $name,\n");
fwrite($handle, " * {$type} {$name},\n");
}
fwrite($handle, " * ]\n");
fwrite($handle, " *\n");
}
fwrite($handle, " * @return {$properties['return']}\n");
fwrite($handle, " */\n");
fwrite($handle, " public function $method(");
fwrite($handle, " public function {$method}(");
if (isset($properties['attr'])) {
fwrite($handle, 'array $params');
}
@ -135,4 +120,4 @@ class AnnotationsBuilder
}
fclose($handle);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,9 +10,8 @@ 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;
class Bug74586Exception extends \Exception
{
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -20,7 +20,6 @@ class Connection
use \danog\Serializable;
use \danog\MadelineProto\Tools;
public $sock = null;
public $protocol = null;
public $ip = null;
public $port = null;
@ -39,15 +38,15 @@ class Connection
public $new_outgoing = [];
public $max_incoming_id;
public $max_outgoing_id;
public $proxy = '\Socket';
public $proxy = '\\Socket';
public $extra = [];
public $obfuscated = [];
public $authorized = false;
public $call_queue = [];
public $object_queue = [];
public $ack_queue = [];
public $i = [];
private $must_open = false;
public function __magic_construct($proxy, $extra, $ip, $port, $protocol, $timeout, $ipv6)
{
@ -67,7 +66,7 @@ class Connection
$this->port = $port;
$this->proxy = $proxy;
$this->extra = $extra;
if (($has_proxy = $proxy !== '\Socket') && !isset(class_implements($proxy)['danog\MadelineProto\Proxy'])) {
if (($has_proxy = $proxy !== '\\Socket') && !isset(class_implements($proxy)['danog\\MadelineProto\\Proxy'])) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['proxy_class_invalid']);
}
switch ($this->protocol) {
@ -97,7 +96,6 @@ class Connection
$this->sock->setBlocking(true);
$this->write(str_repeat(chr(238), 4));
break;
case 'tcp_full':
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if ($has_proxy && $this->extra !== []) {
@ -109,7 +107,6 @@ class Connection
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
}
$this->sock->setBlocking(true);
$this->out_seq_no = -1;
$this->in_seq_no = -1;
break;
@ -129,42 +126,31 @@ class Connection
} while (in_array(substr($random, 0, 4), ['PVrG', 'GET ', 'POST', 'HEAD', str_repeat(chr(238), 4)]) || $random[0] === chr(0xef) || substr($random, 4, 4) === "\0\0\0\0");
$random[56] = $random[57] = $random[58] = $random[59] = chr(0xef);
$reversed = strrev(substr($random, 8, 48));
$this->obfuscated = ['encryption' => new \phpseclib\Crypt\AES('ctr'), 'decryption' => new \phpseclib\Crypt\AES('ctr')];
$this->obfuscated['encryption']->enableContinuousBuffer();
$this->obfuscated['decryption']->enableContinuousBuffer();
$this->obfuscated['encryption']->setKey(substr($random, 8, 32));
$this->obfuscated['encryption']->setIV(substr($random, 40, 16));
$this->obfuscated['decryption']->setKey(substr($reversed, 0, 32));
$this->obfuscated['decryption']->setIV(substr($reversed, 32, 16));
$random = substr_replace(
$random,
substr(
@$this->obfuscated['encryption']->encrypt($random),
56,
8
),
56,
8
);
$random = substr_replace($random, substr(@$this->obfuscated['encryption']->encrypt($random), 56, 8), 56, 8);
$wrote = 0;
if (($wrote += $this->sock->write($random)) !== 64) {
while (($wrote += $this->sock->write(substr($what, $wrote))) !== 64);
while (($wrote += $this->sock->write(substr($what, $wrote))) !== 64) {
}
}
break;
case 'http':
case 'https':
case 'https_proxied':
$this->parsed = parse_url($ip);
if ($this->parsed['host'][0] === '[') {
$this->parsed['host'] = substr($this->parsed['host'], 1, -1);
}
if ($this->protocol === 'https' && $proxy === '\Socket') {
$proxy = '\FSocket';
if (strpos($this->protocol, 'https') === 0 && $proxy === '\\Socket') {
$proxy = '\\FSocket';
}
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, $this->protocol === 'https' ? PHP_INT_MAX : getprotobyname('tcp'));
$this->sock = new $proxy($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, strpos($this->protocol, 'https') === 0 ? PHP_INT_MAX : getprotobyname('tcp'));
if ($has_proxy && $this->extra !== []) {
$this->sock->setExtra($this->extra);
}
@ -174,6 +160,10 @@ class Connection
throw new Exception(\danog\MadelineProto\Lang::$current_lang['socket_con_error']);
}
$this->sock->setBlocking(true);
if ($this->protocol === 'https_proxied') {
$this->write("CONNECT {$this->parsed['host']}:$port HTTP/1.1\r\nHost: {$this->parsed['host']}:$port\r\n\r\n");
$this->read_message();
}
break;
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
@ -182,18 +172,15 @@ class Connection
break;
}
}
public function __destruct()
{
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
case 'https_proxied':
case 'obfuscated2':
try {
unset($this->sock);
@ -207,23 +194,17 @@ class Connection
break;
}
}
public function close_and_reopen()
{
$this->__destruct();
$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
$this->must_open = true;
}
public function __sleep()
{
return ['proxy', 'extra', 'protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id', 'obfuscated', 'authorized', 'object_queue', 'ack_queue'];
}
public function __wakeup()
{
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
$keys = array_keys((array) get_object_vars($this));
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
@ -231,15 +212,17 @@ class Connection
$this->time_delta = 0;
//$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function write($what, $length = null)
{
if ($this->must_open) {
$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
$this->must_open = false;
}
if ($length !== null) {
$what = substr($what, 0, $length);
} else {
$length = strlen($what);
}
switch ($this->protocol) {
case 'obfuscated2':
$what = @$this->obfuscated['encryption']->encrypt($what);
@ -248,11 +231,12 @@ class Connection
case 'tcp_full':
case 'http':
case 'https':
case 'https_proxied':
$wrote = 0;
if (($wrote += $this->sock->write($what)) !== $length) {
while (($wrote += $this->sock->write(substr($what, $wrote))) !== $length);
while (($wrote += $this->sock->write(substr($what, $wrote))) !== $length) {
}
}
return $wrote;
break;
case 'udp':
@ -263,9 +247,12 @@ class Connection
break;
}
}
public function read($length)
{
if ($this->must_open) {
$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
$this->must_open = false;
}
switch ($this->protocol) {
case 'obfuscated2':
$packet = '';
@ -277,17 +264,15 @@ class Connection
}
if (strlen($packet) !== $length) {
$this->close_and_reopen();
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet)));
}
return @$this->obfuscated['decryption']->encrypt($packet);
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
case 'https_proxied':
$packet = '';
while (strlen($packet) < $length) {
$packet .= $this->sock->read($length - strlen($packet));
@ -297,10 +282,8 @@ class Connection
}
if (strlen($packet) !== $length) {
$this->close_and_reopen();
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet)));
}
return $packet;
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
@ -309,7 +292,6 @@ class Connection
break;
}
}
public function read_message()
{
switch ($this->protocol) {
@ -317,8 +299,7 @@ class Connection
$packet_length_data = $this->read(4);
$packet_length = unpack('V', $packet_length_data)[1];
$packet = $this->read($packet_length - 4);
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
if (strrev(hash('crc32b', $packet_length_data . substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!');
}
$this->in_seq_no++;
@ -326,88 +307,102 @@ class Connection
if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch');
}
return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate':
return $this->read(unpack('V', $this->read(4))[1]);
case 'obfuscated2':
case 'tcp_abridged':
$packet_length = ord($this->read(1));
return $this->read($packet_length < 127 ? $packet_length << 2 : unpack('V', $this->read(3)."\0")[1] << 2);
return $this->read($packet_length < 127 ? $packet_length << 2 : unpack('V', $this->read(3) . "\0")[1] << 2);
case 'http':
case 'https':
$headers = [];
$close = false;
$length = 0;
while (true) {
$current_header = '';
while (($curchar = $this->read(1)) !== "\n") {
$current_header .= $curchar;
}
$current_header = rtrim($current_header);
if ($current_header === '') {
break;
}
if ($current_header === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['no_data_in_socket']);
}
if (preg_match('|^Content-Length: |i', $current_header)) {
$length = (int) preg_replace('|Content-Length: |i', '', $current_header);
}
if (preg_match('|^Connection: close|i', $current_header)) {
$close = true;
}
$headers[] = $current_header;
case 'https_proxied':
$response = $this->read_http_payload();
if ($response['code'] !== 200) {
throw new Exception($response['description'], $response['code']);
}
$read = $this->read($length);
$headers[0] = explode(' ', $headers[0], 3);
if ($headers[0][1] !== '200') {
throw new Exception($headers[0][2]);
$close = $response['protocol'] === 'HTTP/1.0';
if (isset($headers['connection'])) {
$close = $headers['connection'] === 'close';
}
if ($close) {
$this->close_and_reopen();
}
return $read;
return $response['body'];
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
}
}
public function send_message($message)
{
switch ($this->protocol) {
case 'tcp_full':
$this->out_seq_no++;
$step1 = pack('VV', (strlen($message) + 12), $this->out_seq_no).$message;
$step2 = $step1.strrev(hash('crc32b', $step1, true));
$step1 = pack('VV', strlen($message) + 12, $this->out_seq_no) . $message;
$step2 = $step1 . strrev(hash('crc32b', $step1, true));
$this->write($step2);
break;
case 'tcp_intermediate':
$this->write(pack('V', strlen($message)).$message);
$this->write(pack('V', strlen($message)) . $message);
break;
case 'obfuscated2':
case 'tcp_abridged':
$len = strlen($message) / 4;
if ($len < 127) {
$message = chr($len).$message;
$message = chr($len) . $message;
} else {
$message = chr(127).substr(pack('V', $len), 0, 3).$message;
$message = chr(127) . substr(pack('V', $len), 0, 3) . $message;
}
$this->write($message);
break;
case 'http':
case 'https':
$this->write('POST '.$this->parsed['path']." HTTP/1.1\r\nHost: ".$this->parsed['host']."\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nContent-Length: ".strlen($message)."\r\n\r\n".$message);
case 'https_proxied':
$this->write('POST ' . $this->parsed['path'] . " HTTP/1.1\r\nHost: " . $this->parsed['host'] . "\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nKeep-Alive: timeout=100000, max=10000000\r\nContent-Length: " . strlen($message) . "\r\n\r\n" . $message);
break;
case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
default:
break;
}
}
public function read_http_line()
{
$line = '';
while (($curchar = $this->read(1)) !== "\n")
{
$line.= $curchar;
}
return rtrim($line);
}
function read_http_payload()
{
$header = explode(' ', $this->read_http_line() , 3);
$protocol = $header[0];
$code = (int)$header[1];
$description = $header[2];
$headers = [];
while (strlen($current_header = $this->read_http_line())) {
$current_header = explode(':', $current_header, 2);
$headers[strtolower($current_header[0]) ] = trim($current_header[1]);
}
$read = '';
if (isset($headers['content-length']))
{
$read = $this->read((int)$headers['content-length']);
} elseif (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] === 'chunked') {
do
{
$length = hexdec($this->read_http_line($res));
$read.= $this->read($length);
$this->read_http_line($res);
} while ($length);
}
return ['protocol' => $protocol, 'code' => $code, 'description' => $description, 'body' => $read, 'headers' => $headers];
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -19,17 +19,14 @@ class DataCenter
{
use \danog\MadelineProto\Tools;
use \danog\Serializable;
public $sockets = [];
public $curdc = 0;
private $dclist = [];
private $settings = [];
public function __sleep()
{
return ['sockets', 'curdc', 'dclist', 'settings'];
}
public function __magic_construct($dclist, $settings)
{
$this->dclist = $dclist;
@ -44,7 +41,6 @@ class DataCenter
}
}
}
public function dc_disconnect($dc_number)
{
if ($this->curdc === $dc_number) {
@ -55,7 +51,6 @@ class DataCenter
unset($this->sockets[$dc_number]);
}
}
public function dc_connect($dc_number)
{
if (isset($this->sockets[$dc_number]) && !isset($this->sockets[$dc_number]->old)) {
@ -64,18 +59,14 @@ class DataCenter
$dc_config_number = isset($this->settings[$dc_number]) ? $dc_number : 'all';
$test = $this->settings[$dc_config_number]['test_mode'] ? 'test' : 'main';
$x = 0;
do {
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) {
unset($this->sockets[$dc_number]);
return false;
}
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
$port = $this->dclist[$test][$ipv6][$dc_number]['port'];
if (isset($this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) && $this->dclist[$test][$ipv6][$dc_number]['tcpo_only']) {
if ($dc_config_number === 'all') {
$dc_config_number = $dc_number;
@ -85,23 +76,21 @@ class DataCenter
}
$this->settings[$dc_config_number]['protocol'] = 'obfuscated2';
}
if ($this->settings[$dc_config_number]['protocol'] === 'https') {
if (strpos($this->settings[$dc_config_number]['protocol'], 'https') === 0) {
$subdomain = $this->dclist['ssl_subdomains'][$dc_number];
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
$address = $this->settings[$dc_config_number]['protocol'].'://'.$subdomain.'.web.telegram.org/'.$path;
$address = 'https://' . $subdomain . '.web.telegram.org/' . $path;
$port = 443;
}
if ($this->settings[$dc_config_number]['protocol'] === 'http') {
if ($ipv6) {
$address = '['.$address.']';
$address = '[' . $address . ']';
}
$address = $this->settings[$dc_config_number]['protocol'].'://'.$address.'/api';
$address = $this->settings[$dc_config_number]['protocol'] . '://' . $address . '/api';
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['dc_con_test_start'], $dc_number, $test, $ipv6, $this->settings[$dc_config_number]['protocol'])], \danog\MadelineProto\Logger::VERBOSE);
foreach (array_unique([$port, 443, 80, 88]) as $port) {
\danog\MadelineProto\Logger::log(['Trying connection on port '.$port.'...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Trying connection on port ' . $port . '...'], \danog\MadelineProto\Logger::WARNING);
try {
if (isset($this->sockets[$dc_number]->old)) {
$this->sockets[$dc_number]->__construct($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
@ -110,59 +99,34 @@ class DataCenter
$this->sockets[$dc_number] = new Connection($this->settings[$dc_config_number]['proxy'], $this->settings[$dc_config_number]['proxy_extra'], $address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
}
\danog\MadelineProto\Logger::log(['OK!'], \danog\MadelineProto\Logger::WARNING);
return true;
} catch (\danog\MadelineProto\Exception $e) {
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
}
}
switch ($x) {
case 0:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue;
case 1:
$this->settings[$dc_config_number]['proxy'] = '\Socket';
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue;
case 2:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue;
default:
return false;
case 0:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection with ' . ($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4') . '...'], \danog\MadelineProto\Logger::WARNING);
continue;
case 1:
$this->settings[$dc_config_number]['proxy'] = '\\Socket';
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with ' . ($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4') . '...'], \danog\MadelineProto\Logger::WARNING);
continue;
case 2:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6'];
\danog\MadelineProto\Logger::log(['Connection failed, retrying connection without the proxy with ' . ($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4') . '...'], \danog\MadelineProto\Logger::WARNING);
continue;
default:
return false;
}
} while (++$x);
return false;
}
public function get_dcs($all = true)
{
$test = $this->settings['all']['test_mode'] ? 'test' : 'main';
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
}
/*
public function &__get($name)
{
return $this->sockets[$this->curdc]->{$name};
}
public function __set($name, $value)
{
$this->sockets[$this->curdc]->{$name} = &$value;
}
public function __isset($name)
{
return isset($this->sockets[$this->curdc]->{$name});
}
public function __call($name, $arguments)
{
return call_user_func_array([$this->sockets[$this->curdc], $name], $arguments);
}*/
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
class DocsBuilder
@ -18,12 +18,10 @@ class DocsBuilder
use \danog\MadelineProto\DocsBuilder\Methods;
use \danog\MadelineProto\DocsBuilder\Constructors;
use Tools;
public $td = false;
public function __construct($settings)
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
$this->construct_TL($settings['tl_schema']);
if (isset($settings['tl_schema']['td']) && !isset($settings['tl_schema']['telegram'])) {
$this->constructors = $this->td_constructors;
@ -37,29 +35,24 @@ class DocsBuilder
chdir($this->settings['output_dir']);
$this->index = $settings['readme'] ? 'README.md' : 'index.md';
}
public $types = [];
public $any = '*';
public function end($what)
{
return end($what);
}
public function escape($hwat)
{
return str_replace('_', '\_', $hwat);
return str_replace('_', '\\_', $hwat);
}
public function mk_docs()
{
\danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents($this->index, '---
title: '.$this->settings['title'].'
description: '.$this->settings['description'].'
title: ' . $this->settings['title'] . '
description: ' . $this->settings['description'] . '
---
# '.$this->settings['description'].'
# ' . $this->settings['description'] . '
[Methods](methods/)
@ -72,19 +65,15 @@ description: '.$this->settings['description'].'
');
$this->mk_methodS();
$this->mk_constructors();
foreach (glob('types/*') as $unlink) {
unlink($unlink);
}
if (file_exists('types')) {
rmdir('types');
}
mkdir('types');
ksort($this->types);
$index = '';
\danog\MadelineProto\Logger::log(['Generating types documentation...'], \danog\MadelineProto\Logger::NOTICE);
$last_namespace = '';
foreach ($this->types as $otype => $keys) {
@ -92,14 +81,14 @@ description: '.$this->settings['description'].'
//$br = $new_namespace != $last_namespace ? '***<br><br>' : '';
$type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $otype);
$type = preg_replace('/.*_of_/', '', $type);
$index .= '['.str_replace('_', '\_', $type).']('.$type.'.md)<a name="'.$type.'"></a>
$index .= '[' . str_replace('_', '\\_', $type) . '](' . $type . '.md)<a name="' . $type . '"></a>
';
$constructors = '';
foreach ($keys['constructors'] as $data) {
$predicate = str_replace('.', '_', $data['predicate']).(isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '');
$md_predicate = str_replace('_', '\_', $predicate);
$constructors .= '['.$md_predicate.'](../constructors/'.$predicate.'.md)
$predicate = str_replace('.', '_', $data['predicate']) . (isset($data['layer']) && $data['layer'] !== '' ? '_' . $data['layer'] : '');
$md_predicate = str_replace('_', '\\_', $predicate);
$constructors .= '[' . $md_predicate . '](../constructors/' . $predicate . '.md)
';
}
@ -107,50 +96,38 @@ description: '.$this->settings['description'].'
foreach ($keys['methods'] as $data) {
$name = str_replace('.', '_', $data['method']);
$md_name = str_replace('_', '->', $name);
$methods .= '[$MadelineProto->'.$md_name.'](../methods/'.$name.'.md)
$methods .= '[$MadelineProto->' . $md_name . '](../methods/' . $name . '.md)
';
}
$description = isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] : ('constructors and methods of typr '.$type);
$description = isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] : 'constructors and methods of typr ' . $type;
$header = '---
title: '.$type.'
description: constructors and methods of type '.$type.'
title: ' . $type . '
description: constructors and methods of type ' . $type . '
---
## Type: '.str_replace('_', '\_', $type).'
## Type: ' . str_replace('_', '\\_', $type) . '
[Back to types index](index.md)
';
$header .= isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype].PHP_EOL.PHP_EOL : '';
$header .= isset($this->td_descriptions['types'][$otype]) ? $this->td_descriptions['types'][$otype] . PHP_EOL . PHP_EOL : '';
if (!isset($this->settings['td'])) {
if (in_array($type, ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) {
$header .= 'The following syntaxes can also be used:
```
$'.$type." = '@username'; // Username
$' . $type . " = '@username'; // Username\n\n\$" . $type . ' = 44700; // bot API id (users)
$' . $type . ' = -492772765; // bot API id (chats)
$' . $type . ' = -10038575794; // bot API id (channels)
$".$type.' = 44700; // bot API id (users)
$'.$type.' = -492772765; // bot API id (chats)
$'.$type.' = -10038575794; // bot API id (channels)
$'.$type." = 'user#44700'; // tg-cli style id (users)
$".$type." = 'chat#492772765'; // tg-cli style id (chats)
$".$type." = 'channel#38575794'; // tg-cli style id (channels)
```
A [Chat](Chat.md), a [User](User.md), an [InputPeer](InputPeer.md), an [InputUser](InputUser.md), an [InputChannel](InputChannel.md), a [Peer](Peer.md), or a [Chat](Chat.md) object can also be used.
";
$' . $type . " = 'user#44700'; // tg-cli style id (users)\n\$" . $type . " = 'chat#492772765'; // tg-cli style id (chats)\n\$" . $type . " = 'channel#38575794'; // tg-cli style id (channels)\n```\n\nA [Chat](Chat.md), a [User](User.md), an [InputPeer](InputPeer.md), an [InputUser](InputUser.md), an [InputChannel](InputChannel.md), a [Peer](Peer.md), or a [Chat](Chat.md) object can also be used.\n\n\n";
}
if (in_array($type, ['InputEncryptedChat'])) {
$header .= 'The following syntax can also be used:
```
$'.$type.' = -147286699; // Numeric chat id returned by request_secret_chat, can be positive or negative
$' . $type . ' = -147286699; // Numeric chat id returned by request_secret_chat, can be positive or negative
```
@ -162,7 +139,7 @@ $'.$type.' = -147286699; // Numeric chat id returned by request_secret_chat, can
To click these buttons simply run the `click` method:
```
$result = $'.$type.'->click();
$result = $' . $type . '->click();
```
`$result` can be one of the following:
@ -182,19 +159,19 @@ $result = $'.$type.'->click();
}
$constructors = '### Possible values (constructors):
'.$constructors.'
' . $constructors . '
';
$methods = '### Methods that return an object of this type (methods):
'.$methods.'
' . $methods . '
';
if (!isset($this->settings['td'])) {
if (in_array($type, ['PhoneCall'])) {
$methods = '';
$constructors = '';
$header .= 'This is an object of type `\danog\MadelineProto\VoIP`.
$header .= 'This is an object of type `\\danog\\MadelineProto\\VoIP`.
It will only be available if the [php-libtgvoip](https://github.com/danog/php-libtgvoip) extension is installed, see [the main docs](https://daniil.it/MadelineProto#calls) for an easy installation script.
@ -362,8 +339,8 @@ Example:
```
$call->configuration["log_file_path"] = "logs".$call->getOtherID().".log"; // Default is /dev/null
$call->configuration["stats_dump_file_path"] = "stats".$call->getOtherID().".log"; // Default is /dev/null
$call->configuration["network_type"] = \danog\MadelineProto\VoIP::NET_TYPE_WIFI; // Default is NET_TYPE_ETHERNET
$call->configuration["data_saving"] = \danog\MadelineProto\VoIP::DATA_SAVING_MOBILE; // Default is DATA_SAVING_NEVER
$call->configuration["network_type"] = \\danog\\MadelineProto\\VoIP::NET_TYPE_WIFI; // Default is NET_TYPE_ETHERNET
$call->configuration["data_saving"] = \\danog\\MadelineProto\\VoIP::DATA_SAVING_MOBILE; // Default is DATA_SAVING_NEVER
$call->parseConfig(); // Always call this after changing settings
```
@ -374,16 +351,14 @@ After modifying it, you must always parse the new configuration with a call to `
';
}
}
if (file_exists('types/'.$type.'.md')) {
if (file_exists('types/' . $type . '.md')) {
\danog\MadelineProto\Logger::log([$type]);
}
file_put_contents('types/'.$type.'.md', $header.$constructors.$methods);
file_put_contents('types/' . $type . '.md', $header . $constructors . $methods);
$last_namespace = $new_namespace;
}
\danog\MadelineProto\Logger::log(['Generating types index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents('types/'.$this->index, '---
file_put_contents('types/' . $this->index, '---
title: Types
description: List of types
---
@ -391,10 +366,8 @@ description: List of types
[Back to API documentation index](..)
'.$index);
' . $index);
\danog\MadelineProto\Logger::log(['Generating additional types...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents('types/string.md', '---
title: string
description: A UTF8 string of variable length
@ -413,7 +386,6 @@ description: A string of variable length
A string of bytes of variable length, with length smaller than or equal to 16777215.
');
file_put_contents('types/int.md', '---
title: integer
description: A 32 bit signed integer ranging from -2147483648 to 2147483647
@ -423,7 +395,6 @@ description: A 32 bit signed integer ranging from -2147483648 to 2147483647
A 32 bit signed integer ranging from `-2147483648` to `2147483647`.
');
file_put_contents('types/int53.md', '---
title: integer
description: A 53 bit signed integer
@ -433,7 +404,6 @@ description: A 53 bit signed integer
A 53 bit signed integer.
');
file_put_contents('types/long.md', '---
title: long
description: A 32 bit signed integer ranging from -9223372036854775808 to 9223372036854775807
@ -443,7 +413,6 @@ description: A 32 bit signed integer ranging from -9223372036854775808 to 922337
A 64 bit signed integer ranging from `-9223372036854775808` to `9223372036854775807`.
');
file_put_contents('types/int128.md', '---
title: int128
description: A 128 bit signed integer
@ -453,7 +422,6 @@ description: A 128 bit signed integer
A 128 bit signed integer represented in little-endian base256 (`string`) format.
');
file_put_contents('types/int256.md', '---
title: int256
description: A 256 bit signed integer
@ -463,7 +431,6 @@ description: A 256 bit signed integer
A 256 bit signed integer represented in little-endian base256 (`string`) format.
');
file_put_contents('types/int512.md', '---
title: int512
description: A 512 bit signed integer
@ -473,7 +440,6 @@ description: A 512 bit signed integer
A 512 bit signed integer represented in little-endian base256 (`string`) format.
');
file_put_contents('types/double.md', '---
title: double
description: A double precision floating point number
@ -483,7 +449,6 @@ description: A double precision floating point number
A double precision floating point number, single precision can also be used (float).
');
file_put_contents('types/!X.md', '---
title: !X
description: Represents a TL serialized payload
@ -493,7 +458,6 @@ description: Represents a TL serialized payload
Represents a TL serialized payload.
');
file_put_contents('types/X.md', '---
title: X
description: Represents a TL serialized payload
@ -503,7 +467,6 @@ description: Represents a TL serialized payload
Represents a TL serialized payload.
');
file_put_contents('constructors/boolFalse.md', '---
title: boolFalse
description: Represents a boolean with value equal to false
@ -513,7 +476,6 @@ description: Represents a boolean with value equal to false
Represents a boolean with value equal to `false`.
');
file_put_contents('constructors/boolTrue.md', '---
title: boolTrue
description: Represents a boolean with value equal to true
@ -523,7 +485,6 @@ description: Represents a boolean with value equal to true
Represents a boolean with value equal to `true`.
');
file_put_contents('constructors/null.md', '---
title: null
description: Represents a null value
@ -533,7 +494,6 @@ description: Represents a null value
Represents a `null` value.
');
file_put_contents('types/Bool.md', '---
title: Bool
description: Represents a boolean.
@ -543,7 +503,6 @@ description: Represents a boolean.
Represents a boolean.
');
file_put_contents('types/DataJSON.md', '---
title: DataJSON
description: Any json-encodable data
@ -553,7 +512,6 @@ description: Any json-encodable data
Any json-encodable data.
');
\danog\MadelineProto\Logger::log(['Done!'], \danog\MadelineProto\Logger::NOTICE);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,14 +10,13 @@ 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\DocsBuilder;
trait Constructors
{
public function mk_constructors()
{
foreach (glob('constructors/'.$this->any) as $unlink) {
foreach (glob('constructors/' . $this->any) as $unlink) {
unlink($unlink);
}
if (file_exists('constructors')) {
@ -32,12 +32,11 @@ trait Constructors
$data['layer'] = '';
}
$got[$id] = '';
/*
if (preg_match('/%/', $type)) {
$type = $this->constructors->find_by_type(str_replace('%', '', $type))['predicate'];
}*/
$layer = isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '';
if (preg_match('/%/', $type)) {
$type = $this->constructors->find_by_type(str_replace('%', '', $type))['predicate'];
}*/
$layer = isset($data['layer']) && $data['layer'] !== '' ? '_' . $data['layer'] : '';
$type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']);
$php_type = preg_replace('/.*_of_/', '', $type);
$constructor = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['predicate']);
@ -58,22 +57,20 @@ trait Constructors
$param['type'] = 'DecryptedMessage';
}
$type_or_subtype = isset($param['subtype']) ? 'subtype' : 'type';
$type_or_bare_type = (ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512'])) ? 'types' : 'constructors';
$type_or_bare_type = ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512']) ? 'types' : 'constructors';
$param[$type_or_subtype] = str_replace(['.', 'true', 'false'], ['_', 'Bool', 'Bool'], $param[$type_or_subtype]);
if (preg_match('/%/', $param[$type_or_subtype])) {
$param[$type_or_subtype] = $this->constructors->find_by_type(str_replace('%', '', $param[$type_or_subtype]))['predicate'];
}
if (substr($param[$type_or_subtype], -1) === '>') {
$param[$type_or_subtype] = substr($param[$type_or_subtype], 0, -1);
}
$params .= "'".$param['name']."' => ";
$param[$type_or_subtype] = '['.$this->escape($param[$type_or_subtype]).'](../'.$type_or_bare_type.'/'.$param[$type_or_subtype].'.md)';
$params .= (isset($param['subtype']) ? '\['.$param[$type_or_subtype].'\]' : $param[$type_or_subtype]).', ';
$params .= "'" . $param['name'] . "' => ";
$param[$type_or_subtype] = '[' . $this->escape($param[$type_or_subtype]) . '](../' . $type_or_bare_type . '/' . $param[$type_or_subtype] . '.md)';
$params .= (isset($param['subtype']) ? '\\[' . $param[$type_or_subtype] . '\\]' : $param[$type_or_subtype]) . ', ';
}
$md_constructor = str_replace('_', '\_', $constructor.$layer);
$this->docs_constructors[$constructor] = '[$'.$md_constructor.'](../constructors/'.$php_constructor.$layer.'.md) = \['.$params.'\];<a name="'.$constructor.$layer.'"></a>
$md_constructor = str_replace('_', '\\_', $constructor . $layer);
$this->docs_constructors[$constructor] = '[$' . $md_constructor . '](../constructors/' . $php_constructor . $layer . '.md) = \\[' . $params . '\\];<a name="' . $constructor . $layer . '"></a>
';
$table = empty($data['params']) ? '' : '### Attributes:
@ -104,49 +101,47 @@ trait Constructors
$ptype = str_replace('.', '_', $param[isset($param['subtype']) ? 'subtype' : 'type']);
//$type_or_bare_type = 'types';
/*if (isset($param['subtype'])) {
if ($param['type'] === 'vector') {
$type_or_bare_type = 'constructors';
}
}*/
if ($param['type'] === 'vector') {
$type_or_bare_type = 'constructors';
}
}*/
if (preg_match('/%/', $ptype)) {
$ptype = $this->constructors->find_by_type(str_replace('%', '', $ptype))['predicate'];
}
$type_or_bare_type = ((ctype_upper($this->end(explode('_', $ptype))[0]) || in_array($ptype, ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512'])) && $ptype !== 'MTmessage') ? 'types' : 'constructors';
$type_or_bare_type = (ctype_upper($this->end(explode('_', $ptype))[0]) || in_array($ptype, ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int53', 'int', 'long', 'int128', 'int256', 'int512'])) && $ptype !== 'MTmessage' ? 'types' : 'constructors';
if (substr($ptype, -1) === '>') {
$ptype = substr($ptype, 0, -1);
}
switch ($ptype) {
case 'true':
case 'false':
$ptype = 'Bool';
}
$table .= '|'.str_replace('_', '\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.str_replace('_', '\_', $ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']).'Empty') ? 'Optional' : 'Yes').'|';
$table .= '|' . str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . str_replace('_', '\\_', $ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . (isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']) . 'Empty') ? 'Optional' : 'Yes') . '|';
if (isset($this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']])) {
$table .= $this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']].'|';
$table .= $this->td_descriptions['constructors'][$data['predicate']]['params'][$param['name']] . '|';
}
$table .= PHP_EOL;
$pptype = in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"'.$ptype.'"' : $ptype;
$params .= ", '".$param['name']."' => ";
$params .= (isset($param['subtype']) ? '['.$pptype.']' : $pptype);
$lua_params .= ', '.$param['name'].'=';
$lua_params .= (isset($param['subtype']) ? '{'.$pptype.'}' : $pptype);
$pwr_params .= ', "'.$param['name'].'": '.(isset($param['subtype']) ? '['.$ppptype.']' : $ppptype);
$pptype = in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"' . $ptype . '"' : $ptype;
$params .= ", '" . $param['name'] . "' => ";
$params .= isset($param['subtype']) ? '[' . $pptype . ']' : $pptype;
$lua_params .= ', ' . $param['name'] . '=';
$lua_params .= isset($param['subtype']) ? '{' . $pptype . '}' : $pptype;
$pwr_params .= ', "' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype);
if ($param['name'] === 'reply_markup') {
$hasreplymarkup = true;
}
}
$params = "['_' => '".$data['predicate']."'".$params.']';
$lua_params = "{_='".$data['predicate']."'".$lua_params.'}';
$pwr_params = '{"_": "'.$data['predicate'].'"'.$pwr_params.'}';
$description = isset($this->td_descriptions['constructors'][$data['predicate']]) ? $this->td_descriptions['constructors'][$data['predicate']]['description'] : ($constructor.' attributes, type and example');
$params = "['_' => '" . $data['predicate'] . "'" . $params . ']';
$lua_params = "{_='" . $data['predicate'] . "'" . $lua_params . '}';
$pwr_params = '{"_": "' . $data['predicate'] . '"' . $pwr_params . '}';
$description = isset($this->td_descriptions['constructors'][$data['predicate']]) ? $this->td_descriptions['constructors'][$data['predicate']]['description'] : $constructor . ' attributes, type and example';
$header = '---
title: '.$data['predicate'].'
description: '.$description.'
title: ' . $data['predicate'] . '
description: ' . $description . '
---
## Constructor: '.str_replace('_', '\_', $data['predicate'].$layer).'
## Constructor: ' . str_replace('_', '\\_', $data['predicate'] . $layer) . '
[Back to constructors index](index.md)
@ -157,9 +152,9 @@ description: '.$description.'
';
if (isset($this->td_descriptions['constructors'][$data['predicate']])) {
$header .= $this->td_descriptions['constructors'][$data['predicate']]['description'].PHP_EOL.PHP_EOL;
$header .= $this->td_descriptions['constructors'][$data['predicate']]['description'] . PHP_EOL . PHP_EOL;
}
$type = '### Type: ['.str_replace('_', '\_', $php_type).'](../types/'.$php_type.'.md)
$type = '### Type: [' . str_replace('_', '\\_', $php_type) . '](../types/' . $php_type . '.md)
';
@ -168,13 +163,13 @@ description: '.$description.'
$example = '### Example:
```
$'.$constructor.$layer.' = '.$params.';
$' . $constructor . $layer . ' = ' . $params . ';
```
[PWRTelegram](https://pwrtelegram.xyz) json-encoded version:
```
'.$pwr_params.'
' . $pwr_params . '
```
@ -182,13 +177,12 @@ Or, if you\'re into Lua:
```
'.$constructor.$layer.'='.$lua_params.'
' . $constructor . $layer . '=' . $lua_params . '
```
';
if ($hasreplymarkup) {
$example .= '
## Usage of reply_markup
@ -233,7 +227,7 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
';
}
}
file_put_contents('constructors/'.$constructor.$layer.'.md', $header.$table.$type.$example);
file_put_contents('constructors/' . $constructor . $layer . '.md', $header . $table . $type . $example);
}
\danog\MadelineProto\Logger::log(['Generating constructors index...'], \danog\MadelineProto\Logger::NOTICE);
ksort($this->docs_constructors);
@ -242,16 +236,16 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
$new_namespace = preg_replace('/_.*/', '', $constructor);
$br = $new_namespace != $last_namespace ? '***
<br><br>' : '';
$value = $br.$value;
$value = $br . $value;
$last_namespace = $new_namespace;
}
file_put_contents('constructors/'.$this->index, '---
file_put_contents('constructors/' . $this->index, '---
title: Constructors
description: List of constructors
---
# Constructors
[Back to API documentation index](..)
'.implode('', $this->docs_constructors));
' . implode('', $this->docs_constructors));
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\DocsBuilder;
trait Methods
@ -19,18 +19,14 @@ trait Methods
$bots = json_decode(file_get_contents('https://rpc.pwrtelegram.xyz/?bot'), true)['result'];
$errors = json_decode(file_get_contents('https://rpc.pwrtelegram.xyz/?all'), true);
$errors['result'] = array_merge_recursive(...$errors['result']);
foreach (glob('methods/'.$this->any) as $unlink) {
foreach (glob('methods/' . $this->any) as $unlink) {
unlink($unlink);
}
if (file_exists('methods')) {
rmdir('methods');
}
mkdir('methods');
$this->docs_methods = [];
\danog\MadelineProto\Logger::log(['Generating methods documentation...'], \danog\MadelineProto\Logger::NOTICE);
foreach ($this->methods->by_id as $id => $data) {
$method = str_replace('.', '_', $data['method']);
@ -43,7 +39,6 @@ trait Methods
if (!in_array($data, $this->types[$php_type]['methods'])) {
$this->types[$php_type]['methods'][] = $data;
}
$params = '';
foreach ($data['params'] as $param) {
if (in_array($param['name'], ['flags', 'random_id', 'random_bytes'])) {
@ -57,19 +52,15 @@ trait Methods
$param['type'] = 'InputPeer';
}
$type_or_subtype = isset($param['subtype']) ? 'subtype' : 'type';
$type_or_bare_type = (ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53'])) ? 'types' : 'constructors';
$type_or_bare_type = ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53']) ? 'types' : 'constructors';
$param[$type_or_subtype] = str_replace(['.', 'true', 'false'], ['_', 'Bool', 'Bool'], $param[$type_or_subtype]);
$param[$type_or_subtype] = '['.$this->escape($param[$type_or_subtype]).'](../'.$type_or_bare_type.'/'.$param[$type_or_subtype].'.md)';
$params .= "'".$param['name']."' => ".(isset($param['subtype']) ? '\['.$param[$type_or_subtype].'\]' : $param[$type_or_subtype]).', ';
$param[$type_or_subtype] = '[' . $this->escape($param[$type_or_subtype]) . '](../' . $type_or_bare_type . '/' . $param[$type_or_subtype] . '.md)';
$params .= "'" . $param['name'] . "' => " . (isset($param['subtype']) ? '\\[' . $param[$type_or_subtype] . '\\]' : $param[$type_or_subtype]) . ', ';
}
$md_method = '['.$php_method.']('.$method.'.md)';
$this->docs_methods[$method] = '$MadelineProto->'.$md_method.'(\['.$params.'\]) === [$'.str_replace('_', '\_', $type).'](../types/'.$php_type.'.md)<a name="'.$method.'"></a>
$md_method = '[' . $php_method . '](' . $method . '.md)';
$this->docs_methods[$method] = '$MadelineProto->' . $md_method . '(\\[' . $params . '\\]) === [$' . str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md)<a name="' . $method . '"></a>
';
$params = '';
$lua_params = '';
$pwr_params = '';
@ -86,7 +77,6 @@ trait Methods
|----------|---------------|----------|-------------|
';
}
$hasentities = false;
$hasreplymarkup = false;
$hasmessage = false;
@ -101,30 +91,26 @@ trait Methods
if ($param['name'] === 'chat_id' && $data['method'] !== 'messages.discardEncryption' && !isset($this->settings['td'])) {
$param['type'] = 'InputPeer';
}
$ptype = str_replace('.', '_', $param[$type_or_subtype = isset($param['subtype']) ? 'subtype' : 'type']);
switch ($ptype) {
case 'true':
case 'false':
$ptype = 'Bool';
}
$type_or_bare_type = (ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53'])) ? 'types' : 'constructors';
$table .= '|'.str_replace('_', '\_', $param['name']).'|'.(isset($param['subtype']) ? 'Array of ' : '').'['.str_replace('_', '\_', $ptype).'](../'.$type_or_bare_type.'/'.$ptype.'.md) | '.(isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']).'Empty') ? 'Optional' : 'Yes').'|';
$type_or_bare_type = ctype_upper($this->end(explode('.', $param[$type_or_subtype]))[0]) || in_array($param[$type_or_subtype], ['!X', 'X', 'bytes', 'true', 'false', 'double', 'string', 'Bool', 'int', 'long', 'int128', 'int256', 'int512', 'int53']) ? 'types' : 'constructors';
$table .= '|' . str_replace('_', '\\_', $param['name']) . '|' . (isset($param['subtype']) ? 'Array of ' : '') . '[' . str_replace('_', '\\_', $ptype) . '](../' . $type_or_bare_type . '/' . $ptype . '.md) | ' . (isset($param['pow']) || $this->constructors->find_by_predicate(lcfirst($param['type']) . 'Empty') ? 'Optional' : 'Yes') . '|';
if (isset($this->td_descriptions['methods'][$data['method']])) {
$table .= $this->td_descriptions['methods'][$data['method']]['params'][$param['name']].'|';
$table .= $this->td_descriptions['methods'][$data['method']]['params'][$param['name']] . '|';
}
$table .= PHP_EOL;
$pptype = in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"'.$ptype.'"' : $ptype;
$params .= "'".$param['name']."' => ";
$params .= (isset($param['subtype']) ? '['.$pptype.']' : $pptype).', ';
$json_params .= '"'.$param['name'].'": '.(isset($param['subtype']) ? '['.$ppptype.']' : $ppptype).', ';
$pwr_params .= $param['name'].' - Json encoded '.(isset($param['subtype']) ? ' array of '.$ptype : $ptype)."\n\n";
$lua_params .= $param['name'].'=';
$lua_params .= (isset($param['subtype']) ? '{'.$pptype.'}' : $pptype).', ';
$pptype = in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"' . $ptype . '"' : $ptype;
$params .= "'" . $param['name'] . "' => ";
$params .= (isset($param['subtype']) ? '[' . $pptype . ']' : $pptype) . ', ';
$json_params .= '"' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype) . ', ';
$pwr_params .= $param['name'] . ' - Json encoded ' . (isset($param['subtype']) ? ' array of ' . $ptype : $ptype) . "\n\n";
$lua_params .= $param['name'] . '=';
$lua_params .= (isset($param['subtype']) ? '{' . $pptype . '}' : $pptype) . ', ';
if ($param['name'] === 'reply_markup') {
$hasreplymarkup = true;
}
@ -133,7 +119,7 @@ trait Methods
}
if ($param['name'] === 'entities') {
$hasentities = true;
$table .= '|parse\_mode| [string](../types/string.md) | Optional |
$table .= '|parse\\_mode| [string](../types/string.md) | Optional |
';
$params .= "'parse_mode' => 'string', ";
$lua_params .= "parse_mode='string', ";
@ -141,19 +127,19 @@ trait Methods
$pwr_params = "parse_mode - string\n";
}
}
$description = isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] : ($data['method'].' parameters, return type and example');
$description = isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] : $data['method'] . ' parameters, return type and example';
$header = '---
title: '.$data['method'].'
description: '.$description.'
title: ' . $data['method'] . '
description: ' . $description . '
---
## Method: '.str_replace('_', '\_', $data['method']).'
## Method: ' . str_replace('_', '\\_', $data['method']) . '
[Back to methods index](index.md)
';
if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']])) {
$header .= '**'.\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]."**\n\n\n\n\n";
file_put_contents('methods/'.$method.'.md', $header);
$header .= '**' . \danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']] . "**\n\n\n\n\n";
file_put_contents('methods/' . $method . '.md', $header);
continue;
}
if ($this->td) {
@ -162,17 +148,17 @@ description: '.$description.'
';
}
$header .= isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'].PHP_EOL.PHP_EOL : '';
$header .= isset($this->td_descriptions['methods'][$data['method']]) ? $this->td_descriptions['methods'][$data['method']]['description'] . PHP_EOL . PHP_EOL : '';
$table .= '
';
$return = '### Return type: ['.str_replace('_', '\_', $type).'](../types/'.$php_type.'.md)
$return = '### Return type: [' . str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md)
';
$bot = !in_array($data['method'], $bots);
$example = '';
if (!isset($this->settings['td'])) {
$example .= '### Can bots use this method: **'.($bot ? 'YES' : 'NO')."**\n\n\n";
$example .= '### Can bots use this method: **' . ($bot ? 'YES' : 'NO') . "**\n\n\n";
if (isset($errors['result'][$data['method']])) {
$example .= '### Errors this method can return:
@ -180,7 +166,7 @@ description: '.$description.'
|----------|---------------|
';
foreach ($errors['result'][$data['method']] as $error) {
$example .= '|'.$error.'|'.$errors['human_result'][$error][0].'|'."\n";
$example .= '|' . $error . '|' . $errors['human_result'][$error][0] . '|' . "\n";
}
$example .= "\n\n";
}
@ -188,46 +174,46 @@ description: '.$description.'
```
$MadelineProto = new \danog\MadelineProto\API();
$MadelineProto = new \\danog\\MadelineProto\\API();
$MadelineProto->session = \'mySession.madeline\';
'.($bot ? 'if (isset($token)) { // Login as a bot
' . ($bot ? 'if (isset($token)) { // Login as a bot
$MadelineProto->bot_login($token);
}
' : '').'if (isset($number)) { // Login as a user
' : '') . 'if (isset($number)) { // Login as a user
$MadelineProto->phone_login($number);
$code = readline(\'Enter the code you received: \'); // Or do this in two separate steps in an HTTP API
$MadelineProto->complete_phone_login($code);
}
$'.$type.' = $MadelineProto->'.$php_method.'(['.$params.']);
$' . $type . ' = $MadelineProto->' . $php_method . '([' . $params . ']);
```
Or, if you\'re using the [PWRTelegram HTTP API](https://pwrtelegram.xyz):
'.($bot ? '### As a bot:
' . ($bot ? '### As a bot:
POST/GET to `https://api.pwrtelegram.xyz/botTOKEN/madeline`
Parameters:
* method - '.$data['method'].'
* params - `{'.$json_params.'}`
* method - ' . $data['method'] . '
* params - `{' . $json_params . '}`
' : '').'
' : '') . '
### As a user:
POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/'.$data['method'].'`
POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/' . $data['method'] . '`
Parameters:
'.$pwr_params.'
' . $pwr_params . '
Or, if you\'re into Lua:
```
'.$type.' = '.$data['method'].'({'.$lua_params.'})
' . $type . ' = ' . $data['method'] . '({' . $lua_params . '})
```
');
@ -244,7 +230,7 @@ You can provide bot API reply_markup objects here.
$example .= '
## Return value
If the length of the provided message is bigger than 4096, the message will be split in chunks and the method will be called multiple times, with the same parameters (except for the message), and an array of ['.str_replace('_', '\_', $type).'](../types/'.$php_type.'.md) will be returned instead.
If the length of the provided message is bigger than 4096, the message will be split in chunks and the method will be called multiple times, with the same parameters (except for the message), and an array of [' . str_replace('_', '\\_', $type) . '](../types/' . $php_type . '.md) will be returned instead.
';
@ -284,22 +270,19 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
';
}
}
file_put_contents('methods/'.$method.'.md', $header.$table.$return.$example);
file_put_contents('methods/' . $method . '.md', $header . $table . $return . $example);
}
\danog\MadelineProto\Logger::log(['Generating methods index...'], \danog\MadelineProto\Logger::NOTICE);
ksort($this->docs_methods);
$last_namespace = '';
foreach ($this->docs_methods as $method => &$value) {
$new_namespace = preg_replace('/_.*/', '', $method);
$br = $new_namespace != $last_namespace ? '***
<br><br>' : '';
$value = $br.$value;
$value = $br . $value;
$last_namespace = $new_namespace;
}
file_put_contents('methods/'.$this->index, '---
file_put_contents('methods/' . $this->index, '---
title: Methods
description: List of methods
---
@ -329,6 +312,6 @@ $MadelineProto->[get_full_info](https://docs.madelineproto.xyz/get_full_info.htm
$MadelineProto->[get_self](https://docs.madelineproto.xyz/get_self.html)();
'.implode('', $this->docs_methods));
' . implode('', $this->docs_methods));
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,19 +10,16 @@ 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;
class Exception extends \Exception
{
use TL\PrettyException;
public static $rollbar = true;
public function __toString()
{
return $this->file === 'MadelineProto' ? $this->message : '\danog\MadelineProto\Exception'.($this->message !== '' ? ': ' : '').$this->message.' in '.$this->file.':'.$this->line.PHP_EOL.'Revision: '.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.$this->getTLTrace();
return $this->file === 'MadelineProto' ? $this->message : '\\danog\\MadelineProto\\Exception' . ($this->message !== '' ? ': ' : '') . $this->message . ' in ' . $this->file . ':' . $this->line . PHP_EOL . 'Revision: ' . @file_get_contents(__DIR__ . '/../../../.git/refs/heads/master') . PHP_EOL . 'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):' . PHP_EOL . $this->getTLTrace();
}
public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
{
$this->prettify_tl();
@ -36,8 +34,7 @@ class Exception extends \Exception
$this->line = $line;
}
parent::__construct($message, $code, $previous);
\danog\MadelineProto\Logger::log([$message.' in '.basename($this->file).':'.$this->line], \danog\MadelineProto\Logger::FATAL_ERROR);
\danog\MadelineProto\Logger::log([$message . ' in ' . basename($this->file) . ':' . $this->line], \danog\MadelineProto\Logger::FATAL_ERROR);
if (in_array($message, ['The session is corrupted!', 'Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden', 'The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.', 'File does not exist', 'Please install this fork of phpseclib: https://github.com/danog/phpseclib'])) {
return;
}
@ -48,7 +45,6 @@ class Exception extends \Exception
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));
}
}
/**
* ExceptionErrorHandler.
*
@ -58,9 +54,9 @@ class Exception extends \Exception
{
// If error is suppressed with @, don't throw an exception
if (error_reporting() === 0) {
return true; // return true to continue through the others error handlers
return true;
// return true to continue through the others error handlers
}
throw new self($errstr, $errno, null, $errfile, $errline);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -12,74 +13,14 @@ If not, see <http://www.gnu.org/licenses/>.
/*
* Logger class
*/
namespace danog\MadelineProto;
class Logger
{
const foreground = [
'default' => 39,
'black' => 30,
'red' => 31,
'green' => 32,
'yellow' => 33,
'blue' => 34,
'magenta' => 35,
'cyan' => 36,
'light_gray' => 37,
'dark_gray' => 90,
'light_red' => 91,
'light_green' => 92,
'light_yellow' => 93,
'light_blue' => 94,
'light_magenta' => 95,
'light_cyan' => 96,
'white' => 97,
];
const background = [
'default' => 49,
'black' => 40,
'red' => 41,
'magenta' => 45,
'yellow' => 43,
'green' => 42,
'blue' => 44,
'cyan' => 46,
'light_gray' => 47,
'dark_gray' => 100,
'light_red' => 101,
'light_green' => 102,
'light_yellow' => 103,
'light_blue' => 104,
'light_magenta' => 105,
'light_cyan' => 106,
'white' => 107,
];
const set = [
'bold' => 1,
'dim' => 2,
'underlined' => 3,
'blink' => 4,
'reverse' => 5,
'hidden' => 6,
];
const reset = [
'all' => 0,
'bold' => 21,
'dim' => 22,
'underlined' => 24,
'blink' => 25,
'reverse' => 26,
'hidden' => 28,
];
const foreground = ['default' => 39, 'black' => 30, 'red' => 31, 'green' => 32, 'yellow' => 33, 'blue' => 34, 'magenta' => 35, 'cyan' => 36, 'light_gray' => 37, 'dark_gray' => 90, 'light_red' => 91, 'light_green' => 92, 'light_yellow' => 93, 'light_blue' => 94, 'light_magenta' => 95, 'light_cyan' => 96, 'white' => 97];
const background = ['default' => 49, 'black' => 40, 'red' => 41, 'magenta' => 45, 'yellow' => 43, 'green' => 42, 'blue' => 44, 'cyan' => 46, 'light_gray' => 47, 'dark_gray' => 100, 'light_red' => 101, 'light_green' => 102, 'light_yellow' => 103, 'light_blue' => 104, 'light_magenta' => 105, 'light_cyan' => 106, 'white' => 107];
const set = ['bold' => 1, 'dim' => 2, 'underlined' => 3, 'blink' => 4, 'reverse' => 5, 'hidden' => 6];
const reset = ['all' => 0, 'bold' => 21, 'dim' => 22, 'underlined' => 24, 'blink' => 25, 'reverse' => 26, 'hidden' => 28];
public static $storage = [];
public static $mode = 1;
public static $optional = null;
@ -90,32 +31,27 @@ class Logger
public static $bigint = true;
public static $colors = [];
public static $isatty = false;
const ULTRA_VERBOSE = 5;
const VERBOSE = 4;
const NOTICE = 3;
const WARNING = 2;
const ERROR = 1;
const FATAL_ERROR = 0;
public static function class_exists()
{
self::$has_thread = class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread');
self::$BIG_ENDIAN = (pack('L', 1) === pack('N', 1));
self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread');
self::$BIG_ENDIAN = pack('L', 1) === pack('N', 1);
self::$bigint = PHP_INT_SIZE < 8;
preg_match('/const V = (\d+);/', @file_get_contents('https://raw.githubusercontent.com/danog/MadelineProto/master/src/danog/MadelineProto/MTProto.php'), $matches);
preg_match('/const V = (\\d+);/', @file_get_contents('https://raw.githubusercontent.com/danog/MadelineProto/master/src/danog/MadelineProto/MTProto.php'), $matches);
if (isset($matches[1]) && \danog\MadelineProto\MTProto::V < (int) $matches[1]) {
throw new \danog\MadelineProto\Exception(hex2bin(\danog\MadelineProto\Lang::$current_lang['v_error']), 0, null, 'MadelineProto', 1);
}
if (class_exists('\danog\MadelineProto\VoIP')) {
if (!defined('\danog\MadelineProto\VoIP::PHP_LIBTGVOIP_VERSION') || \danog\MadelineProto\VoIP::PHP_LIBTGVOIP_VERSION !== '1.1.2') {
if (class_exists('\\danog\\MadelineProto\\VoIP')) {
if (!defined('\\danog\\MadelineProto\\VoIP::PHP_LIBTGVOIP_VERSION') || \danog\MadelineProto\VoIP::PHP_LIBTGVOIP_VERSION !== '1.1.2') {
throw new \danog\MadelineProto\Exception(hex2bin(\danog\MadelineProto\Lang::$current_lang['v_tgerror']), 0, null, 'MadelineProto', 1);
}
try {
\Threaded::extend('\danog\MadelineProto\VoIP');
\Threaded::extend('\\danog\\MadelineProto\\VoIP');
} catch (\RuntimeException $e) {
}
}
@ -125,13 +61,11 @@ class Logger
self::$colors[self::WARNING] = implode(';', [self::foreground['white'], self::set['dim'], self::background['red']]);
self::$colors[self::ERROR] = implode(';', [self::foreground['white'], self::set['bold'], self::background['red']]);
self::$colors[self::FATAL_ERROR] = implode(';', [self::foreground['red'], self::set['bold'], self::background['light_gray']]);
try {
self::$isatty = defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT);
} catch (\danog\MadelineProto\Exception $e) {
}
}
/*
* Constructor function
* Accepts various logger modes:
@ -149,11 +83,10 @@ class Logger
}
self::$mode = $mode;
self::$optional = $optional;
self::$prefix = $prefix === '' ? '' : ', '.$prefix;
self::$prefix = $prefix === '' ? '' : ', ' . $prefix;
self::$level = $level;
self::class_exists();
}
public static function log($params, $level = self::NOTICE)
{
if (self::$mode === 4) {
@ -162,7 +95,6 @@ class Logger
if ($level > self::$level) {
return false;
}
$prefix = self::$prefix;
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
$prefix .= ' (t)';
@ -171,20 +103,20 @@ class Logger
if (!is_string($param)) {
$param = json_encode($param, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
$param = str_pad(basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['file'], '.php').$prefix.': ', 16 + strlen($prefix))."\t".$param;
$param = str_pad(basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)[0]['file'], '.php') . $prefix . ': ', 16 + strlen($prefix)) . "\t" . $param;
switch (self::$mode) {
case 1:
error_log($param);
break;
case 2:
error_log($param.PHP_EOL, 3, self::$optional);
error_log($param . PHP_EOL, 3, self::$optional);
break;
case 3:
echo self::$isatty ? "\033[".self::$colors[$level].'m'.$param."\033[0m".PHP_EOL : $param.PHP_EOL;
echo self::$isatty ? "\33[" . self::$colors[$level] . 'm' . $param . "\33[0m" . PHP_EOL : $param . PHP_EOL;
break;
default:
break;
}
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
class Lua
@ -18,7 +18,6 @@ class Lua
public $MadelineProto;
protected $Lua;
protected $script;
public function __magic_construct($script, $MadelineProto)
{
if (!file_exists($script)) {
@ -29,12 +28,10 @@ class Lua
$this->script = $script;
$this->__wakeup();
}
public function __sleep()
{
return ['MadelineProto', 'script'];
}
public function __wakeup()
{
$this->Lua = new \Lua($this->script);
@ -68,7 +65,6 @@ class Lua
$this->MadelineProto->{$namespace}->lua = true;
}
}
public function tdcli_function($params, $cb = null, $cb_extra = null)
{
$params = $this->MadelineProto->td_to_mtproto($this->MadelineProto->tdcli_to_td($params));
@ -79,10 +75,8 @@ class Lua
if (is_callable($cb)) {
$cb($this->MadelineProto->mtproto_to_td($result), $cb_extra);
}
return $result;
}
public function madeline_function($params, $cb = null, $cb_extra = null)
{
$result = $this->MadelineProto->API->method_call($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]);
@ -90,15 +84,12 @@ class Lua
$cb($result, $cb_extra);
}
self::convert_objects($result);
return $result;
}
public function tdcli_update_callback($update)
{
$this->Lua->tdcli_update_callback($this->MadelineProto->mtproto_to_tdcli($update));
}
private function convert_array($array)
{
if (!is_array($value)) {
@ -110,29 +101,23 @@ class Lua
}, array_flip($array)));
}
}
private function is_sequential(array $arr)
{
if ([] === $arr) {
return false;
}
return isset($arr[0]) && array_keys($arr) === range(0, count($arr) - 1);
}
public function __get($name)
{
if ($name === 'API') {
return $this->MadelineProto->API;
}
return $this->Lua->{$name};
}
public function __call($name, $params)
{
self::convert_objects($params);
try {
return $this->Lua->{$name}(...$params);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -151,16 +136,14 @@ class Lua
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
}
}
public function __set($name, $value)
{
return $this->Lua->{$name} = $value;
}
public static function convert_objects(&$data)
{
array_walk_recursive($data, function (&$value, $key) {
if (is_object($value) && !($value instanceof \phpseclib\Math\BigInteger)) {
if (is_object($value) && !$value instanceof \phpseclib\Math\BigInteger) {
$newval = [];
foreach (get_class_methods($value) as $name) {
$newval[$name] = [$value, $name];
@ -178,4 +161,4 @@ class Lua
}
});
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -21,28 +21,25 @@ trait AckHandler
{
// The server acknowledges that it received my message
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);
\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);
return false;
}
return $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['ack'] = true;
}
public function ack_incoming_message_id($message_id, $datacenter)
{
// I let the server know that I received its message
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) {
\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);
\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->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") { // || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0") {
// || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
return;
}
$this->datacenter->sockets[$datacenter]->ack_queue[] = $message_id;
//$this->object_call('msgs_ack', ['msg_ids' => [$message_id]], ['datacenter' => $datacenter]);
return true; //$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true;
return true;
//$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -26,7 +26,6 @@ trait AuthKeyHandler
for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
try {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['req_pq']], \danog\MadelineProto\Logger::VERBOSE);
/**
* ***********************************************************************
* Make pq request, DH exchange initiation.
@ -45,342 +44,257 @@ trait AuthKeyHandler
* ]
*/
$nonce = $this->random(16);
$ResPQ = $this->method_call($req_pq,
[
'nonce' => $nonce,
],
['datacenter' => $datacenter]
);
$ResPQ = $this->method_call($req_pq, ['nonce' => $nonce], ['datacenter' => $datacenter]);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
if ($ResPQ['nonce'] !== $nonce) {
throw new \danog\MadelineProto\SecurityException('wrong nonce');
}
/*
* ***********************************************************************
* Find our key in the server_public_key_fingerprints vector
*/
* ***********************************************************************
* Find our key in the server_public_key_fingerprints vector
*/
foreach ($this->rsa_keys as $curkey) {
if (in_array($curkey->fp, $ResPQ['server_public_key_fingerprints'])) {
$key = $curkey;
}
}
if (!isset($key)) {
throw new \danog\MadelineProto\SecurityException("Couldn't find any of our keys in the server_public_key_fingerprints vector.");
}
$pq_bytes = $ResPQ['pq'];
$server_nonce = $ResPQ['server_nonce'];
/*
* ***********************************************************************
* Compute p and q
*/
* ***********************************************************************
* Compute p and 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->equals($p->multiply($q))) {
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q. Original pq: $pq, computed p: $p, computed q: $q, computed pq: ".$p->multiply($q));
throw new \danog\MadelineProto\SecurityException("couldn't compute p and q. Original pq: {$pq}, computed p: {$p}, computed q: {$q}, computed pq: " . $p->multiply($q));
}
\danog\MadelineProto\Logger::log(['Factorization '.$pq.' = '.$p.' * '.$q], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Factorization ' . $pq . ' = ' . $p . ' * ' . $q], \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Serialize object for req_DH_params
*/
* ***********************************************************************
* Serialize object for req_DH_params
*/
$p_bytes = $p->toBytes();
$q_bytes = $q->toBytes();
$new_nonce = $this->random(32);
$data_unserialized = [
'pq' => $pq_bytes,
'p' => $p_bytes,
'q' => $q_bytes,
'nonce' => $nonce,
'server_nonce' => $server_nonce,
'new_nonce' => $new_nonce,
'expires_in' => $expires_in,
];
$p_q_inner_data = $this->serialize_object(['type' => 'p_q_inner_data'.($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
$data_unserialized = ['pq' => $pq_bytes, 'p' => $p_bytes, 'q' => $q_bytes, 'nonce' => $nonce, 'server_nonce' => $server_nonce, 'new_nonce' => $new_nonce, 'expires_in' => $expires_in];
$p_q_inner_data = $this->serialize_object(['type' => 'p_q_inner_data' . ($expires_in < 0 ? '' : '_temp')], $data_unserialized, 'p_q_inner_data');
/*
* ***********************************************************************
* Encrypt serialized object
*/
* ***********************************************************************
* Encrypt serialized object
*/
$sha_digest = sha1($p_q_inner_data, true);
$random_bytes = $this->random(255 - strlen($p_q_inner_data) - strlen($sha_digest));
$to_encrypt = $sha_digest.$p_q_inner_data.$random_bytes;
$to_encrypt = $sha_digest . $p_q_inner_data . $random_bytes;
$encrypted_data = $key->encrypt($to_encrypt);
\danog\MadelineProto\Logger::log(['Starting Diffie Hellman key exchange'], \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Starting Diffie Hellman key exchange, Server authentication
* @method req_DH_params
* @param [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $p : The value of BigInteger
* string $q : The value of BigInteger
* long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector
* string $encrypted_data
* ]
* @return Server_DH_Params [
* int128 $nonce : The value of nonce is selected randomly by the server
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $new_nonce_hash : Return this value if server responds with server_DH_params_fail
* string $encrypted_answer : Return this value if server responds with server_DH_params_ok
* ]
*/
* ***********************************************************************
* Starting Diffie Hellman key exchange, Server authentication
* @method req_DH_params
* @param [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $p : The value of BigInteger
* string $q : The value of BigInteger
* long $public_key_fingerprint : This is our key in the server_public_key_fingerprints vector
* string $encrypted_data
* ]
* @return Server_DH_Params [
* int128 $nonce : The value of nonce is selected randomly by the server
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $new_nonce_hash : Return this value if server responds with server_DH_params_fail
* string $encrypted_answer : Return this value if server responds with server_DH_params_ok
* ]
*/
//
$server_dh_params = $this->method_call('req_DH_params',
[
'nonce' => $nonce,
'server_nonce' => $server_nonce,
'p' => $p_bytes,
'q' => $q_bytes,
'public_key_fingerprint' => $key->fp,
'encrypted_data' => $encrypted_data,
],
['datacenter' => $datacenter]
);
$server_dh_params = $this->method_call('req_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'p' => $p_bytes, 'q' => $q_bytes, 'public_key_fingerprint' => $key->fp, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
if ($nonce != $server_dh_params['nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong nonce.');
}
/*
* ***********************************************************************
* Check if server_nonce and new server_nonce are the same
*/
* ***********************************************************************
* Check if server_nonce and new server_nonce are the same
*/
if ($server_nonce != $server_dh_params['server_nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong server nonce.');
}
/*
* ***********************************************************************
* Check valid new nonce hash if return from server
* new nonce hash return in server_DH_params_fail
*/
* ***********************************************************************
* Check valid new nonce hash if return from server
* new nonce hash return in server_DH_params_fail
*/
if (isset($server_dh_params['new_nonce_hash']) && substr(sha1($new_nonce), -32) != $server_dh_params['new_nonce_hash']) {
throw new \danog\MadelineProto\SecurityException('wrong new nonce hash.');
}
/*
* ***********************************************************************
* Get key, iv and decrypt answer
*/
* ***********************************************************************
* Get key, iv and decrypt answer
*/
$encrypted_answer = $server_dh_params['encrypted_answer'];
$tmp_aes_key = sha1($new_nonce.$server_nonce, true).substr(sha1($server_nonce.$new_nonce, true), 0, 12);
$tmp_aes_iv = substr(sha1($server_nonce.$new_nonce, true), 12, 8).sha1($new_nonce.$new_nonce, true).substr($new_nonce, 0, 4);
$tmp_aes_key = sha1($new_nonce . $server_nonce, true) . substr(sha1($server_nonce . $new_nonce, true), 0, 12);
$tmp_aes_iv = substr(sha1($server_nonce . $new_nonce, true), 12, 8) . sha1($new_nonce . $new_nonce, true) . substr($new_nonce, 0, 4);
$answer_with_hash = $this->ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
/*
* ***********************************************************************
* Separate answer and hash
*/
* ***********************************************************************
* Separate answer and hash
*/
$answer_hash = substr($answer_with_hash, 0, 20);
$answer = substr($answer_with_hash, 20);
/*
* ***********************************************************************
* Deserialize answer
* @return Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* int $g
* string $dh_prime
* string $g_a
* int $server_time
* ]
*/
* ***********************************************************************
* Deserialize answer
* @return Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* int $g
* string $dh_prime
* string $g_a
* int $server_time
* ]
*/
$server_DH_inner_data = $this->deserialize($answer, ['type' => '']);
/*
* ***********************************************************************
* Do some checks
*/
* ***********************************************************************
* Do some checks
*/
$server_DH_inner_data_length = $this->get_length($answer);
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
throw new \danog\MadelineProto\SecurityException('answer_hash mismatch.');
}
if ($nonce != $server_DH_inner_data['nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong nonce');
}
if ($server_nonce != $server_DH_inner_data['server_nonce']) {
throw new \danog\MadelineProto\SecurityException('wrong server nonce');
}
$g = new \phpseclib\Math\BigInteger($server_DH_inner_data['g']);
$g_a = new \phpseclib\Math\BigInteger($server_DH_inner_data['g_a'], 256);
$dh_prime = new \phpseclib\Math\BigInteger($server_DH_inner_data['dh_prime'], 256);
/*
* ***********************************************************************
* Time delta
*/
* ***********************************************************************
* Time delta
*/
$server_time = $server_DH_inner_data['server_time'];
$this->datacenter->sockets[$datacenter]->time_delta = $server_time - time();
\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);
for ($retry_id = 0; $retry_id <= $this->settings['max_tries']['authorization']; $retry_id++) {
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
$b = new \phpseclib\Math\BigInteger($this->random(256), 256);
\danog\MadelineProto\Logger::log(['Generating g_b...'], \danog\MadelineProto\Logger::VERBOSE);
$g_b = $g->powMod($b, $dh_prime);
$this->check_G($g_b, $dh_prime);
/*
* ***********************************************************************
* Check validity of g_b
* 1 < g_b < dh_prime - 1
*/
* ***********************************************************************
* Check validity of g_b
* 1 < g_b < dh_prime - 1
*/
\danog\MadelineProto\Logger::log(['Executing g_b check...'], \danog\MadelineProto\Logger::VERBOSE);
if ($g_b->compare($this->one) <= 0 // 1 < g_b or g_b > 1 or ! g_b <= 1
|| $g_b->compare($dh_prime->subtract($this->one)) >= 0 // g_b < dh_prime - 1 or ! g_b >= dh_prime - 1
) {
if ($g_b->compare($this->one) <= 0 || $g_b->compare($dh_prime->subtract($this->one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_b is invalid (1 < g_b < dh_prime - 1 is false).');
}
\danog\MadelineProto\Logger::log(['Preparing client_DH_inner_data...'], \danog\MadelineProto\Logger::VERBOSE);
$g_b_str = $g_b->toBytes();
/*
* ***********************************************************************
* serialize client_DH_inner_data
* @method client_DH_inner_data
* @param Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* long $retry_id : First attempt
* string $g_b : g^b mod dh_prime
* ]
*/
$data = $this->serialize_object(
['type' => 'client_DH_inner_data'],
[
'nonce' => $nonce,
'server_nonce' => $server_nonce,
'retry_id' => $retry_id,
'g_b' => $g_b_str,
], 'client_DH_inner_data'
);
* ***********************************************************************
* serialize client_DH_inner_data
* @method client_DH_inner_data
* @param Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* long $retry_id : First attempt
* string $g_b : g^b mod dh_prime
* ]
*/
$data = $this->serialize_object(['type' => 'client_DH_inner_data'], ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'retry_id' => $retry_id, 'g_b' => $g_b_str], 'client_DH_inner_data');
/*
* ***********************************************************************
* encrypt client_DH_inner_data
*/
$data_with_sha = sha1($data, true).$data;
$data_with_sha_padded = $data_with_sha.$this->random($this->posmod(-strlen($data_with_sha), 16));
* ***********************************************************************
* encrypt client_DH_inner_data
*/
$data_with_sha = sha1($data, true) . $data;
$data_with_sha_padded = $data_with_sha . $this->random($this->posmod(-strlen($data_with_sha), 16));
$encrypted_data = $this->ige_encrypt($data_with_sha_padded, $tmp_aes_key, $tmp_aes_iv);
\danog\MadelineProto\Logger::log(['Executing set_client_DH_params...'], \danog\MadelineProto\Logger::VERBOSE);
/*
* ***********************************************************************
* Send set_client_DH_params query
* @method set_client_DH_params
* @param Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $encrypted_data
* ]
* @return Set_client_DH_params_answer [
* string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* int128 $new_nonce_hash1 : Return this value if server responds with dh_gen_ok
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_retry
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_fail
* ]
*/
$Set_client_DH_params_answer = $this->method_call(
'set_client_DH_params',
[
'nonce' => $nonce,
'server_nonce' => $server_nonce,
'encrypted_data' => $encrypted_data,
],
['datacenter' => $datacenter]
);
* ***********************************************************************
* Send set_client_DH_params query
* @method set_client_DH_params
* @param Server_DH_inner_data [
* int128 $nonce : The value of nonce is selected randomly by the client (random number) and identifies the client within this communication
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* string $encrypted_data
* ]
* @return Set_client_DH_params_answer [
* string $_ : This value is dh_gen_ok, dh_gen_retry OR dh_gen_fail
* int128 $server_nonce : The value of server_nonce is selected randomly by the server
* int128 $new_nonce_hash1 : Return this value if server responds with dh_gen_ok
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_retry
* int128 $new_nonce_hash2 : Return this value if server responds with dh_gen_fail
* ]
*/
$Set_client_DH_params_answer = $this->method_call('set_client_DH_params', ['nonce' => $nonce, 'server_nonce' => $server_nonce, 'encrypted_data' => $encrypted_data], ['datacenter' => $datacenter]);
/*
* ***********************************************************************
* Generate auth_key
*/
* ***********************************************************************
* Generate auth_key
*/
\danog\MadelineProto\Logger::log(['Generating authorization key...'], \danog\MadelineProto\Logger::VERBOSE);
$auth_key = $g_a->powMod($b, $dh_prime);
$auth_key_str = $auth_key->toBytes();
$auth_key_sha = sha1($auth_key_str, true);
$auth_key_aux_hash = substr($auth_key_sha, 0, 8);
$new_nonce_hash1 = substr(sha1($new_nonce.chr(1).$auth_key_aux_hash, true), -16);
$new_nonce_hash2 = substr(sha1($new_nonce.chr(2).$auth_key_aux_hash, true), -16);
$new_nonce_hash3 = substr(sha1($new_nonce.chr(3).$auth_key_aux_hash, true), -16);
$new_nonce_hash1 = substr(sha1($new_nonce . chr(1) . $auth_key_aux_hash, true), -16);
$new_nonce_hash2 = substr(sha1($new_nonce . chr(2) . $auth_key_aux_hash, true), -16);
$new_nonce_hash3 = substr(sha1($new_nonce . chr(3) . $auth_key_aux_hash, true), -16);
/*
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
* ***********************************************************************
* Check if the client's nonce and the server's nonce are the same
*/
if ($Set_client_DH_params_answer['nonce'] != $nonce) {
throw new \danog\MadelineProto\SecurityException('wrong nonce.');
}
/*
* ***********************************************************************
* Check if server_nonce and new server_nonce are the same
*/
* ***********************************************************************
* Check if server_nonce and new server_nonce are the same
*/
if ($Set_client_DH_params_answer['server_nonce'] != $server_nonce) {
throw new \danog\MadelineProto\SecurityException('wrong server nonce');
}
/*
* ***********************************************************************
* Check Set_client_DH_params_answer type
*/
* ***********************************************************************
* Check Set_client_DH_params_answer type
*/
switch ($Set_client_DH_params_answer['_']) {
case 'dh_gen_ok':
if ($Set_client_DH_params_answer['new_nonce_hash1'] != $new_nonce_hash1) {
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash1');
}
\danog\MadelineProto\Logger::log(['Diffie Hellman key exchange processed successfully!'], \danog\MadelineProto\Logger::VERBOSE);
$res_authorization['server_salt'] = substr($new_nonce, 0, 8 - 0) ^ substr($server_nonce, 0, 8 - 0);
$res_authorization['auth_key'] = $auth_key_str;
$res_authorization['id'] = substr($auth_key_sha, -8);
$res_authorization['connection_inited'] = false;
\danog\MadelineProto\Logger::log(['Auth key generated'], \danog\MadelineProto\Logger::NOTICE);
return $res_authorization;
case 'dh_gen_retry':
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash_2');
}
//repeat foreach
\danog\MadelineProto\Logger::log(['Retrying Auth'], \danog\MadelineProto\Logger::VERBOSE);
break;
@ -388,7 +302,6 @@ trait AuthKeyHandler
if ($Set_client_DH_params_answer['new_nonce_hash3'] != $new_nonce_hash3) {
throw new \danog\MadelineProto\SecurityException('wrong new_nonce_hash_3');
}
\danog\MadelineProto\Logger::log(['Auth Failed'], \danog\MadelineProto\Logger::WARNING);
break 2;
default:
@ -397,109 +310,88 @@ trait AuthKeyHandler
}
}
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine() . '. Retrying...'], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine().'. Retrying...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine() . '. Retrying...'], \danog\MadelineProto\Logger::WARNING);
$req_pq = $req_pq === 'req_pq_multi' ? 'req_pq' : 'req_pq_multi';
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'RPC_CALL_FAIL') {
throw $e;
}
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
\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->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = [];
}
}
if (strpos($datacenter, 'cdn') === false) {
throw new \danog\MadelineProto\SecurityException('Auth Failed');
}
}
public function check_G($g_a, $p)
{
/*
* ***********************************************************************
* Check validity of g_a
* 1 < g_a < p - 1
*/
\danog\MadelineProto\Logger::log(['Executing g_a check (1/2)...'], \danog\MadelineProto\Logger::VERBOSE);
if ($g_a->compare($this->one) <= 0 // 1 < g_a or g_a > 1 or ! g_a <= 1
|| $g_a->compare($p->subtract($this->one)) >= 0 // g_a < dh_prime - 1 or ! g_a >= dh_prime - 1
) {
if ($g_a->compare($this->one) <= 0 || $g_a->compare($p->subtract($this->one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_a is invalid (1 < g_a < dh_prime - 1 is false).');
}
\danog\MadelineProto\Logger::log(['Executing g_a check (2/2)...'], \danog\MadelineProto\Logger::VERBOSE);
if ($g_a->compare($this->twoe1984) < 0 // gA < 2^{2048-64}
|| $g_a->compare($p->subtract($this->twoe1984)) >= 0 // gA > dhPrime - 2^{2048-64}
) {
if ($g_a->compare($this->twoe1984) < 0 || $g_a->compare($p->subtract($this->twoe1984)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < gA < dh_prime - 2^1984 is false).');
}
return true;
}
public function check_p_g($p, $g)
{
/*
* ***********************************************************************
* Check validity of dh_prime
* Is it a prime?
*/
* ***********************************************************************
* Check validity of dh_prime
* Is it a prime?
*/
\danog\MadelineProto\Logger::log(['Executing p/g checks (1/2)...'], \danog\MadelineProto\Logger::VERBOSE);
if (!$p->isPrime()) {
throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime (p isn't a prime).");
}
/*
* ***********************************************************************
* Check validity of p
* Is (p - 1) / 2 a prime?
*
* Almost always fails
*/
* ***********************************************************************
* Check validity of p
* Is (p - 1) / 2 a prime?
*
* Almost always fails
*/
/*
\danog\MadelineProto\Logger::log(['Executing p/g checks (2/3)...'], \danog\MadelineProto\Logger::VERBOSE);
if (!$p->subtract($this->one)->divide($this->two)[0]->isPrime()) {
throw new \danog\MadelineProto\SecurityException("p isn't a safe 2048-bit prime ((p - 1) / 2 isn't a prime).");
}
*/
/*
* ***********************************************************************
* Check validity of p
* 2^2047 < p < 2^2048
*/
* ***********************************************************************
* Check validity of p
* 2^2047 < p < 2^2048
*/
\danog\MadelineProto\Logger::log(['Executing p/g checks (2/2)...'], \danog\MadelineProto\Logger::VERBOSE);
if ($p->compare($this->twoe2047) <= 0 // 2^2047 < p or p > 2^2047 or ! p <= 2^2047
|| $p->compare($this->twoe2048) >= 0 // p < 2^2048 or ! p >= 2^2048
) {
if ($p->compare($this->twoe2047) <= 0 || $p->compare($this->twoe2048) >= 0) {
throw new \danog\MadelineProto\SecurityException("g isn't a safe 2048-bit prime (2^2047 < p < 2^2048 is false).");
}
/*
* ***********************************************************************
* Check validity of g
* 1 < g < p - 1
*/
* ***********************************************************************
* Check validity of g
* 1 < g < p - 1
*/
\danog\MadelineProto\Logger::log(['Executing g check...'], \danog\MadelineProto\Logger::VERBOSE);
if ($g->compare($this->one) <= 0 // 1 < g or g > 1 or ! g <= 1
|| $g->compare($p->subtract($this->one)) >= 0 // g < p - 1 or ! g >= p - 1
) {
if ($g->compare($this->one) <= 0 || $g->compare($p->subtract($this->one)) >= 0) {
throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).');
}
return true;
}
public function get_dh_config()
{
$this->updates_state['sync_loading'] = true;
try {
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
} finally {
@ -507,16 +399,13 @@ trait AuthKeyHandler
}
if ($dh_config['_'] === 'messages.dhConfigNotModified') {
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Logger::VERBOSE, ['DH configuration not modified']);
return $this->dh_config;
}
$dh_config['p'] = new \phpseclib\Math\BigInteger($dh_config['p'], 256);
$dh_config['g'] = new \phpseclib\Math\BigInteger($dh_config['g']);
$this->check_p_g($dh_config['p'], $dh_config['g']);
return $this->dh_config = $dh_config;
}
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++) {
@ -527,54 +416,39 @@ trait AuthKeyHandler
$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,
'temp_auth_key_id' => $temp_auth_key_id,
'perm_auth_key_id' => $perm_auth_key_id,
'temp_session_id' => $temp_session_id,
'expires_at' => $expires_at,
], 'bind_temp_auth_key_inner'
);
$message_data = $this->serialize_object(['type' => 'bind_auth_key_inner'], ['nonce' => $nonce, 'temp_auth_key_id' => $temp_auth_key_id, 'perm_auth_key_id' => $perm_auth_key_id, 'temp_session_id' => $temp_session_id, 'expires_at' => $expires_at], 'bind_temp_auth_key_inner');
$message_id = $this->generate_message_id($datacenter);
$seq_no = 0;
$encrypted_data = $this->random(16).$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$encrypted_data = $this->random(16) . $message_id . pack('VV', $seq_no, strlen($message_data)) . $message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
//$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->auth_key['auth_key'], 88, 32).$encrypted_data.$padding, true), 8, 16);
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);
$encrypted_message = $this->datacenter->sockets[$datacenter]->auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$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, DC '.$datacenter], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Successfully binded temporary and permanent authorization keys, DC ' . $datacenter], \danog\MadelineProto\Logger::NOTICE);
return true;
}
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...'], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['An exception occurred while generating the authorization key: ' . $e->getMessage() . ' Retrying (try number ' . $retry_id_total . ')...'], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\RPCErrorException $e) {
\danog\MadelineProto\Logger::log(['An RPCErrorException occurred while generating the authorization key: '.$e->getMessage().' Retrying (try number '.$retry_id_total.')...'], \danog\MadelineProto\Logger::WARNING);
\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->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.');
}
// Creates authorization keys
public function init_authorization()
{
$this->initing_authorization = true;
$this->updates_state['sync_loading'] = true;
$this->postpone_updates = true;
try {
foreach ($this->datacenter->sockets as $id => $socket) {
$cdn = strpos($id, 'cdn');
@ -626,7 +500,6 @@ trait AuthKeyHandler
$this->updates_state['sync_loading'] = false;
}
}
public function sync_authorization($id)
{
if (!isset($this->datacenter->sockets[$id])) {
@ -640,21 +513,22 @@ trait AuthKeyHandler
}
if ($authorized_socket->temp_auth_key !== null && $authorized_socket->auth_key !== null && $authorized_socket->authorized === true && $this->authorized === self::LOGGED_IN && $socket->authorized === false && strpos($authorized_dc_id, 'cdn') === false) {
try {
\danog\MadelineProto\Logger::log(['Trying to copy authorization from dc '.$authorized_dc_id.' to dc '.$id]);
\danog\MadelineProto\Logger::log(['Trying to copy authorization from dc ' . $authorized_dc_id . ' to dc ' . $id]);
$exported_authorization = $this->method_call('auth.exportAuthorization', ['dc_id' => preg_replace('|_.*|', '', $id)], ['datacenter' => $authorized_dc_id]);
$authorization = $this->method_call('auth.importAuthorization', $exported_authorization, ['datacenter' => $id]);
$socket->authorized = true;
break;
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage()], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC ' . $authorized_dc_id . ' to DC ' . $id . ': ' . $e->getMessage()], \danog\MadelineProto\Logger::ERROR);
} catch (\danog\MadelineProto\RPCErrorException $e) {
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC '.$authorized_dc_id.' to DC '.$id.': '.$e->getMessage()], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Failure while syncing authorization from DC ' . $authorized_dc_id . ' to DC ' . $id . ': ' . $e->getMessage()], \danog\MadelineProto\Logger::ERROR);
if ($e->rpc === 'DC_ID_INVALID') {
break;
}
} // Turns out this DC isn't authorized after all
}
// Turns out this DC isn't authorized after all
}
}
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -73,45 +73,24 @@ trait CallHandler
}
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] === false)) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['write_client_info'], $method)], \danog\MadelineProto\Logger::NOTICE);
$serialized = $this->serialize_method(
'invokeWithLayer',
[
'layer' => $this->settings['tl_schema']['layer'],
'query' => $this->serialize_method('initConnection',
[
'api_id' => $this->settings['app_info']['api_id'],
'api_hash' => $this->settings['app_info']['api_hash'],
'device_model' => strpos($aargs['datacenter'], 'cdn') === false ? $this->settings['app_info']['device_model'] : 'n/a',
'system_version' => strpos($aargs['datacenter'], 'cdn') === false ? $this->settings['app_info']['system_version'] : 'n/a',
'app_version' => $this->settings['app_info']['app_version'],
'system_lang_code' => $this->settings['app_info']['lang_code'],
'lang_code' => $this->settings['app_info']['lang_code'],
'lang_pack' => '',
'query' => $serialized,
]
),
]);
$serialized = $this->serialize_method('invokeWithLayer', ['layer' => $this->settings['tl_schema']['layer'], 'query' => $this->serialize_method('initConnection', ['api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash'], 'device_model' => strpos($aargs['datacenter'], 'cdn') === false ? $this->settings['app_info']['device_model'] : 'n/a', 'system_version' => strpos($aargs['datacenter'], 'cdn') === false ? $this->settings['app_info']['system_version'] : 'n/a', 'app_version' => $this->settings['app_info']['app_version'], 'system_lang_code' => $this->settings['app_info']['lang_code'], 'lang_code' => $this->settings['app_info']['lang_code'], 'lang_pack' => '', 'query' => $serialized])]);
}
$content_related = $this->content_related($method);
$type = $this->methods->find_by_method($method)['type'];
if (isset($queue)) {
$serialized = $this->serialize_method('invokeAfterMsgs', ['msg_ids' => $this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue], 'query' => $serialized]);
}
if ($this->settings['requests']['gzip_encode_if_gt'] !== -1 && ($l = strlen($serialized)) > $this->settings['requests']['gzip_encode_if_gt'] && ($g = strlen($gzipped = gzencode($serialized))) < $l) {
$serialized = $this->serialize_object(['type' => 'gzip_packed'], ['packed_data' => $gzipped], 'gzipped data');
\danog\MadelineProto\Logger::log(['Using GZIP compression for '.$method.', saved '.($l - $g).' bytes of data, reduced call size by '.($g * 100 / $l).'%'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Using GZIP compression for ' . $method . ', saved ' . ($l - $g) . ' bytes of data, reduced call size by ' . $g * 100 / $l . '%'], \danog\MadelineProto\Logger::VERBOSE);
}
$last_recv = $this->last_recv;
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
if ($canunset = !$this->updates_state['sync_loading']) {
$this->updates_state['sync_loading'] = true;
}
try {
\danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Calling method (try number ' . $count . ' for ' . $method . ')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
$this->datacenter->sockets[$aargs['datacenter']]->object_queue[] = ['body' => $serialized, 'content_related' => $content_related, 'msg_id' => $message_id = isset($aargs['message_id']) ? $aargs['message_id'] : $this->generate_message_id($aargs['datacenter'])];
if (count($this->datacenter->sockets[$aargs['datacenter']]->ack_queue)) {
@ -123,8 +102,8 @@ trait CallHandler
$this->send_messages($aargs['datacenter']);
} else {
$this->send_unencrypted_message($serialized, $message_id = isset($aargs['message_id']) ? $aargs['message_id'] : $this->generate_message_id($aargs['datacenter']), $aargs['datacenter']);
$aargs['message_id'] = $message_id;
}
if ($this->is_http($aargs['datacenter']) && isset($aargs['reopen'])) {
\danog\MadelineProto\Logger::log(['Closing and reopening connection...'], \danog\MadelineProto\Logger::WARNING);
$this->close_and_reopen($aargs['datacenter']);
@ -140,7 +119,7 @@ trait CallHandler
unset($this->datacenter->sockets[$aargs['datacenter']]->call_queue[$queue][$key]);
}
}
if ($method === 'http_wait' || (isset($aargs['noResponse']) && $aargs['noResponse'])) {
if ($method === 'http_wait' || isset($aargs['noResponse']) && $aargs['noResponse']) {
return true;
}
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['content'] = ['method' => $method, 'args' => $args];
@ -149,10 +128,12 @@ trait CallHandler
$server_answer = null;
$update_count = 0;
$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
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);
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping
\danog\MadelineProto\Logger::log(['Getting response (try number ' . $res_count . ' for ' . $method . ')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'])) {
// Checks if I have received the response to the called method, if not continue looping
if ($only_updates) {
if ($update_count > 50) {
$update_count = 0;
@ -172,26 +153,24 @@ trait CallHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
}
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$only_updates = $this->handle_messages($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);
\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);
\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 ($canunset) {
$this->updates_state['sync_loading'] = false;
$this->handle_pending_updates();
@ -214,16 +193,15 @@ trait CallHandler
continue 3;
case 16:
case 17:
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->time_delta = (int) ((new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$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']]->time_delta], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: ' . self::BAD_MSG_ERROR_CODES[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->time_delta = (int) (new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$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']]->time_delta], \danog\MadelineProto\Logger::WARNING);
$this->reset_session();
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
continue 3;
}
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: '.self::BAD_MSG_ERROR_CODES[$server_answer['error_code']], $server_answer['error_code']);
throw new \danog\MadelineProto\RPCErrorException('Received bad_msg_notification: ' . self::BAD_MSG_ERROR_CODES[$server_answer['error_code']], $server_answer['error_code']);
break;
case 'boolTrue':
case 'boolFalse':
@ -234,13 +212,16 @@ trait CallHandler
$server_answer = $this->MTProto_to_botAPI($server_answer, $args);
}
} catch (\danog\MadelineProto\Exception $e) {
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
$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);
$this->close_and_reopen($aargs['datacenter']);
if ($e->getMessage() === 'Re-executing query after server error...') {
return $this->method_call($method, $args, $aargs);
}
continue;
} catch (\RuntimeException $e) {
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
$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);
$this->close_and_reopen($aargs['datacenter']);
continue;
} finally {
@ -258,23 +239,19 @@ trait CallHandler
$this->handle_pending_updates();
}
}
if ($server_answer === null) {
if ($last_recv === $this->last_recv && $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization();
return $this->method_call($method, $args, $aargs);
}
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
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);
\danog\MadelineProto\Logger::log(['Got response for method ' . $method . ' @ try ' . $count . ' (response try ' . $res_count . ')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null && (!isset($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited']) || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] === false)) {
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['connection_inited'] = true;
}
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id] = [];
if (isset($message_chunks) && count($message_chunks)) {
$server_answer = [$server_answer];
@ -283,16 +260,13 @@ trait CallHandler
$server_answer[] = $this->method_call($method, $args, $aargs);
}
}
return $server_answer;
}
if ($method === 'req_pq') {
throw new \danog\MadelineProto\RPCErrorException('RPC_CALL_FAIL');
}
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
throw new \danog\MadelineProto\Exception('An error occurred while calling method ' . $method . ' (' . $last_error . ').');
}
public function object_call($object, $args = [], $aargs = ['message_id' => null, 'heavy' => false])
{
if (!is_array($args)) {

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
trait Crypt
@ -17,51 +17,42 @@ trait Crypt
public function aes_calculate($msg_key, $auth_key, $to_server = true)
{
$x = $to_server ? 0 : 8;
$sha256_a = hash('sha256', $msg_key.substr($auth_key, $x, 36), true);
$sha256_b = hash('sha256', substr($auth_key, 40 + $x, 36).$msg_key, true);
$aes_key = substr($sha256_a, 0, 8).substr($sha256_b, 8, 16).substr($sha256_a, 24, 8);
$aes_iv = substr($sha256_b, 0, 8).substr($sha256_a, 8, 16).substr($sha256_b, 24, 8);
$sha256_a = hash('sha256', $msg_key . substr($auth_key, $x, 36), true);
$sha256_b = hash('sha256', substr($auth_key, 40 + $x, 36) . $msg_key, true);
$aes_key = substr($sha256_a, 0, 8) . substr($sha256_b, 8, 16) . substr($sha256_a, 24, 8);
$aes_iv = substr($sha256_b, 0, 8) . substr($sha256_a, 8, 16) . substr($sha256_b, 24, 8);
return [$aes_key, $aes_iv];
}
public function old_aes_calculate($msg_key, $auth_key, $to_server = true)
{
$x = $to_server ? 0 : 8;
$sha1_a = sha1($msg_key.substr($auth_key, $x, 32), true);
$sha1_b = sha1(substr($auth_key, 32 + $x, 16).$msg_key.substr($auth_key, 48 + $x, 16), true);
$sha1_c = sha1(substr($auth_key, 64 + $x, 32).$msg_key, true);
$sha1_d = sha1($msg_key.substr($auth_key, 96 + $x, 32), true);
$aes_key = substr($sha1_a, 0, 8).substr($sha1_b, 8, 12).substr($sha1_c, 4, 12);
$aes_iv = substr($sha1_a, 8, 12).substr($sha1_b, 0, 8).substr($sha1_c, 16, 4).substr($sha1_d, 0, 8);
$sha1_a = sha1($msg_key . substr($auth_key, $x, 32), true);
$sha1_b = sha1(substr($auth_key, 32 + $x, 16) . $msg_key . substr($auth_key, 48 + $x, 16), true);
$sha1_c = sha1(substr($auth_key, 64 + $x, 32) . $msg_key, true);
$sha1_d = sha1($msg_key . substr($auth_key, 96 + $x, 32), true);
$aes_key = substr($sha1_a, 0, 8) . substr($sha1_b, 8, 12) . substr($sha1_c, 4, 12);
$aes_iv = substr($sha1_a, 8, 12) . substr($sha1_b, 0, 8) . substr($sha1_c, 16, 4) . substr($sha1_d, 0, 8);
return [$aes_key, $aes_iv];
}
public function ige_encrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->encrypt($message);
}
public function ctr_encrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ctr');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->encrypt($message);
}
public function ige_decrypt($message, $key, $iv)
{
$cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key);
$cipher->setIV($iv);
return @$cipher->decrypt($message);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -32,21 +32,21 @@ trait Files
}
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Upload status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Upload status: ' . $percent . '%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$part_size = 512 * 1024;
$part_total_num = (int) ceil($file_size / $part_size);
$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' : '');
$constructor = 'input' . ($encrypted === true ? 'Encrypted' : '') . ($file_size > 10 * 1024 * 1024 ? 'FileBig' : 'File') . ($encrypted === true ? 'Uploaded' : '');
$file_id = $this->random(8);
$f = fopen($file, 'r');
fseek($f, 0);
if ($encrypted === true) {
$key = $this->random(32);
$iv = $this->random(32);
$digest = hash('md5', $key.$iv, true);
$digest = hash('md5', $key . $iv, true);
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
$ige = new \phpseclib\Crypt\AES('ige');
$ige->setIV($iv);
@ -54,7 +54,6 @@ trait Files
$ige->enableContinuousBuffer();
}
$ctx = hash_init('md5');
while (ftell($f) !== $file_size) {
$bytes = stream_get_contents($f, $part_size);
if ($encrypted === true) {
@ -62,12 +61,11 @@ trait Files
}
hash_update($ctx, $bytes);
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => &$datacenter])) {
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part ' . $part_num);
}
$cb(ftell($f) * 100 / $file_size);
}
fclose($f);
$constructor = ['_' => $constructor, 'id' => $file_id, 'parts' => $part_total_num, 'name' => $file_name, 'md5_checksum' => hash_final($ctx)];
if ($encrypted === true) {
$constructor['key_fingerprint'] = $fingerprint;
@ -75,15 +73,12 @@ trait Files
$constructor['iv'] = $iv;
$constructor['md5_checksum'] = '';
}
return $constructor;
}
public function upload_encrypted($file, $file_name = '', $cb = null)
{
return $this->upload($file, $file_name, $cb, true);
}
public function get_download_info($message_media)
{
if (is_string($message_media)) {
@ -95,142 +90,132 @@ trait Files
$res = [];
switch ($message_media['_']) {
case 'encryptedMessage':
if ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaExternalDocument') {
return $this->get_download_info($message_media['decrypted_message']['media']);
}
$res['InputFileLocation'] = ['_' => 'inputEncryptedFileLocation', 'id' => $message_media['file']['id'], 'access_hash' => $message_media['file']['access_hash'], 'dc_id' => $message_media['file']['dc_id']];
$res['size'] = $message_media['decrypted_message']['media']['size'];
$res['key_fingerprint'] = $message_media['file']['key_fingerprint'];
$res['key'] = $message_media['decrypted_message']['media']['key'];
$res['iv'] = $message_media['decrypted_message']['media']['iv'];
if (isset($message_media['decrypted_message']['media']['file_name'])) {
$pathinfo = pathinfo($message_media['decrypted_message']['media']['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.'.$pathinfo['extension'];
if ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaExternalDocument') {
return $this->get_download_info($message_media['decrypted_message']['media']);
}
$res['name'] = $pathinfo['filename'];
}
if (isset($message_media['decrypted_message']['media']['mime_type'])) {
$res['mime'] = $message_media['decrypted_message']['media']['mime_type'];
} elseif ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaPhoto') {
$res['mime'] = 'image/jpeg';
}
if (isset($message_media['decrypted_message']['media']['attributes'])) {
foreach ($message_media['decrypted_message']['media']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
$res['InputFileLocation'] = ['_' => 'inputEncryptedFileLocation', 'id' => $message_media['file']['id'], 'access_hash' => $message_media['file']['access_hash'], 'dc_id' => $message_media['file']['dc_id']];
$res['size'] = $message_media['decrypted_message']['media']['size'];
$res['key_fingerprint'] = $message_media['file']['key_fingerprint'];
$res['key'] = $message_media['decrypted_message']['media']['key'];
$res['iv'] = $message_media['decrypted_message']['media']['iv'];
if (isset($message_media['decrypted_message']['media']['file_name'])) {
$pathinfo = pathinfo($message_media['decrypted_message']['media']['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.'.$pathinfo['extension'];
$res['ext'] = '.' . $pathinfo['extension'];
}
$res['name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
break;
}
if (isset($message_media['decrypted_message']['media']['mime_type'])) {
$res['mime'] = $message_media['decrypted_message']['media']['mime_type'];
} elseif ($message_media['decrypted_message']['media']['_'] === 'decryptedMessageMediaPhoto') {
$res['mime'] = 'image/jpeg';
}
}
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - '.$audio['performer'];
if (isset($message_media['decrypted_message']['media']['attributes'])) {
foreach ($message_media['decrypted_message']['media']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension'];
}
$res['name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
break;
}
}
}
}
if (!isset($res['ext'])) {
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($res['mime']));
}
if (!isset($res['name'])) {
$res['name'] = $message_media['file']['access_hash'];
}
return $res;
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - ' . $audio['performer'];
}
}
if (!isset($res['ext'])) {
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($res['mime']));
}
if (!isset($res['name'])) {
$res['name'] = $message_media['file']['access_hash'];
}
return $res;
case 'photo':
case 'messageMediaPhoto':
if ($message_media['_'] == 'photo') {
$photo = end($message_media['sizes']);
} else {
$photo = end($message_media['photo']['sizes']);
}
$res['name'] = $photo['location']['volume_id'].'_'.$photo['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
if (isset($photo['location']['size'])) {
$res['size'] = $photo['location']['size'];
}
if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']);
}
return $res;
if ($message_media['_'] == 'photo') {
$photo = end($message_media['sizes']);
} else {
$photo = end($message_media['photo']['sizes']);
}
$res['name'] = $photo['location']['volume_id'] . '_' . $photo['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
if (isset($photo['location']['size'])) {
$res['size'] = $photo['location']['size'];
}
if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']);
}
return $res;
case 'photoSize':
case 'photoCachedSize':
$res['name'] = $message_media['location']['volume_id'].'_'.$message_media['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['location']['volume_id'], 'local_id' => $message_media['location']['local_id'], 'secret' => $message_media['location']['secret'], 'dc_id' => $message_media['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
if (isset($photo['location']['size'])) {
$res['size'] = $photo['location']['size'];
}
if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']);
}
return $res;
$res['name'] = $message_media['location']['volume_id'] . '_' . $message_media['location']['local_id'];
$res['InputFileLocation'] = ['_' => 'inputFileLocation', 'volume_id' => $message_media['location']['volume_id'], 'local_id' => $message_media['location']['local_id'], 'secret' => $message_media['location']['secret'], 'dc_id' => $message_media['location']['dc_id']];
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg';
if (isset($photo['location']['size'])) {
$res['size'] = $photo['location']['size'];
}
if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']);
}
return $res;
case 'decryptedMessageMediaExternalDocument':
case 'document':
$message_media = ['document' => $message_media];
$message_media = ['document' => $message_media];
case 'messageMediaDocument':
foreach ($message_media['document']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.'.$pathinfo['extension'];
foreach ($message_media['document']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension'];
}
$res['name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
break;
}
$res['name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
break;
}
}
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - '.$audio['performer'];
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['name'] .= ' - ' . $audio['performer'];
}
}
}
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id']];
if (!isset($res['ext'])) {
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
}
if (!isset($res['name'])) {
$res['name'] = $message_media['document']['access_hash'];
}
if (isset($message_media['document']['size'])) {
$res['size'] = $message_media['document']['size'];
}
$res['name'] .= '_'.$message_media['document']['id'];
$res['mime'] = $message_media['document']['mime_type'];
return $res;
$res['InputFileLocation'] = ['_' => 'inputDocumentFileLocation', 'id' => $message_media['document']['id'], 'access_hash' => $message_media['document']['access_hash'], 'version' => isset($message_media['document']['version']) ? $message_media['document']['version'] : 0, 'dc_id' => $message_media['document']['dc_id']];
if (!isset($res['ext'])) {
$res['ext'] = $this->get_extension_from_location($res['InputFileLocation'], $this->get_extension_from_mime($message_media['document']['mime_type']));
}
if (!isset($res['name'])) {
$res['name'] = $message_media['document']['access_hash'];
}
if (isset($message_media['document']['size'])) {
$res['size'] = $message_media['document']['size'];
}
$res['name'] .= '_' . $message_media['document']['id'];
$res['mime'] = $message_media['document']['mime_type'];
return $res;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor provided: '.$message_media['_']);
throw new \danog\MadelineProto\Exception('Invalid constructor provided: ' . $message_media['_']);
}
}
public function download_to_dir($message_media, $dir, $cb = null)
{
$message_media = $this->get_download_info($message_media);
return $this->download_to_file($message_media, $dir.'/'.$message_media['name'].$message_media['ext'], $cb);
return $this->download_to_file($message_media, $dir . '/' . $message_media['name'] . $message_media['ext'], $cb);
}
public function download_to_file($message_media, $file, $cb = null)
{
$file = preg_replace('|/+|', '/', $file);
@ -242,7 +227,6 @@ trait Files
$stream = fopen($file, 'r+b');
\danog\MadelineProto\Logger::log(['Waiting for lock of file to download...']);
flock($stream, LOCK_EX);
try {
$this->download_to_stream($message_media, $stream, $cb, filesize($file), -1);
} finally {
@ -250,15 +234,13 @@ trait Files
fclose($stream);
clearstatcache();
}
return $file;
}
public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1)
{
if ($cb === null) {
$cb = function ($percent) {
\danog\MadelineProto\Logger::log(['Download status: '.$percent.'%'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Download status: ' . $percent . '%'], \danog\MadelineProto\Logger::NOTICE);
};
}
$message_media = $this->get_download_info($message_media);
@ -274,7 +256,7 @@ trait Files
$percent = 0;
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
if (isset($message_media['key'])) {
$digest = hash('md5', $message_media['key'].$message_media['iv'], true);
$digest = hash('md5', $message_media['key'] . $message_media['iv'], true);
$fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
if ($fingerprint !== $message_media['key_fingerprint']) {
throw new \danog\MadelineProto\Exception('Fingerprint mismatch!');
@ -290,17 +272,15 @@ trait Files
if ($start_at = $offset % $part_size) {
$offset -= $start_at;
}
try {
$res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => &$datacenter]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
switch ($e->rpc) {
case 'FILE_TOKEN_INVALID':
$cdn = false;
continue 2;
$cdn = false;
continue 2;
default:
throw $e;
throw $e;
}
}
if ($res['_'] === 'upload.fileCdnRedirect') {
@ -309,7 +289,7 @@ trait Files
$message_media['cdn_key'] = $res['encryption_key'];
$message_media['cdn_iv'] = $res['encryption_iv'];
$old_dc = $datacenter;
$datacenter = $res['dc_id'].'_cdn';
$datacenter = $res['dc_id'] . '_cdn';
if (!isset($this->datacenter->sockets[$datacenter])) {
$this->config['expires'] = -1;
$this->get_config([], ['datacenter' => $this->datacenter->curdc]);
@ -320,18 +300,16 @@ trait Files
if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['cdn_reupload']], \danog\MadelineProto\Logger::NOTICE);
$this->get_config([], ['datacenter' => $this->datacenter->curdc]);
try {
$this->add_cdn_hashes($message_media['file_token'], $this->method_call('upload.reuploadCdnFile', ['file_token' => $message_media['file_token'], 'request_token' => $res['request_token']], ['heavy' => true, 'datacenter' => $old_dc]));
} catch (\danog\MadelineProto\RPCErrorException $e) {
switch ($e->rpc) {
case 'FILE_TOKEN_INVALID':
case 'REQUEST_TOKEN_INVALID':
$cdn = false;
continue 2;
$cdn = false;
continue 2;
default:
throw $e;
throw $e;
}
}
continue;
@ -347,7 +325,7 @@ trait Files
}
}
if (isset($message_media['cdn_key'])) {
$ivec = substr($message_media['cdn_iv'], 0, 12).pack('N', $offset >> 4);
$ivec = substr($message_media['cdn_iv'], 0, 12) . pack('N', $offset >> 4);
$res['bytes'] = $this->ctr_encrypt($res['bytes'], $message_media['cdn_key'], $ivec);
$this->check_cdn_hash($message_media['file_token'], $offset, $res['bytes'], $old_dc);
}
@ -367,7 +345,6 @@ trait Files
$offset += strlen($res['bytes']);
$downloaded_size += strlen($res['bytes']);
\danog\MadelineProto\Logger::log([fwrite($stream, $res['bytes'])], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if ($theend) {
break;
}
@ -381,12 +358,9 @@ trait Files
if ($cdn) {
$this->clear_cdn_hashes($message_media['file_token']);
}
return true;
}
private $cdn_hashes = [];
private function add_cdn_hashes($file, $hashes)
{
if (!isset($this->cdn_hashes[$file])) {
@ -396,7 +370,6 @@ trait Files
$this->cdn_hashes[$file][$hash['offset']] = ['limit' => $hash['limit'], 'hash' => (string) $hash['hash']];
}
}
private function check_cdn_hash($file, $offset, $data, &$datacenter)
{
while (strlen($data)) {
@ -404,23 +377,19 @@ trait Files
$this->add_cdn_hashes($file, $this->method_call('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset], ['datacenter' => $datacenter]));
}
if (!isset($this->cdn_hashes[$file][$offset])) {
throw new \danog\MadelineProto\Exception('Could not fetch CDN hashes for offset '.$offset);
throw new \danog\MadelineProto\Exception('Could not fetch CDN hashes for offset ' . $offset);
}
if (hash('sha256', substr($data, 0, $this->cdn_hashes[$file][$offset]['limit']), true) !== $this->cdn_hashes[$file][$offset]['hash']) {
throw new \danog\MadelineProto\SecurityException('CDN hash mismatch for offset '.$offset);
throw new \danog\MadelineProto\SecurityException('CDN hash mismatch for offset ' . $offset);
}
$data = substr($data, $this->cdn_hashes[$file][$offset]['limit']);
$offset += $this->cdn_hashes[$file][$offset]['limit'];
}
return true;
}
private function clear_cdn_hashes($file)
{
unset($this->cdn_hashes[$file]);
return true;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -19,11 +19,10 @@ trait MessageHandler
{
public function send_unencrypted_message($message_data, $message_id, $datacenter)
{
$message_data = "\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int(strlen($message_data)).$message_data;
$message_data = "\0\0\0\0\0\0\0\0" . $message_id . $this->pack_unsigned_int(strlen($message_data)) . $message_data;
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['response' => -1];
$this->datacenter->sockets[$datacenter]->send_message($message_data);
}
public function send_messages($datacenter)
{
if (count($this->datacenter->sockets[$datacenter]->object_queue) > 1) {
@ -46,27 +45,23 @@ trait MessageHandler
} else {
return;
}
$plaintext = $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'].$this->datacenter->sockets[$datacenter]->session_id.$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$plaintext = $this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] . $this->datacenter->sockets[$datacenter]->session_id . $message_id . pack('VV', $seq_no, strlen($message_data)) . $message_data;
$padding = $this->posmod(-strlen($plaintext), 16);
if ($padding < 12) {
$padding += 16;
}
$padding = $this->random($padding);
$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 88, 32).$plaintext.$padding, true), 8, 16);
$message_key = substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 88, 32) . $plaintext . $padding, true), 8, 16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key']);
$message = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'].$message_key.$this->ige_encrypt($plaintext.$padding, $aes_key, $aes_iv);
$message = $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] . $message_key . $this->ige_encrypt($plaintext . $padding, $aes_key, $aes_iv);
$this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id] = ['seq_no' => $seq_no, 'response' => -1];
$this->datacenter->sockets[$datacenter]->send_message($message);
$this->datacenter->sockets[$datacenter]->object_queue = [];
foreach ($this->datacenter->sockets[$datacenter]->ack_queue as $msg_id) {
$this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id]['ack'] = true;
}
$this->datacenter->sockets[$datacenter]->ack_queue = [];
}
/**
* Reading connection and receiving message from server.
*/
@ -98,37 +93,28 @@ trait MessageHandler
if ($session_id != $this->datacenter->sockets[$datacenter]->session_id) {
throw new \danog\MadelineProto\Exception('Session id mismatch.');
}
$message_id = substr($decrypted_data, 16, 8);
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
$seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
// Dunno how to handle any incorrect sequence numbers
$message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
}
if ((strlen($decrypted_data) - 32) - $message_data_length < 12) {
if (strlen($decrypted_data) - 32 - $message_data_length < 12) {
throw new \danog\MadelineProto\SecurityException('padding is too small');
}
if ((strlen($decrypted_data) - 32) - $message_data_length > 1024) {
if (strlen($decrypted_data) - 32 - $message_data_length > 1024) {
throw new \danog\MadelineProto\SecurityException('padding is too big');
}
if ($message_data_length < 0) {
throw new \danog\MadelineProto\SecurityException('message_data_length not positive');
}
if ($message_data_length % 4 != 0) {
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
}
$message_data = substr($decrypted_data, 32, $message_data_length);
if ($message_key != substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 96, 32).$decrypted_data, true), 8, 16)) {
if ($message_key != substr(hash('sha256', substr($this->datacenter->sockets[$datacenter]->temp_auth_key['auth_key'], 96, 32) . $decrypted_data, true), 8, 16)) {
throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
@ -141,7 +127,6 @@ trait MessageHandler
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1;
$this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id;
$this->last_recv = time();
return true;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -24,18 +24,18 @@ trait MsgIdHandler
}
$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);
\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);
}
$max_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta + 30))->bitwise_leftShift(32);
if ($max_message_id->compare($new_message_id) < 0) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is too new compared to the max value ('.$max_message_id.'). Consider syncing your date.');
throw new \danog\MadelineProto\Exception('Given message id (' . $new_message_id . ') is too new compared to the max value (' . $max_message_id . '). Consider syncing your date.');
}
if ($aargs['outgoing']) {
if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4. Consider syncing your date.');
throw new \danog\MadelineProto\Exception('Given message id (' . $new_message_id . ') is not divisible by 4. Consider syncing your date.');
}
if (!\danog\MadelineProto\Logger::$has_thread && $new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], false)) <= 0) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.', 1);
throw new \danog\MadelineProto\Exception('Given message id (' . $new_message_id . ') is lower than or equal to the current limit (' . $key . '). Consider syncing your date.', 1);
}
if (count($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
@ -51,14 +51,13 @@ trait MsgIdHandler
$key = $this->get_max_id($aargs['datacenter'], true);
if ($aargs['container']) {
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) >= 0) {
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is bigger than or equal to the current limit ('.$key.'). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['WARNING: Given message id (' . $new_message_id . ') is bigger than or equal to the current limit (' . $key . '). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
}
} else {
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) <= 0) {
\danog\MadelineProto\Logger::log(['WARNING: Given message id ('.$new_message_id.') is lower than or equal to the current limit ('.$key.'). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
\danog\MadelineProto\Logger::log(['WARNING: Given message id (' . $new_message_id . ') is lower than or equal to the current limit (' . $key . '). Consider syncing your date.'], \danog\MadelineProto\Logger::WARNING);
}
}
if (count($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
reset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
$key = key($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
@ -71,7 +70,6 @@ trait MsgIdHandler
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = [];
}
}
public function generate_message_id($datacenter)
{
$message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta))->bitwise_leftShift(32);
@ -79,17 +77,14 @@ trait MsgIdHandler
$message_id = $key->add($this->four);
}
$this->check_message_id($message_id, ['outgoing' => true, 'datacenter' => $datacenter, 'container' => false]);
return strrev($message_id->toBytes());
}
public function get_max_id($datacenter, $incoming)
{
$incoming = $incoming ? 'incoming' : 'outgoing';
if (isset($this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'}) && is_object($this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'})) {
return $this->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'};
if (isset($this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'}) && is_object($this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'})) {
return $this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'};
}
return $this->zero;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -30,7 +30,6 @@ trait PeerHandler
case 'user':
if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) {
$this->chats[$user['id']] = $user;
try {
$this->get_pwr_chat($user['id'], false, true);
} catch (\danog\MadelineProto\Exception $e) {
@ -42,12 +41,11 @@ trait PeerHandler
case 'userEmpty':
break;
default:
throw new \danog\MadelineProto\Exception('Invalid user provided at key '.$key.': '.var_export($user, true));
throw new \danog\MadelineProto\Exception('Invalid user provided at key ' . $key . ': ' . var_export($user, true));
break;
}
}
}
public function add_chats($chats)
{
foreach ($chats as $key => $chat) {
@ -57,7 +55,6 @@ trait PeerHandler
case 'chatForbidden':
if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) {
$this->chats[-$chat['id']] = $chat;
try {
$this->get_pwr_chat(-$chat['id'], $this->settings['peer']['full_fetch'], true);
} catch (\danog\MadelineProto\Exception $e) {
@ -66,7 +63,6 @@ trait PeerHandler
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
}
}
case 'channelEmpty':
break;
case 'channel':
@ -80,7 +76,6 @@ trait PeerHandler
}
if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) {
$this->chats[$bot_api_id] = $chat;
try {
if (!isset($this->full_chats[$bot_api_id]) || $this->full_chats[$bot_api_id]['full']['participants_count'] !== $this->get_full_info($bot_api_id)['full']['participants_count']) {
$this->get_pwr_chat($this->to_supergroup($chat['id']), $this->settings['peer']['full_fetch'], true);
@ -93,12 +88,11 @@ trait PeerHandler
}
break;
default:
throw new \danog\MadelineProto\Exception('Invalid chat provided at key '.$key.': '.var_export($chat, true));
throw new \danog\MadelineProto\Exception('Invalid chat provided at key ' . $key . ': ' . var_export($chat, true));
break;
}
}
}
public function peer_isset($id)
{
try {
@ -109,7 +103,6 @@ trait PeerHandler
return false;
}
}
public function entities_peer_isset($entities)
{
try {
@ -123,10 +116,8 @@ trait PeerHandler
} catch (\danog\MadelineProto\Exception $e) {
return false;
}
return true;
}
public function fwd_peer_isset($fwd)
{
try {
@ -139,10 +130,8 @@ trait PeerHandler
} catch (\danog\MadelineProto\Exception $e) {
return false;
}
return true;
}
public function get_info($id, $recursive = true)
{
if (is_array($id)) {
@ -162,47 +151,42 @@ trait PeerHandler
case 'peerUser':
$id = $id['user_id'];
break;
case 'chat':
case 'chatFull':
$id = -$id['id'];
break;
case 'inputPeerChat':
case 'inputPeerChat':
case 'peerChat':
$id = -$id['chat_id'];
break;
case 'channel':
case 'channelFull':
$id = $this->to_supergroup($id['id']);
break;
case 'inputPeerChannel':
case 'inputChannel':
case 'peerChannel':
$id = $this->to_supergroup($id['channel_id']);
break;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor given '.var_export($id, true));
throw new \danog\MadelineProto\Exception('Invalid constructor given ' . var_export($id, true));
break;
}
}
if (is_string($id) && strpos($id, '#') !== false) {
if (preg_match('/^channel#/', $id)) {
$id = $this->to_supergroup(preg_replace('|\D+|', '', $id));
$id = $this->to_supergroup(preg_replace('|\\D+|', '', $id));
}
if (preg_match('/^chat#/', $id)) {
$id = preg_replace('|\D+|', '-', $id);
$id = preg_replace('|\\D+|', '-', $id);
}
if (preg_match('/^user#/', $id)) {
$id = preg_replace('|\D+|', '', $id);
$id = preg_replace('|\\D+|', '', $id);
}
}
if (is_numeric($id)) {
if (is_string($id)) {
$id = \danog\MadelineProto\Logger::$bigint ? ((float) $id) : ((int) $id);
$id = \danog\MadelineProto\Logger::$bigint ? (double) $id : (int) $id;
}
if (!isset($this->chats[$id]) && $id < 0 && !preg_match('/^-100/', $id)) {
$this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]);
@ -211,15 +195,11 @@ trait PeerHandler
return $this->gen_all($this->chats[$id]);
}
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true) {
$dbres = json_decode(@file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id='.$id, false, stream_context_create(['http' => [
'timeout' => 2,
],
])), true);
$dbres = json_decode(@file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id=' . $id, false, stream_context_create(['http' => ['timeout' => 2]])), true);
if (isset($dbres['ok']) && $dbres['ok']) {
return $this->get_info('@'.$dbres['result']);
return $this->get_info('@' . $dbres['result']);
}
}
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
$id = strtolower(str_replace('@', '', $id));
@ -230,13 +210,10 @@ trait PeerHandler
}
if ($recursive) {
$this->resolve_username($id);
return $this->get_info($id, false);
}
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
public function gen_all($constructor)
{
$res = [$this->constructors->find_by_predicate($constructor['_'])['type'] => $constructor];
@ -278,20 +255,16 @@ trait PeerHandler
case 'channelForbidden':
throw new \danog\MadelineProto\Exception('Chat forbidden');
break;
default:
throw new \danog\MadelineProto\Exception('Invalid constructor given '.var_export($constructor, true));
throw new \danog\MadelineProto\Exception('Invalid constructor given ' . var_export($constructor, true));
break;
}
return $res;
}
public function full_chat_last_updated($id)
{
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
}
public function get_full_info($id)
{
$partial = $this->get_info($id);
@ -301,26 +274,22 @@ trait PeerHandler
switch ($partial['type']) {
case 'user':
case 'bot':
$full = $this->method_call('users.getFullUser', ['id' => $partial['InputUser']], ['datacenter' => $this->datacenter->curdc]);
break;
$full = $this->method_call('users.getFullUser', ['id' => $partial['InputUser']], ['datacenter' => $this->datacenter->curdc]);
break;
case 'chat':
$full = $this->method_call('messages.getFullChat', $partial, ['datacenter' => $this->datacenter->curdc])['full_chat'];
break;
$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']], ['datacenter' => $this->datacenter->curdc])['full_chat'];
break;
$full = $this->method_call('channels.getFullChannel', ['channel' => $partial['InputChannel']], ['datacenter' => $this->datacenter->curdc])['full_chat'];
break;
}
$res = [];
$res['full'] = $full;
$res['last_update'] = time();
$this->full_chats[$partial['bot_api_id']] = $res;
return array_merge($partial, $res);
}
public function get_pwr_chat($id, $fullfetch = true, $send = true)
{
$full = $fullfetch ? $this->get_full_info($id) : $this->get_info($id);
@ -352,16 +321,16 @@ trait PeerHandler
$res['photo'] = $this->photosize_to_botapi(end($full['full']['profile_photo']['sizes']), []);
}
/*$bio = '';
if ($full['type'] === 'user' && isset($res['username']) && !isset($res['about']) && $fullfetch) {
if (preg_match('/meta property="og:description" content=".+/', file_get_contents('https://telegram.me/'.$res['username']), $biores)) {
$bio = html_entity_decode(preg_replace_callback('/(&#[0-9]+;)/', function ($m) {
return mb_convert_encoding($m[1], 'UTF-8', 'HTML-ENTITIES');
}, str_replace(['meta property="og:description" content="', '">'], '', $biores[0])));
}
if ($bio != '' && $bio != 'You can contact @'.$res['username'].' right away.') {
$res['about'] = $bio;
}
}*/
if ($full['type'] === 'user' && isset($res['username']) && !isset($res['about']) && $fullfetch) {
if (preg_match('/meta property="og:description" content=".+/', file_get_contents('https://telegram.me/'.$res['username']), $biores)) {
$bio = html_entity_decode(preg_replace_callback('/(&#[0-9]+;)/', function ($m) {
return mb_convert_encoding($m[1], 'UTF-8', 'HTML-ENTITIES');
}, str_replace(['meta property="og:description" content="', '">'], '', $biores[0])));
}
if ($bio != '' && $bio != 'You can contact @'.$res['username'].' right away.') {
$res['about'] = $bio;
}
}*/
break;
case 'chat':
foreach (['title', 'participants_count', 'admin', 'admins_enabled'] as $key) {
@ -372,7 +341,6 @@ trait PeerHandler
if (isset($res['admins_enabled'])) {
$res['all_members_are_administrators'] = $res['admins_enabled'];
}
if (isset($full['full']['chat_photo']['sizes'])) {
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), []);
}
@ -395,7 +363,6 @@ trait PeerHandler
$res[$key] = $full['full'][$key];
}
}
if (isset($full['full']['chat_photo']['sizes'])) {
$res['photo'] = $this->photosize_to_botapi(end($full['full']['chat_photo']['sizes']), []);
}
@ -437,16 +404,14 @@ trait PeerHandler
}
switch ($participant['_']) {
case 'chatParticipant':
$newres['role'] = 'user';
break;
$newres['role'] = 'user';
break;
case 'chatParticipantAdmin':
$newres['role'] = 'admin';
break;
$newres['role'] = 'admin';
break;
case 'chatParticipantCreator':
$newres['role'] = 'creator';
break;
$newres['role'] = 'creator';
break;
}
$res['participants'][$key] = $newres;
}
@ -457,7 +422,6 @@ trait PeerHandler
$filters = ['channelParticipantsRecent', 'channelParticipantsAdmins', 'channelParticipantsKicked', 'channelParticipantsBots', 'channelParticipantsBanned'];
foreach ($filters as $filter) {
$offset = -$limit;
try {
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => $filter, 'q' => ''], 'offset' => $offset += $limit, 'limit' => $limit, 'hash' => 0], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -486,36 +450,30 @@ trait PeerHandler
}
switch ($participant['_']) {
case 'channelParticipantSelf':
$newres['role'] = 'user';
if (isset($newres['admin_rights'])) {
$newres['admin_rights'] = $full['Chat']['admin_rights'];
}
if (isset($newres['banned_rights'])) {
$newres['banned_rights'] = $full['Chat']['banned_rights'];
}
break;
$newres['role'] = 'user';
if (isset($newres['admin_rights'])) {
$newres['admin_rights'] = $full['Chat']['admin_rights'];
}
if (isset($newres['banned_rights'])) {
$newres['banned_rights'] = $full['Chat']['banned_rights'];
}
break;
case 'channelParticipant':
$newres['role'] = 'user';
break;
$newres['role'] = 'user';
break;
case 'channelParticipantCreator':
$newres['role'] = 'creator';
break;
$newres['role'] = 'creator';
break;
case 'channelParticipantAdmin':
$newres['role'] = 'admin';
break;
$newres['role'] = 'admin';
break;
case 'channelParticipantBanned':
$newres['role'] = 'banned';
break;
$newres['role'] = 'banned';
break;
}
$res['participants'][$participant['user_id']] = $newres;
}
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => $filter, 'q' => ''], 'offset' => $offset += $limit, 'limit' => $limit, 'hash' => 0], ['datacenter' => $this->datacenter->curdc]);
if (empty($gres['participants'])) {
break;
}
@ -529,10 +487,8 @@ trait PeerHandler
if ($fullfetch || $send) {
$this->store_db($res);
}
return $res;
}
public function store_db($res, $force = false)
{
$settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all'];
@ -560,13 +516,12 @@ trait PeerHandler
if (empty($this->qres)) {
return false;
}
try {
$payload = json_encode($this->qres);
$path = '/tmp/ids'.hash('sha256', $payload);
$path = '/tmp/ids' . hash('sha256', $payload);
file_put_contents($path, $payload);
$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 & ');
$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) {
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::VERBOSE);
@ -574,7 +529,6 @@ trait PeerHandler
$this->qres = [];
$this->last_stored = time() + 10;
}
public function resolve_username($username)
{
try {
@ -585,12 +539,10 @@ trait PeerHandler
if ($res['_'] === 'contacts.resolvedPeer') {
return $res;
}
return false;
}
public function to_supergroup($id)
{
return -($id + pow(10, (int) floor(log($id, 10) + 3)));
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -41,13 +41,12 @@ trait ResponseHandler
}
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id;
}
public function handle_messages($datacenter)
{
$only_updates = true;
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
$unset = false;
\danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Received ' . $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'] . '.'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
//\danog\MadelineProto\Logger::log([$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
if (!$this->synchronized(function ($zis, $datacenter, $current_msg_id) {
@ -55,13 +54,12 @@ trait ResponseHandler
return false;
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'] = true;
return true;
}, $this, $datacenter, $current_msg_id)) {
\danog\MadelineProto\Logger::log([base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].' is already being handled'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log([base64_encode($current_msg_id) . $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'] . ' is already being handled'], \danog\MadelineProto\Logger::VERBOSE);
continue;
}
\danog\MadelineProto\Logger::log(['Handling '.base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Handling ' . base64_encode($current_msg_id) . $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'] . '.'], \danog\MadelineProto\Logger::VERBOSE);
}
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
case 'msgs_ack':
@ -69,15 +67,17 @@ trait ResponseHandler
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $msg_id) {
$this->ack_outgoing_message_id($msg_id, $datacenter); // Acknowledge that the server received my message
$this->ack_outgoing_message_id($msg_id, $datacenter);
// Acknowledge that the server received my message
}
break;
case 'rpc_result':
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request
$this->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;
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result'];
$this->check_in_seq_no($datacenter, $current_msg_id);
@ -88,35 +88,36 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request
$this->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;
break;
case 'bad_server_salt':
case 'bad_msg_notification':
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['bad_msg_id'], $datacenter); // Acknowledge that the server received my request
$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':
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]);
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id'], $datacenter); // Acknowledge that the server received my request
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id'], $datacenter);
// Acknowledge that the server received my request
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_id']]['response'] = $current_msg_id;
break;
case 'new_session_created':
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
$this->datacenter->sockets[$datacenter]->temp_auth_key['server_salt'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['server_salt'];
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
$this->ack_incoming_message_id($current_msg_id, $datacenter);
// Acknowledge that I received the server's response
if ($this->authorized === self::LOGGED_IN && !$this->initing_authorization && $this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key !== null) {
$this->get_updates_difference();
}
@ -128,7 +129,6 @@ trait ResponseHandler
$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($datacenter);
}
$unset = true;
@ -138,14 +138,15 @@ trait ResponseHandler
case 'msg_copy':
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
$this->ack_incoming_message_id($current_msg_id, $datacenter); // 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
$this->ack_incoming_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']['msg_id'], $datacenter);
// Acknowledge that I received the server's response
} else {
$this->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]);
$this->datacenter->sockets[$datacenter]->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']];
$this->datacenter->sockets[$datacenter]->new_incoming[$message['orig_message']['msg_id']] = $message['orig_message']['msg_id'];
$this->handle_messages($datacenter);
}
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
@ -154,7 +155,6 @@ trait ResponseHandler
case 'http_wait':
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
\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;
@ -177,10 +177,9 @@ trait ResponseHandler
$this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256);
$status = 'Status for message id '.$msg_id.': ';
$status = 'Status for message id ' . $msg_id . ': ';
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) !== 0) {
$this->ack_outgoing_message_id($msg_id, $datacenter);
}
@ -240,7 +239,8 @@ trait ResponseHandler
break;
default:
$this->check_in_seq_no($datacenter, $current_msg_id);
$this->ack_incoming_message_id($current_msg_id, $datacenter); // 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
$response_type = $this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'])['type'];
switch ($response_type) {
case 'Updates':
@ -253,26 +253,23 @@ trait ResponseHandler
break;
default:
$only_updates = false;
\danog\MadelineProto\Logger::log(['Trying to assign a response of type '.$response_type.' to its request...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Trying to assign a response of type ' . $response_type . ' to its request...'], \danog\MadelineProto\Logger::VERBOSE);
foreach ($this->datacenter->sockets[$datacenter]->new_outgoing as $key => $expecting) {
\danog\MadelineProto\Logger::log(['Does the request of return type '.$expecting['type'].' match?'], \danog\MadelineProto\Logger::VERBOSE);
\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->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]);
break 2;
}
\danog\MadelineProto\Logger::log(['No'], \danog\MadelineProto\Logger::VERBOSE);
}
throw new \danog\MadelineProto\ResponseException('Dunno how to handle '.PHP_EOL.var_export($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'], 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($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']);
}
@ -288,23 +285,19 @@ trait ResponseHandler
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['_'])) {
switch ($this->constructors->find_by_predicate($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']['_'])['type']) {
case 'Update':
$this->handle_update($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']);
break;
$this->handle_update($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']);
break;
}
}
if ($unset) {
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
}
}
return $only_updates;
}
public function handle_messages_threaded()
{
}
public function handle_rpc_error($server_answer, &$aargs)
{
if (in_array($server_answer['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
@ -313,8 +306,7 @@ trait ResponseHandler
switch ($server_answer['error_code']) {
case 303:
$this->datacenter->curdc = $aargs['datacenter'] = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
throw new \danog\MadelineProto\Exception('Received request to switch to DC ' . $this->datacenter->curdc);
case 401:
switch ($server_answer['error_message']) {
case 'USER_DEACTIVATED':
@ -327,7 +319,8 @@ trait ResponseHandler
}
$this->authorized = self::NOT_LOGGED_IN;
$this->authorization = null;
$this->init_authorization(); // idk
$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':
@ -337,30 +330,32 @@ trait ResponseHandler
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->datacenter->sockets[$aargs['datacenter']]->auth_key = null;
$this->datacenter->sockets[$aargs['datacenter']]->authorized = false;
$this->init_authorization(); // idk
$this->init_authorization();
// idk
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
case 'AUTH_KEY_PERM_EMPTY':
if ($this->authorized !== self::LOGGED_IN) {
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
}
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization(); // idk
$this->init_authorization();
// idk
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
}
case 420:
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
$limit = isset($aargs['FloodWaitLimit']) ? $aargs['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt'];
if (is_numeric($seconds) && $seconds < $limit) {
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Flood, waiting ' . $seconds . ' seconds...'], \danog\MadelineProto\Logger::NOTICE);
sleep($seconds);
throw new \danog\MadelineProto\Exception('Re-executing query...');
}
case 500:
throw new \danog\MadelineProto\Exception('Re-executing query after server error...');
default:
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
}
}
public function handle_pending_updates()
{
if ($this->postpone_updates) {
@ -374,7 +369,6 @@ trait ResponseHandler
}
}
}
public function handle_updates($updates)
{
if (!$this->settings['updates']['handle_updates']) {
@ -383,7 +377,6 @@ trait ResponseHandler
if ($this->postpone_updates) {
\danog\MadelineProto\Logger::log(['Postpone update handling'], \danog\MadelineProto\Logger::VERBOSE);
$this->pending_updates[] = $updates;
return false;
}
$this->handle_pending_updates();
@ -407,20 +400,11 @@ trait ResponseHandler
case 'updateShortMessage':
case 'updateShortChatMessage':
$from_id = isset($updates['from_id']) ? $updates['from_id'] : ($updates['out'] ? $this->authorization['user']['id'] : $updates['user_id']);
$to_id = isset($updates['chat_id'])
? -$updates['chat_id']
: ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
if (!$this->peer_isset($from_id) ||
!$this->peer_isset($to_id) ||
(isset($updates['via_bot_id']) && !$this->peer_isset($updates['via_bot_id'])) ||
(isset($updates['entities']) && !$this->entities_peer_isset($updates['entities'])) ||
(isset($updates['fwd_from']) && !$this->fwd_peer_isset($updates['fwd_from']))) {
$to_id = isset($updates['chat_id']) ? -$updates['chat_id'] : ($updates['out'] ? $updates['user_id'] : $this->authorization['user']['id']);
if (!$this->peer_isset($from_id) || !$this->peer_isset($to_id) || isset($updates['via_bot_id']) && !$this->peer_isset($updates['via_bot_id']) || isset($updates['entities']) && !$this->entities_peer_isset($updates['entities']) || isset($updates['fwd_from']) && !$this->fwd_peer_isset($updates['fwd_from'])) {
\danog\MadelineProto\Logger::log(['getDifference: good - getting user for updateShortMessage'], \danog\MadelineProto\Logger::VERBOSE);
$this->get_updates_difference();
}
$message = $updates;
$message['_'] = 'message';
$message['from_id'] = $from_id;
@ -435,7 +419,7 @@ trait ResponseHandler
$this->get_updates_difference();
break;
default:
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: '.var_export($updates, true));
throw new \danog\MadelineProto\ResponseException('Unrecognized update received: ' . var_export($updates, true));
break;
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -23,16 +23,14 @@ trait SaltHandler
$this->add_salt($salt['valid_since'], $salt['valid_until'], $salt['salt']);
}
}
public function add_salt($valid_since, $valid_until, $salt)
{
if (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt])) {
$this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
}
}
public function handle_future_salts($salt)
{
$this->method_call('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -23,61 +23,55 @@ trait SeqNoHandler
$value = $this->datacenter->sockets[$datacenter]->session_out_seq_no;
$this->datacenter->sockets[$datacenter]->session_out_seq_no += $in;
//var_dump("OUT $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_out_seq_no);
return ($value * 2) + $in;
return $value * 2 + $in;
}
public function check_in_seq_no($datacenter, $current_msg_id)
{
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) && ($seq_no = $this->generate_in_seq_no($datacenter, $this->content_related($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']))) !== $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no']) {
//\danog\MadelineProto\Logger::log(['SECURITY WARNING: Seqno mismatch (should be '.$seq_no.', is '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['seq_no'].', '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].')'], \danog\MadelineProto\Logger::ERROR);
}
}
public function generate_in_seq_no($datacenter, $content_related)
{
$in = $content_related ? 1 : 0;
$value = $this->datacenter->sockets[$datacenter]->session_in_seq_no;
$this->datacenter->sockets[$datacenter]->session_in_seq_no += $in;
//var_dump("IN $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_in_seq_no);
return ($value * 2) + $in;
return $value * 2 + $in;
}
public function content_related($method)
{
return isset($method['_']) ? !in_array(
$method['_'],
[
'rpc_result',
// 'rpc_error',
'rpc_drop_answer',
'rpc_answer_unknown',
'rpc_answer_dropped_running',
'rpc_answer_dropped',
'get_future_salts',
'future_salt',
'future_salts',
'ping',
'pong',
'ping_delay_disconnect',
'destroy_session',
'destroy_session_ok',
'destroy_session_none',
// 'new_session_created',
'msg_container',
'msg_copy',
'gzip_packed',
'http_wait',
'msgs_ack',
'bad_msg_notification',
'bad_server_salt',
'msgs_state_req',
'msgs_state_info',
'msgs_all_info',
'msg_detailed_info',
'msg_new_detailed_info',
'msg_resend_req',
'msg_resend_ans_req',
]
) : true;
return isset($method['_']) ? !in_array($method['_'], [
'rpc_result',
// 'rpc_error',
'rpc_drop_answer',
'rpc_answer_unknown',
'rpc_answer_dropped_running',
'rpc_answer_dropped',
'get_future_salts',
'future_salt',
'future_salts',
'ping',
'pong',
'ping_delay_disconnect',
'destroy_session',
'destroy_session_ok',
'destroy_session_none',
// 'new_session_created',
'msg_container',
'msg_copy',
'gzip_packed',
'http_wait',
'msgs_ack',
'bad_msg_notification',
'bad_server_salt',
'msgs_state_req',
'msgs_state_info',
'msgs_all_info',
'msg_detailed_info',
'msg_new_detailed_info',
'msg_resend_req',
'msg_resend_ans_req',
]) : true;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\MTProtoTools;
/**
@ -23,7 +23,6 @@ trait UpdateHandler
private $channels_state = [];
public $updates = [];
public $updates_key = 0;
public function pwr_update_handler($update)
{
/*
@ -53,10 +52,9 @@ trait UpdateHandler
if (is_string($this->settings['pwr']['update_handler'])) {
return $this->{$this->settings['pwr']['update_handler']}($update);
}
in_array($this->settings['pwr']['update_handler'], [['danog\MadelineProto\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['pwr']['update_handler']($update);
in_array($this->settings['pwr']['update_handler'], [['danog\\MadelineProto\\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['pwr']['update_handler']($update);
}
}
public function get_updates_update_handler($update)
{
if (!$this->settings['updates']['handle_updates']) {
@ -65,7 +63,6 @@ trait UpdateHandler
$this->updates[$this->updates_key++] = $update;
//\danog\MadelineProto\Logger::log(['Stored ', $update);
}
public function get_updates($params = [])
{
if (!$this->settings['updates']['handle_updates']) {
@ -76,9 +73,7 @@ trait UpdateHandler
$controller->discard();
}
});
$time = microtime(true);
try {
try {
if (($error = $this->recv_message($this->datacenter->curdc)) !== true) {
@ -87,11 +82,9 @@ trait UpdateHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key = null;
$this->init_authorization();
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
}
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$only_updates = $this->handle_messages($this->datacenter->curdc);
@ -107,7 +100,6 @@ trait UpdateHandler
} catch (\danog\MadelineProto\Exception $e) {
$this->connect_to_all_dcs();
}
$default_params = ['offset' => 0, 'limit' => null, 'timeout' => 0];
foreach ($default_params as $key => $default) {
if (!isset($params[$key])) {
@ -135,26 +127,21 @@ trait UpdateHandler
$updates[] = ['update_id' => $key, 'update' => $value];
}
}
return $updates;
}
public function &load_channel_state($channel, $pts = 0)
{
if (!isset($this->channels_state[$channel])) {
$this->channels_state[$channel] = ['pts' => $pts, 'sync_loading' => false];
}
return $this->channels_state[$channel];
}
public function set_channel_state($channel, $data)
{
if (isset($data['pts']) && $data['pts'] !== 0) {
$this->load_channel_state($channel)['pts'] = $data['pts'];
}
}
public function check_msg_id($message)
{
try {
@ -163,30 +150,24 @@ trait UpdateHandler
return true;
}
$message_id = $message['id'];
if (!isset($this->msg_ids[$peer_id]) || $message_id > $this->msg_ids[$peer_id]) {
$this->msg_ids[$peer_id] = $message_id;
return true;
}
return false;
}
public function get_channel_difference($channel)
{
if (!$this->settings['updates']['handle_updates']) {
return;
}
if ($this->load_channel_state($channel)['sync_loading']) {
\danog\MadelineProto\Logger::log(['Not fetching '.$channel.' difference, I am already fetching it']);
\danog\MadelineProto\Logger::log(['Not fetching ' . $channel . ' difference, I am already fetching it']);
return;
}
$this->load_channel_state($channel)['sync_loading'] = true;
try {
$input = $this->get_info('channel#'.$channel);
$input = $this->get_info('channel#' . $channel);
if (!isset($input['InputChannel'])) {
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
@ -199,20 +180,17 @@ trait UpdateHandler
$this->load_channel_state($channel)['sync_loading'] = false;
}
$this->load_channel_state($channel)['sync_loading'] = true;
\danog\MadelineProto\Logger::log(['Fetching '.$channel.' difference...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Fetching ' . $channel . ' difference...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
try {
$difference = $this->method_call('updates.getChannelDifference', ['channel' => $input, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $this->load_channel_state($channel)['pts'], 'limit' => 30], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->getMessage() === "You haven't joined this channel/supergroup") {
return false;
}
throw $e;
} catch (\danog\MadelineProto\PTSException $e) {
unset($this->channels_state[$channel]);
$this->load_channel_state($channel)['sync_loading'] = false;
return $this->get_channel_difference($channel);
} finally {
$this->load_channel_state($channel)['sync_loading'] = false;
@ -224,7 +202,6 @@ trait UpdateHandler
break;
case 'updates.channelDifference':
$this->load_channel_state($channel)['sync_loading'] = true;
try {
$this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['new_messages'], $channel);
@ -238,9 +215,8 @@ trait UpdateHandler
}
break;
case 'updates.channelDifferenceTooLong':
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Got ' . $difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->load_channel_state($channel)['sync_loading'] = true;
try {
$this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['messages'], $channel);
@ -251,11 +227,10 @@ trait UpdateHandler
$this->get_channel_difference($channel);
break;
default:
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: ' . var_export($difference, true));
break;
}
}
public function set_update_state($data)
{
if (isset($data['pts']) && $data['pts'] !== 0) {
@ -271,7 +246,6 @@ trait UpdateHandler
$this->load_update_state()['date'] = $data['date'];
}
}
public function &load_update_state()
{
if (!isset($this->updates_state['qts'])) {
@ -281,10 +255,8 @@ trait UpdateHandler
$this->got_state = true;
$this->set_update_state($this->get_updates_state());
}
return $this->updates_state;
}
public function get_updates_difference()
{
if (!$this->settings['updates']['handle_updates']) {
@ -292,13 +264,10 @@ trait UpdateHandler
}
if ($this->updates_state['sync_loading']) {
\danog\MadelineProto\Logger::log(['Not fetching normal difference, I am already fetching it']);
return false;
}
$this->updates_state['sync_loading'] = true;
\danog\MadelineProto\Logger::log(['Fetching normal difference...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
while (!isset($difference)) {
try {
$difference = $this->method_call('updates.getDifference', ['pts' => $this->load_update_state()['pts'], 'date' => $this->load_update_state()['date'], 'qts' => $this->load_update_state()['qts']], ['datacenter' => $this->datacenter->curdc]);
@ -309,71 +278,62 @@ trait UpdateHandler
$this->updates_state['sync_loading'] = false;
}
}
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
\danog\MadelineProto\Logger::log(['Got ' . $difference['_']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
try {
switch ($difference['_']) {
case 'updates.differenceEmpty':
$this->set_update_state($difference);
break;
case 'updates.difference':
$this->updates_state['sync_loading'] = true;
$this->handle_multiple_update($difference['other_updates']);
foreach ($difference['new_encrypted_messages'] as $encrypted) {
$this->handle_encrypted_update(['_' => 'updateNewEncryptedMessage', 'message' => $encrypted], true);
}
$this->handle_update_messages($difference['new_messages']);
$this->set_update_state($difference['state']);
break;
case 'updates.differenceSlice':
$this->updates_state['sync_loading'] = true;
$this->handle_multiple_update($difference['other_updates']);
$this->handle_update_messages($difference['new_messages']);
$this->set_update_state($difference['intermediate_state']);
unset($difference);
$this->updates_state['sync_loading'] = false;
$this->get_updates_difference();
break;
default:
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
break;
}
case 'updates.differenceEmpty':
$this->set_update_state($difference);
break;
case 'updates.difference':
$this->updates_state['sync_loading'] = true;
$this->handle_multiple_update($difference['other_updates']);
foreach ($difference['new_encrypted_messages'] as $encrypted) {
$this->handle_encrypted_update(['_' => 'updateNewEncryptedMessage', 'message' => $encrypted], true);
}
$this->handle_update_messages($difference['new_messages']);
$this->set_update_state($difference['state']);
break;
case 'updates.differenceSlice':
$this->updates_state['sync_loading'] = true;
$this->handle_multiple_update($difference['other_updates']);
$this->handle_update_messages($difference['new_messages']);
$this->set_update_state($difference['intermediate_state']);
unset($difference);
$this->updates_state['sync_loading'] = false;
$this->get_updates_difference();
break;
default:
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: ' . var_export($difference, true));
break;
}
} finally {
$this->updates_state['sync_loading'] = false;
}
}
public function get_updates_state()
{
$last = $this->updates_state['sync_loading'];
$this->updates_state['sync_loading'] = true;
try {
$data = $this->method_call('updates.getState', [], ['datacenter' => $this->datacenter->curdc]);
$this->get_cdn_config($this->datacenter->curdc);
} finally {
$this->updates_state['sync_loading'] = $last;
}
return $data;
}
public function handle_update($update, $options = [])
{
if (!$this->settings['updates']['handle_updates']) {
return;
}
\danog\MadelineProto\Logger::log(['Handling an update of type '.$update['_'].'...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Handling an update of type ' . $update['_'] . '...'], \danog\MadelineProto\Logger::VERBOSE);
$channel_id = false;
switch ($update['_']) {
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
if ($update['message']['_'] === 'messageEmpty') {
\danog\MadelineProto\Logger::log(['Got message empty, not saving'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
return false;
}
$channel_id = $update['message']['to_id']['channel_id'];
@ -386,85 +346,68 @@ trait UpdateHandler
\danog\MadelineProto\Logger::log(['Got channel too long update, getting difference...'], \danog\MadelineProto\Logger::VERBOSE);
if (!isset($this->channels_state[$channel_id]) && !isset($update['pts'])) {
\danog\MadelineProto\Logger::log(['I do not have the channel in the states and the pts is not set.'], \danog\MadelineProto\Logger::ERROR);
return;
}
break;
}
if ($channel_id === false) {
$cur_state = &$this->load_update_state();
$cur_state =& $this->load_update_state();
} else {
$cur_state = &$this->load_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
$cur_state =& $this->load_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
}
/*
if ($cur_state['sync_loading'] && in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
\danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
return false;
}*/
if ($cur_state['sync_loading'] && in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
\danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
return false;
}*/
switch ($update['_']) {
case 'updateChannelTooLong':
$this->get_channel_difference($channel_id);
return false;
case 'updateNewMessage':
case 'updateEditMessage':
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
if ((isset($update['message']['from_id']) && !$this->peer_isset($update['message']['from_id'])) ||
!$this->peer_isset($update['message']['to_id']) ||
(isset($update['message']['via_bot_id']) && !$this->peer_isset($update['message']['via_bot_id'])) ||
(isset($update['message']['entities']) && !$this->entities_peer_isset($update['message']['entities'])) ||
(isset($update['message']['fwd_from']) && !$this->fwd_peer_isset($update['message']['fwd_from']))) {
if (isset($update['message']['from_id']) && !$this->peer_isset($update['message']['from_id']) || !$this->peer_isset($update['message']['to_id']) || isset($update['message']['via_bot_id']) && !$this->peer_isset($update['message']['via_bot_id']) || isset($update['message']['entities']) && !$this->entities_peer_isset($update['message']['entities']) || isset($update['message']['fwd_from']) && !$this->fwd_peer_isset($update['message']['fwd_from'])) {
\danog\MadelineProto\Logger::log(['Not enough data for message update, getting difference...'], \danog\MadelineProto\Logger::VERBOSE);
if ($channel_id !== false && $this->peer_isset($this->to_supergroup($channel_id))) {
$this->get_channel_difference($channel_id);
} else {
$this->get_updates_difference();
}
return false;
}
break;
default:
if ($channel_id !== false && !$this->peer_isset($this->to_supergroup($channel_id))) {
\danog\MadelineProto\Logger::log(['Skipping update, I do not have the channel id '.$channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Skipping update, I do not have the channel id ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
return false;
}
break;
}
if (isset($update['pts'])) {
if ($update['pts'] < $cur_state['pts']) {
\danog\MadelineProto\Logger::log(['Duplicate update, channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Duplicate update, channel id: ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
return false;
}
if ($cur_state['pts'] + (isset($update['pts_count']) ? $update['pts_count'] : 0) !== $update['pts']) {
\danog\MadelineProto\Logger::log(['Pts hole. current pts: '.$cur_state['pts'].', pts count: '.(isset($update['pts_count']) ? $update['pts_count'] : 0).', pts: '.$update['pts'].', channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Pts hole. current pts: ' . $cur_state['pts'] . ', pts count: ' . (isset($update['pts_count']) ? $update['pts_count'] : 0) . ', pts: ' . $update['pts'] . ', channel id: ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
if ($channel_id !== false && $this->peer_isset($this->to_supergroup($channel_id))) {
$this->get_channel_difference($channel_id);
} else {
$this->get_updates_difference();
}
return false;
}
if (isset($update['message']['id'], $update['message']['to_id'])) {
if (!$this->check_msg_id($update['message'])) {
\danog\MadelineProto\Logger::log(['Duplicate update by message id, channel id: '.$channel_id], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Duplicate update by message id, channel id: ' . $channel_id], \danog\MadelineProto\Logger::ERROR);
return false;
}
}
\danog\MadelineProto\Logger::log(['Applying pts. current pts: '.$cur_state['pts'].', new pts: '.$update['pts'].', channel id: '.$channel_id], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Applying pts. current pts: ' . $cur_state['pts'] . ', new pts: ' . $update['pts'] . ', channel id: ' . $channel_id], \danog\MadelineProto\Logger::VERBOSE);
$cur_state['pts'] = $update['pts'];
if ($channel_id === false && isset($options['date']) && $cur_state['date'] < $options['date']) {
$cur_state['date'] = $options['date'];
}
@ -473,12 +416,10 @@ trait UpdateHandler
$seq = $options['seq'];
$seq_start = isset($options['seq_start']) ? $options['seq_start'] : $options['seq'];
if ($seq_start != $cur_state['seq'] + 1 && $seq_start > $cur_state['seq']) {
\danog\MadelineProto\Logger::log(['Seq hole. seq_start: '.$seq_start.' != cur seq: '.$cur_state['seq'].' + 1'], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Seq hole. seq_start: ' . $seq_start . ' != cur seq: ' . $cur_state['seq'] . ' + 1'], \danog\MadelineProto\Logger::ERROR);
$this->get_updates_difference();
return false;
}
if ($cur_state['seq'] !== $seq) {
$cur_state['seq'] = $seq;
if (isset($options['date']) && $cur_state['date'] < $options['date']) {
@ -488,7 +429,6 @@ trait UpdateHandler
}
$this->save_update($update);
}
public function handle_multiple_update($updates, $options = [], $channel = false)
{
if (!$this->settings['updates']['handle_updates']) {
@ -504,7 +444,6 @@ trait UpdateHandler
}
}
}
public function handle_update_messages($messages, $channel = false)
{
if (!$this->settings['updates']['handle_updates']) {
@ -514,7 +453,6 @@ trait UpdateHandler
$this->handle_update(['_' => $channel === false ? 'updateNewMessage' : 'updateNewChannelMessage', 'message' => $message, 'pts' => $channel === false ? $this->load_update_state()['pts'] : $this->load_channel_state($channel)['pts'], 'pts_count' => 0]);
}
}
public function save_update($update)
{
array_walk($this->calls, function ($controller, $id) {
@ -525,46 +463,39 @@ trait UpdateHandler
if ($update['_'] === 'updateDcOptions') {
\danog\MadelineProto\Logger::log(['Got new dc options'], \danog\MadelineProto\Logger::VERBOSE);
$this->parse_dc_options($update['dc_options']);
return;
}
if ($update['_'] === 'updatePhoneCall') {
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
\danog\MadelineProto\Logger::log(['The php-libtgvoip extension is required to accept and manage calls. See daniil.it/MadelineProto for more info.'], \danog\MadelineProto\Logger::WARNING);
return;
}
switch ($update['phone_call']['_']) {
case 'phoneCallRequested':
if (isset($this->calls[$update['phone_call']['id']])) {
return;
}
$controller = new \danog\MadelineProto\VoIP(false, $update['phone_call']['admin_id'], ['_' => 'inputPhoneCall', 'id' => $update['phone_call']['id'], 'access_hash' => $update['phone_call']['access_hash']], $this, \danog\MadelineProto\VoIP::CALL_STATE_INCOMING, $update['phone_call']['protocol']);
$controller->storage = ['g_a_hash' => $update['phone_call']['g_a_hash']];
$update['phone_call'] = $this->calls[$update['phone_call']['id']] = $controller;
break;
if (isset($this->calls[$update['phone_call']['id']])) {
return;
}
$controller = new \danog\MadelineProto\VoIP(false, $update['phone_call']['admin_id'], ['_' => 'inputPhoneCall', 'id' => $update['phone_call']['id'], 'access_hash' => $update['phone_call']['access_hash']], $this, \danog\MadelineProto\VoIP::CALL_STATE_INCOMING, $update['phone_call']['protocol']);
$controller->storage = ['g_a_hash' => $update['phone_call']['g_a_hash']];
$update['phone_call'] = $this->calls[$update['phone_call']['id']] = $controller;
break;
case 'phoneCallAccepted':
if (!$this->confirm_call($update['phone_call'])) {
return;
}
$update['phone_call'] = $this->calls[$update['phone_call']['id']];
break;
if (!$this->confirm_call($update['phone_call'])) {
return;
}
$update['phone_call'] = $this->calls[$update['phone_call']['id']];
break;
case 'phoneCall':
if (!$this->complete_call($update['phone_call'])) {
return;
}
$update['phone_call'] = $this->calls[$update['phone_call']['id']];
break;
if (!$this->complete_call($update['phone_call'])) {
return;
}
$update['phone_call'] = $this->calls[$update['phone_call']['id']];
break;
case 'phoneCallDiscarded':
if (!isset($this->calls[$update['phone_call']['id']])) {
return;
}
return $this->calls[$update['phone_call']['id']]->discard(['_' => 'phoneCallDiscardReasonHangup'], [], $update['phone_call']['need_debug']);
if (!isset($this->calls[$update['phone_call']['id']])) {
return;
}
return $this->calls[$update['phone_call']['id']]->discard(['_' => 'phoneCallDiscardReasonHangup'], [], $update['phone_call']['need_debug']);
}
}
if ($update['_'] === 'updateNewEncryptedMessage' && !isset($update['message']['decrypted_message'])) {
@ -573,20 +504,17 @@ trait UpdateHandler
$cur_state['qts'] = $update['qts'];
}
if ($update['qts'] < $cur_state['qts']) {
\danog\MadelineProto\Logger::log(['Duplicate update. update qts: '.$update['qts'].' <= current qts '.$cur_state['qts'].', chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Duplicate update. update qts: ' . $update['qts'] . ' <= current qts ' . $cur_state['qts'] . ', chat id: ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
return false;
}
if ($update['qts'] > $cur_state['qts'] + 1) {
\danog\MadelineProto\Logger::log(['Qts hole. Fetching updates manually: update qts: '.$update['qts'].' > current qts '.$cur_state['qts'].'+1, chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::ERROR);
\danog\MadelineProto\Logger::log(['Qts hole. Fetching updates manually: 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;
}
\danog\MadelineProto\Logger::log(['Applying qts: '.$update['qts'].' over current qts '.$cur_state['qts'].', chat id: '.$update['message']['chat_id']], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Applying qts: ' . $update['qts'] . ' over current qts ' . $cur_state['qts'] . ', chat id: ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::VERBOSE);
$this->method_call('messages.receivedQueue', ['max_qts' => $cur_state['qts'] = $update['qts']], ['datacenter' => $this->datacenter->curdc]);
$this->handle_encrypted_update($update);
return;
}
/*
@ -597,29 +525,25 @@ trait UpdateHandler
if ($update['_'] === 'updateEncryption') {
switch ($update['chat']['_']) {
case 'encryptedChatRequested':
if ($this->settings['secret_chats']['accept_chats'] === false || (is_array($this->settings['secret_chats']['accept_chats']) && !in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats']))) {
return;
}
\danog\MadelineProto\Logger::log(['Accepting secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->accept_secret_chat($update['chat']);
break;
if ($this->settings['secret_chats']['accept_chats'] === false || is_array($this->settings['secret_chats']['accept_chats']) && !in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats'])) {
return;
}
\danog\MadelineProto\Logger::log(['Accepting secret chat ' . $update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->accept_secret_chat($update['chat']);
break;
case 'encryptedChatDiscarded':
\danog\MadelineProto\Logger::log(['Deleting secret chat '.$update['chat']['id'].' because it was revoked by the other user'], \danog\MadelineProto\Logger::NOTICE);
if (isset($this->secret_chats[$update['chat']['id']])) {
unset($this->secret_chats[$update['chat']['id']]);
}
if (isset($this->temp_requested_secret_chats[$update['chat']['id']])) {
unset($this->temp_requested_secret_chats[$update['chat']['id']]);
}
break;
\danog\MadelineProto\Logger::log(['Deleting secret chat ' . $update['chat']['id'] . ' because it was revoked by the other user'], \danog\MadelineProto\Logger::NOTICE);
if (isset($this->secret_chats[$update['chat']['id']])) {
unset($this->secret_chats[$update['chat']['id']]);
}
if (isset($this->temp_requested_secret_chats[$update['chat']['id']])) {
unset($this->temp_requested_secret_chats[$update['chat']['id']]);
}
break;
case 'encryptedChat':
\danog\MadelineProto\Logger::log(['Completing creation of secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->complete_secret_chat($update['chat']);
break;
\danog\MadelineProto\Logger::log(['Completing creation of secret chat ' . $update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->complete_secret_chat($update['chat']);
break;
}
//\danog\MadelineProto\Logger::log([$update], \danog\MadelineProto\Logger::NOTICE);
}
@ -632,21 +556,19 @@ trait UpdateHandler
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);
\danog\MadelineProto\Logger::log(['Saving an update of type ' . $update['_'] . '...'], \danog\MadelineProto\Logger::VERBOSE);
if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict'] && isset($this->settings['pwr']['update_handler'])) {
$this->pwr_update_handler($update);
} else {
in_array($this->settings['updates']['callback'], [['danog\MadelineProto\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['updates']['callback']($update);
in_array($this->settings['updates']['callback'], [['danog\\MadelineProto\\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['updates']['callback']($update);
}
}
public function pwr_webhook($update)
{
$payload = json_encode($update);
\danog\MadelineProto\Logger::log([$update, $payload, json_last_error()]);
if ($payload === '') {
\danog\MadelineProto\Logger::log(['EMPTY UPDATE']);
return false;
}
$ch = curl_init();
@ -665,9 +587,8 @@ trait UpdateHandler
}
}
$result = curl_exec($ch);
curl_close($ch);
\danog\MadelineProto\Logger::log(['Result of webhook query is '.$result], \danog\MadelineProto\Logger::NOTICE);
\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'])) {
try {
@ -679,4 +600,4 @@ trait UpdateHandler
}
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,9 +10,8 @@ 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;
class NothingInTheSocketException extends \Exception
{
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,21 +10,18 @@ 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;
class PTSException extends \Exception
{
use TL\PrettyException;
public function __toString()
{
return get_class($this).($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.'TL Trace:'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
return get_class($this) . ($this->message !== '' ? ': ' : '') . $this->message . PHP_EOL . 'TL Trace:' . PHP_EOL . PHP_EOL . $this->getTLTrace() . PHP_EOL;
}
public function __construct($message, $file = '')
{
parent::__construct($message);
$this->prettify_tl($file);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,38 +10,23 @@ 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;
interface Proxy
{
public function __construct(int $domain, int $type, int $protocol);
public function setOption(int $level, int $name, $value);
public function getOption(int $level, int $name);
public function setBlocking(bool $blocking);
public function bind(string $address, int $port = 0);
public function listen(int $backlog = 0);
public function __construct($domain, $type, $protocol);
public function setOption($level, $name, $value);
public function getOption($level, $name);
public function setBlocking($blocking);
public function bind($address, $port = 0);
public function listen($backlog = 0);
public function accept();
public function connect(string $address, int $port = 0);
public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0);
public function read(int $length, int $flags = 0);
public function write(string $buffer, int $length = -1);
public function send(string $data, int $length, int $flags);
public function connect($address, $port = 0);
public function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0);
public function read($length, $flags = 0);
public function write($buffer, $length = -1);
public function send($data, $length, $flags);
public function close();
public function getPeerName(bool $port = true);
public function getSockName(bool $port = true);
}
public function getPeerName($port = true);
public function getSockName($port = true);
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
class RPCErrorException extends \Exception
@ -17,75 +17,152 @@ class RPCErrorException extends \Exception
use TL\PrettyException;
private $fetched = false;
public static $rollbar = true;
public function getMess()
{
if ($this->fetched === false) {
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?method='.$additional[0].'&code='.$code.'&error='.$this->rpc), true);
$res = json_decode(@file_get_contents('https://rpc.pwrtelegram.xyz/?method=' . $additional[0] . '&code=' . $code . '&error=' . $this->rpc), true);
if (isset($res['ok']) && $res['ok']) {
$this->message = $res['result'];
}
}
return $this->message;
}
public function __toString()
{
return sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess(), $this->rpc, $this->file, $this->line.PHP_EOL.PHP_EOL).PHP_EOL.'Revision: '.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.$this->getTLTrace().PHP_EOL;
return sprintf(\danog\MadelineProto\Lang::$current_lang['rpc_tg_error'], $this->getMess(), $this->rpc, $this->file, $this->line . PHP_EOL . PHP_EOL) . PHP_EOL . 'Revision: ' . @file_get_contents(__DIR__ . '/../../../.git/refs/heads/master') . PHP_EOL . $this->getTLTrace() . PHP_EOL;
}
public function __construct($message = null, $code = 0, Exception $previous = null)
{
$this->rpc = $message;
switch ($message) {
case 'RPC_MCGET_FAIL':
case 'RPC_CALL_FAIL': $message = 'Telegram is having internal issues, please try again later.'; break;
case 'USER_PRIVACY_RESTRICTED':$message = "The user's privacy settings do not allow you to do this"; break;
case 'CHANNEL_PRIVATE':$message = "You haven't joined this channel/supergroup"; break;
case 'FLOOD_WAIT_666':$message = 'Spooky af m8'; break;
case 'USER_IS_BOT':$message = "Bots can't send messages to other bots"; break;
case 'BOT_METHOD_INVALID':$message = 'This method cannot be run by a bot'; break;
case 'PHONE_CODE_EXPIRED': $message = 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)'; break;
case 'USERNAME_INVALID': $message = 'The provided username is not valid'; break;
case 'ACCESS_TOKEN_INVALID': $message = 'The provided token is not valid'; break;
case 'ACTIVE_USER_REQUIRED': $message = 'The method is only available to already activated users'; break;
case 'FIRSTNAME_INVALID': $message = 'The first name is invalid'; break;
case 'LASTNAME_INVALID': $message = 'The last name is invalid'; break;
case 'PHONE_NUMBER_INVALID': $message = 'The phone number is invalid'; break;
case 'PHONE_CODE_HASH_EMPTY': $message = 'phone_code_hash is missing'; break;
case 'PHONE_CODE_EMPTY': $message = 'phone_code is missing'; break;
case 'PHONE_CODE_EXPIRED': $message = 'The confirmation code has expired'; break;
case 'API_ID_INVALID': $message = 'The api_id/api_hash combination is invalid'; break;
case 'PHONE_NUMBER_OCCUPIED': $message = 'The phone number is already in use'; break;
case 'PHONE_NUMBER_UNOCCUPIED': $message = 'The phone number is not yet being used'; break;
case 'USERS_TOO_FEW': $message = 'Not enough users (to create a chat, for example)'; break;
case 'USERS_TOO_MUCH': $message = 'The maximum number of users has been exceeded (to create a chat, for example)'; break;
case 'TYPE_CONSTRUCTOR_INVALID': $message = 'The type constructor is invalid'; break;
case 'FILE_PART_INVALID': $message = 'The file part number is invalid'; break;
case 'FILE_PARTS_INVALID': $message = 'The number of file parts is invalid'; break;
case 'MD5_CHECKSUM_INVALID': $message = 'The MD5 checksums do not match'; break;
case 'PHOTO_INVALID_DIMENSIONS': $message = 'The photo dimensions are invalid'; break;
case 'FIELD_NAME_INVALID': $message = 'The field with the name FIELD_NAME is invalid'; break;
case 'FIELD_NAME_EMPTY': $message = 'The field with the name FIELD_NAME is missing'; break;
case 'MSG_WAIT_FAILED': $message = 'A waiting call returned an error'; break;
case 'USERNAME_NOT_OCCUPIED': $message = 'The provided username is not occupied'; break;
case 'PHONE_NUMBER_BANNED': $message = 'The provided phone number is banned from telegram'; break;
case 'AUTH_KEY_UNREGISTERED': $message = 'The authorization key has expired'; break;
case 'INVITE_HASH_EXPIRED': $message = 'The invite link has expired'; break;
case 'USER_DEACTIVATED': $message = 'The user was deactivated'; break;
case 'USER_ALREADY_PARTICIPANT': $message = 'The user is already in the group'; break;
case 'MESSAGE_ID_INVALID': $message = 'The provided message id is invalid'; break;
case 'PEER_ID_INVALID': $message = 'The provided peer id is invalid'; break;
case 'CHAT_ID_INVALID': $message = 'The provided chat id is invalid'; break;
case 'MESSAGE_DELETE_FORBIDDEN': $message = "You can't delete one of the messages you tried to delete, most likely because it is a service message."; break;
case 'CHAT_ADMIN_REQUIRED': $message = 'You must be an admin in this chat to do this'; break;
case 'RPC_CALL_FAIL':
$message = 'Telegram is having internal issues, please try again later.';
break;
case 'USER_PRIVACY_RESTRICTED':
$message = "The user's privacy settings do not allow you to do this";
break;
case 'CHANNEL_PRIVATE':
$message = "You haven't joined this channel/supergroup";
break;
case 'FLOOD_WAIT_666':
$message = 'Spooky af m8';
break;
case 'USER_IS_BOT':
$message = "Bots can't send messages to other bots";
break;
case 'BOT_METHOD_INVALID':
$message = 'This method cannot be run by a bot';
break;
case 'PHONE_CODE_EXPIRED':
$message = 'The phone code you provided has expired, this may happen if it was sent to any chat on telegram (if the code is sent through a telegram chat (not the official account) to avoid it append or prepend to the code some chars)';
break;
case 'USERNAME_INVALID':
$message = 'The provided username is not valid';
break;
case 'ACCESS_TOKEN_INVALID':
$message = 'The provided token is not valid';
break;
case 'ACTIVE_USER_REQUIRED':
$message = 'The method is only available to already activated users';
break;
case 'FIRSTNAME_INVALID':
$message = 'The first name is invalid';
break;
case 'LASTNAME_INVALID':
$message = 'The last name is invalid';
break;
case 'PHONE_NUMBER_INVALID':
$message = 'The phone number is invalid';
break;
case 'PHONE_CODE_HASH_EMPTY':
$message = 'phone_code_hash is missing';
break;
case 'PHONE_CODE_EMPTY':
$message = 'phone_code is missing';
break;
case 'PHONE_CODE_EXPIRED':
$message = 'The confirmation code has expired';
break;
case 'API_ID_INVALID':
$message = 'The api_id/api_hash combination is invalid';
break;
case 'PHONE_NUMBER_OCCUPIED':
$message = 'The phone number is already in use';
break;
case 'PHONE_NUMBER_UNOCCUPIED':
$message = 'The phone number is not yet being used';
break;
case 'USERS_TOO_FEW':
$message = 'Not enough users (to create a chat, for example)';
break;
case 'USERS_TOO_MUCH':
$message = 'The maximum number of users has been exceeded (to create a chat, for example)';
break;
case 'TYPE_CONSTRUCTOR_INVALID':
$message = 'The type constructor is invalid';
break;
case 'FILE_PART_INVALID':
$message = 'The file part number is invalid';
break;
case 'FILE_PARTS_INVALID':
$message = 'The number of file parts is invalid';
break;
case 'MD5_CHECKSUM_INVALID':
$message = 'The MD5 checksums do not match';
break;
case 'PHOTO_INVALID_DIMENSIONS':
$message = 'The photo dimensions are invalid';
break;
case 'FIELD_NAME_INVALID':
$message = 'The field with the name FIELD_NAME is invalid';
break;
case 'FIELD_NAME_EMPTY':
$message = 'The field with the name FIELD_NAME is missing';
break;
case 'MSG_WAIT_FAILED':
$message = 'A waiting call returned an error';
break;
case 'USERNAME_NOT_OCCUPIED':
$message = 'The provided username is not occupied';
break;
case 'PHONE_NUMBER_BANNED':
$message = 'The provided phone number is banned from telegram';
break;
case 'AUTH_KEY_UNREGISTERED':
$message = 'The authorization key has expired';
break;
case 'INVITE_HASH_EXPIRED':
$message = 'The invite link has expired';
break;
case 'USER_DEACTIVATED':
$message = 'The user was deactivated';
break;
case 'USER_ALREADY_PARTICIPANT':
$message = 'The user is already in the group';
break;
case 'MESSAGE_ID_INVALID':
$message = 'The provided message id is invalid';
break;
case 'PEER_ID_INVALID':
$message = 'The provided peer id is invalid';
break;
case 'CHAT_ID_INVALID':
$message = 'The provided chat id is invalid';
break;
case 'MESSAGE_DELETE_FORBIDDEN':
$message = "You can't delete one of the messages you tried to delete, most likely because it is a service message.";
break;
case 'CHAT_ADMIN_REQUIRED':
$message = 'You must be an admin in this chat to do this';
break;
case -429:
case 'PEER_FLOOD': $message = 'Too many requests'; break;
case 'PEER_FLOOD':
$message = 'Too many requests';
break;
}
parent::__construct($message, $code, $previous);
$this->prettify_tl();
$additional = [];
foreach ($this->getTrace() as $level) {
if (isset($level['function']) && $level['function'] === 'method_call') {
@ -101,7 +178,6 @@ class RPCErrorException extends \Exception
if (!self::$rollbar) {
return;
}
if (in_array($this->rpc, ['CHANNEL_PRIVATE', -404, -429, 'USERNAME_NOT_OCCUPIED', 'ACCESS_TOKEN_INVALID', 'AUTH_KEY_UNREGISTERED', 'SESSION_PASSWORD_NEEDED', 'PHONE_NUMBER_UNOCCUPIED', 'PEER_ID_INVALID', 'CHAT_ID_INVALID', 'USERNAME_INVALID', 'CHAT_WRITE_FORBIDDEN', 'CHAT_ADMIN_REQUIRED', 'PEER_FLOOD'])) {
return;
}
@ -110,4 +186,4 @@ class RPCErrorException extends \Exception
}
$message === 'Telegram is having internal issues, please try again later.' ? \Rollbar\Rollbar::log(\Rollbar\Payload\Level::critical(), $message) : \Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, $additional);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class RSA
@ -17,50 +17,29 @@ class RSA
use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\Tools;
use \danog\Serializable;
public $e;
public $n;
public $fp;
public function __magic_construct($rsa_key)
{
//if ($this->unserialized($rsa_key)) return true;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['rsa_init']], Logger::ULTRA_VERBOSE);
$key = new \phpseclib\Crypt\RSA();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['loading_key']], Logger::ULTRA_VERBOSE);
$key->load($rsa_key);
$this->n = \phpseclib\Common\Functions\Objects::getVar($key, 'modulus');
$this->e = \phpseclib\Common\Functions\Objects::getVar($key, 'exponent');
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['computing_fingerprint']], Logger::ULTRA_VERBOSE);
$this->fp = substr(
sha1(
$this->serialize_object(
['type' => 'bytes'],
$this->n->toBytes(), 'key'
)
.
$this->serialize_object(
['type' => 'bytes'],
$this->e->toBytes(), 'key'
),
true
),
-8
);
$this->fp = substr(sha1($this->serialize_object(['type' => 'bytes'], $this->n->toBytes(), 'key') . $this->serialize_object(['type' => 'bytes'], $this->e->toBytes(), 'key'), true), -8);
return true;
}
public function __sleep()
{
return ['e', 'n', 'fp'];
}
public function encrypt($data)
{
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['rsa_encrypting']], Logger::VERBOSE);
return (new \phpseclib\Math\BigInteger($data, 256))->powMod($this->e, $this->n)->toBytes();
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,9 +10,8 @@ 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;
class ResponseException extends \Exception
{
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -21,14 +21,12 @@ trait AuthKeyHandler
{
protected $temp_requested_secret_chats = [];
protected $secret_chats = [];
public function accept_secret_chat($params)
{
//var_dump($params['id'],$this->secret_chat_status($params['id']));
if ($this->secret_chat_status($params['id']) !== 0) {
//var_dump($this->secret_chat_status($params['id']));
\danog\MadelineProto\Logger::log(["I've already accepted secret chat ".$params['id']]);
\danog\MadelineProto\Logger::log(["I've already accepted secret chat " . $params['id']]);
return false;
}
$dh_config = $this->get_dh_config();
@ -47,9 +45,8 @@ trait AuthKeyHandler
$this->method_call('messages.acceptEncryption', ['peer' => $params['id'], 'g_b' => $g_b->toBytes(), 'key_fingerprint' => $key['fingerprint']], ['datacenter' => $this->datacenter->curdc]);
$this->notify_layer($params['id']);
$this->handle_pending_updates();
\danog\MadelineProto\Logger::log(['Secret chat '.$params['id'].' accepted successfully!'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Secret chat ' . $params['id'] . ' accepted successfully!'], \danog\MadelineProto\Logger::NOTICE);
}
public function request_secret_chat($user)
{
$user = $this->get_info($user);
@ -57,7 +54,7 @@ trait AuthKeyHandler
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
}
$user = $user['InputUser'];
\danog\MadelineProto\Logger::log(['Creating secret chat with '.$user['user_id'].'...'], \danog\MadelineProto\Logger::VERBOSE);
\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);
@ -68,18 +65,14 @@ trait AuthKeyHandler
$this->temp_requested_secret_chats[$res['id']] = $a;
$this->handle_pending_updates();
$this->get_updates_difference();
\danog\MadelineProto\Logger::log(['Secret chat '.$res['id'].' requested successfully!'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Secret chat ' . $res['id'] . ' requested successfully!'], \danog\MadelineProto\Logger::NOTICE);
return $res['id'];
}
public function complete_secret_chat($params)
{
if ($this->secret_chat_status($params['id']) !== 1) {
//var_dump($this->secret_chat_status($params['id']));
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat '.$params['id']]);
\danog\MadelineProto\Logger::log(['Could not find and complete secret chat ' . $params['id']]);
return false;
}
$dh_config = $this->get_dh_config();
@ -91,7 +84,6 @@ trait AuthKeyHandler
//var_dump($key);
if ($key['fingerprint'] !== $params['key_fingerprint']) {
$this->discard_secret_chat($params['id']);
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
}
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
@ -99,22 +91,19 @@ trait AuthKeyHandler
$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, 'in_seq_no' => 0, 'out_seq_no' => 0, 'layer' => 8, 'ttl' => 0, 'ttr' => 100, 'updated' => time(), 'incoming' => [], 'outgoing' => [], 'created' => time(), 'rekeying' => [0], 'key_x' => 'to server', 'mtproto' => 1];
$this->notify_layer($params['id']);
$this->handle_pending_updates();
\danog\MadelineProto\Logger::log(['Secret chat '.$params['id'].' completed successfully!'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Secret chat ' . $params['id'] . ' completed successfully!'], \danog\MadelineProto\Logger::NOTICE);
}
public function notify_layer($chat)
{
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]);
}
protected $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);
\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);
@ -127,10 +116,8 @@ trait AuthKeyHandler
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionRequestKey', 'g_a' => $g_a->toBytes(), 'exchange_id' => $e]]], ['datacenter' => $this->datacenter->curdc]);
$this->handle_pending_updates();
$this->get_updates_difference();
return $e;
}
public function accept_rekey($chat, $params)
{
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
@ -142,11 +129,10 @@ trait AuthKeyHandler
}
if ($my_exchange_id === $other_exchange_id) {
$this->secret_chats[$chat]['rekeying'] = [0];
return;
}
}
\danog\MadelineProto\Logger::log(['Accepting rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
\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);
@ -154,7 +140,6 @@ trait AuthKeyHandler
$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;
@ -165,15 +150,13 @@ trait AuthKeyHandler
$this->handle_pending_updates();
$this->get_updates_difference();
}
public function commit_rekey($chat, $params)
{
if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) {
$this->secret_chats[$chat]['rekeying'] = [0];
return;
}
\danog\MadelineProto\Logger::log(['Committing rekeying of secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
\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']);
@ -183,7 +166,6 @@ trait AuthKeyHandler
$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]);
@ -193,11 +175,9 @@ trait AuthKeyHandler
$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 || !isset($this->temp_rekeyed_secret_chats['fingerprint'])) {
@ -205,10 +185,9 @@ trait AuthKeyHandler
}
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);
\danog\MadelineProto\Logger::log(['Completing rekeying of secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
$this->secret_chats[$chat]['rekeying'] = [0];
$this->secret_chats[$chat]['old_key'] = $this->secret_chats[$chat]['key'];
$this->secret_chats[$chat]['key'] = $this->temp_rekeyed_secret_chats[$chat];
@ -216,11 +195,9 @@ trait AuthKeyHandler
$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]);
\danog\MadelineProto\Logger::log(['Secret chat '.$chat.' rekeyed successfully!'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Secret chat ' . $chat . ' rekeyed successfully!'], \danog\MadelineProto\Logger::VERBOSE);
return true;
}
public function secret_chat_status($chat)
{
if (isset($this->secret_chats[$chat])) {
@ -229,18 +206,15 @@ trait AuthKeyHandler
if (isset($this->temp_requested_secret_chats[$chat])) {
return 1;
}
return 0;
}
public function get_secret_chat($chat)
{
return $this->secret_chats[$chat];
}
public function discard_secret_chat($chat)
{
\danog\MadelineProto\Logger::log(['Discarding secret chat '.$chat.'...'], \danog\MadelineProto\Logger::VERBOSE);
\danog\MadelineProto\Logger::log(['Discarding secret chat ' . $chat . '...'], \danog\MadelineProto\Logger::VERBOSE);
//var_dump(debug_backtrace(0)[0]);
if (isset($this->secret_chats[$chat])) {
unset($this->secret_chats[$chat]);
@ -251,7 +225,6 @@ trait AuthKeyHandler
if (isset($this->temp_rekeyed_secret_chats[$chat])) {
unset($this->temp_rekeyed_secret_chats[$chat]);
}
try {
$this->method_call('messages.discardEncryption', ['chat_id' => $chat], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -260,4 +233,4 @@ trait AuthKeyHandler
}
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -21,10 +21,8 @@ trait MessageHandler
{
if (!isset($this->secret_chats[$chat_id])) {
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id));
return false;
}
$this->secret_chats[$chat_id]['ttr']--;
if ($this->secret_chats[$chat_id]['layer'] > 8) {
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] === 0) {
@ -35,34 +33,27 @@ trait MessageHandler
}
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message;
$message = $this->serialize_object(['type' => $constructor = $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $constructor, $this->secret_chats[$chat_id]['layer']);
$message = $this->pack_unsigned_int(strlen($message)).$message;
$message = $this->pack_unsigned_int(strlen($message)) . $message;
if ($this->secret_chats[$chat_id]['mtproto'] === 2) {
$padding = $this->posmod(-strlen($message), 16);
if ($padding < 12) {
$padding += 16;
}
$message .= $this->random($padding);
$message_key = substr(hash('sha256', substr($this->secret_chats[$chat_id]['key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 0 : 8), 32).$message, true), 8, 16);
$message_key = substr(hash('sha256', substr($this->secret_chats[$chat_id]['key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 0 : 8), 32) . $message, true), 8, 16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], $this->secret_chats[$chat_id]['admin']);
} else {
$message_key = substr(sha1($message, true), -16);
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], true);
$message .= $this->random($this->posmod(-strlen($message), 16));
}
$message = $this->secret_chats[$chat_id]['key']['fingerprint'].$message_key.$this->ige_encrypt($message, $aes_key, $aes_iv);
$message = $this->secret_chats[$chat_id]['key']['fingerprint'] . $message_key . $this->ige_encrypt($message, $aes_key, $aes_iv);
return $message;
}
public function handle_encrypted_update($message, $test = false)
{
if (!isset($this->secret_chats[$message['message']['chat_id']])) {
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $message['message']['chat_id']));
return false;
}
$auth_key_id = substr($message['message']['bytes'], 0, 8);
@ -71,47 +62,39 @@ trait MessageHandler
if (isset($this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint'])) {
if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint']) {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
}
$old = true;
} else {
$this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
}
}
$message_key = substr($message['message']['bytes'], 8, 16);
$encrypted_data = substr($message['message']['bytes'], 24);
if ($this->secret_chats[$message['message']['chat_id']]['mtproto'] === 2) {
\danog\MadelineProto\Logger::log(['Trying MTProto v2 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Trying MTProto v2 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
try {
$message_data = $this->try_mtproto_v2_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['MTProto v2 decryption failed with message '.$e->getMessage().', trying MTProto v1 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption failed with message ' . $e->getMessage() . ', trying MTProto v1 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
$message_data = $this->try_mtproto_v1_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
$this->secret_chats[$message['message']['chat_id']]['mtproto'] = 1;
}
} else {
\danog\MadelineProto\Logger::log(['Trying MTProto v1 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Trying MTProto v1 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
try {
$message_data = $this->try_mtproto_v1_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
} catch (\danog\MadelineProto\SecurityException $e) {
\danog\MadelineProto\Logger::log(['MTProto v1 decryption failed with message '.$e->getMessage().', trying MTProto v2 decryption for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v1 decryption failed with message ' . $e->getMessage() . ', trying MTProto v2 decryption for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
$message_data = $this->try_mtproto_v2_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat '.$message['message']['chat_id'].'...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['MTProto v2 decryption OK for chat ' . $message['message']['chat_id'] . '...'], \danog\MadelineProto\Logger::NOTICE);
$this->secret_chats[$message['message']['chat_id']]['mtproto'] = 2;
}
}
$deserialized = $this->deserialize($message_data, ['type' => '']);
$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] === 0) {
@ -120,60 +103,49 @@ trait MessageHandler
unset($message['message']['bytes']);
$message['message']['decrypted_message'] = $deserialized;
$this->secret_chats[$message['message']['chat_id']]['incoming'][$this->secret_chats[$message['message']['chat_id']]['in_seq_no']] = $message['message'];
$this->handle_decrypted_update($message);
}
public function try_mtproto_v1_decrypt($message_key, $chat_id, $old, $encrypted_data)
{
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], true);
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
$message_data_length = unpack('V', substr($decrypted_data, 0, 4))[1];
$message_data = substr($decrypted_data, 4, $message_data_length);
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_data_length_too_big']);
}
if ($message_key != substr(sha1(substr($decrypted_data, 0, 4 + $message_data_length), true), -16)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_key_mismatch']);
}
if ((strlen($decrypted_data) - 4) - $message_data_length > 15) {
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 (strlen($decrypted_data) % 16 != 0) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
}
return $message_data;
}
public function try_mtproto_v2_decrypt($message_key, $chat_id, $old, $encrypted_data)
{
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], !$this->secret_chats[$chat_id]['admin']);
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
$message_data_length = unpack('V', substr($decrypted_data, 0, 4))[1];
$message_data = substr($decrypted_data, 4, $message_data_length);
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_data_length_too_big']);
}
if ($message_key != substr(hash('sha256', substr($this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 8 : 0), 32).$decrypted_data, true), 8, 16)) {
if ($message_key != substr(hash('sha256', substr($this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], 88 + ($this->secret_chats[$chat_id]['admin'] ? 8 : 0), 32) . $decrypted_data, true), 8, 16)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_key_mismatch']);
}
if ((strlen($decrypted_data) - 4) - $message_data_length < 12) {
if (strlen($decrypted_data) - 4 - $message_data_length < 12) {
throw new \danog\MadelineProto\SecurityException('padding is too small');
}
if ((strlen($decrypted_data) - 4) - $message_data_length > 1024) {
if (strlen($decrypted_data) - 4 - $message_data_length > 1024) {
throw new \danog\MadelineProto\SecurityException('padding is too big');
}
if (strlen($decrypted_data) % 16 != 0) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
}
return $message_data;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -20,63 +20,51 @@ trait ResponseHandler
public function handle_decrypted_update($update)
{
/*if (isset($update['message']['decrypted_message']['random_bytes']) && strlen($update['message']['decrypted_message']['random_bytes']) < 15) {
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['rand_bytes_too_short']);
}*/ // already checked in TL.php
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['rand_bytes_too_short']);
}*/
// already checked in TL.php
switch ($update['message']['decrypted_message']['_']) {
case 'decryptedMessageService':
switch ($update['message']['decrypted_message']['action']['_']) {
case 'decryptedMessageActionRequestKey':
$this->accept_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionAcceptKey':
$this->commit_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionCommitKey':
$this->complete_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionNotifyLayer':
$this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['action']['layer'];
if ($update['message']['decrypted_message']['action']['layer'] >= 17 && time() - $this->secret_chats[$update['message']['chat_id']]['created'] > 15) {
$this->notify_layer($update['message']['chat_id']);
}
if ($update['message']['decrypted_message']['action']['layer'] >= 73) {
$this->secret_chats[$update['message']['chat_id']]['mtproto'] = 2;
}
return;
case 'decryptedMessageActionSetMessageTTL':
$this->secret_chats[$update['message']['chat_id']]['ttl'] = $update['message']['decrypted_message']['action']['ttl_seconds'];
return;
case 'decryptedMessageActionNoop':
return;
case 'decryptedMessageActionResend':
$update['message']['decrypted_message']['action']['start_seq_no'] -= $this->secret_chats[$update['message']['chat_id']]['out_seq_no_x'];
$update['message']['decrypted_message']['action']['end_seq_no'] -= $this->secret_chats[$update['message']['chat_id']]['out_seq_no_x'];
$update['message']['decrypted_message']['action']['start_seq_no'] /= 2;
$update['message']['decrypted_message']['action']['end_seq_no'] /= 2;
\danog\MadelineProto\Logger::log(['Resending messages for secret chat '.$update['message']['chat_id']], \danog\MadelineProto\Logger::WARNING);
foreach ($this->secret_chats[$update['message']['chat_id']]['outgoing'] as $seq => $message) {
if ($seq >= $update['message']['decrypted_message']['action']['start_seq_no'] && $seq <= $update['message']['decrypted_message']['action']['end_seq_no']) {
//throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['resending_unsupported']);
$this->method_call('messages.sendEncrypted', ['peer' => $update['message']['chat_id'], 'message' => $update['message']['decrypted_message']], ['datacenter' => $this->datacenter->curdc]);
}
}
return;
default:
// $this->save_update(['_' => 'updateNewDecryptedMessage', 'peer' => $this->secret_chats[$update['message']['chat_id']]['InputEncryptedChat'], 'in_seq_no' => $this->get_in_seq_no($update['message']['chat_id']), 'out_seq_no' => $this->get_out_seq_no($update['message']['chat_id']), 'message' => $update['message']['decrypted_message']]);
$this->save_update($update);
switch ($update['message']['decrypted_message']['action']['_']) {
case 'decryptedMessageActionRequestKey':
$this->accept_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionAcceptKey':
$this->commit_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionCommitKey':
$this->complete_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return;
case 'decryptedMessageActionNotifyLayer':
$this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['action']['layer'];
if ($update['message']['decrypted_message']['action']['layer'] >= 17 && time() - $this->secret_chats[$update['message']['chat_id']]['created'] > 15) {
$this->notify_layer($update['message']['chat_id']);
}
if ($update['message']['decrypted_message']['action']['layer'] >= 73) {
$this->secret_chats[$update['message']['chat_id']]['mtproto'] = 2;
}
return;
case 'decryptedMessageActionSetMessageTTL':
$this->secret_chats[$update['message']['chat_id']]['ttl'] = $update['message']['decrypted_message']['action']['ttl_seconds'];
return;
case 'decryptedMessageActionNoop':
return;
case 'decryptedMessageActionResend':
$update['message']['decrypted_message']['action']['start_seq_no'] -= $this->secret_chats[$update['message']['chat_id']]['out_seq_no_x'];
$update['message']['decrypted_message']['action']['end_seq_no'] -= $this->secret_chats[$update['message']['chat_id']]['out_seq_no_x'];
$update['message']['decrypted_message']['action']['start_seq_no'] /= 2;
$update['message']['decrypted_message']['action']['end_seq_no'] /= 2;
\danog\MadelineProto\Logger::log(['Resending messages for secret chat ' . $update['message']['chat_id']], \danog\MadelineProto\Logger::WARNING);
foreach ($this->secret_chats[$update['message']['chat_id']]['outgoing'] as $seq => $message) {
if ($seq >= $update['message']['decrypted_message']['action']['start_seq_no'] && $seq <= $update['message']['decrypted_message']['action']['end_seq_no']) {
//throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['resending_unsupported']);
$this->method_call('messages.sendEncrypted', ['peer' => $update['message']['chat_id'], 'message' => $update['message']['decrypted_message']], ['datacenter' => $this->datacenter->curdc]);
}
}
return;
default:
// $this->save_update(['_' => 'updateNewDecryptedMessage', 'peer' => $this->secret_chats[$update['message']['chat_id']]['InputEncryptedChat'], 'in_seq_no' => $this->get_in_seq_no($update['message']['chat_id']), 'out_seq_no' => $this->get_out_seq_no($update['message']['chat_id']), 'message' => $update['message']['decrypted_message']]);
$this->save_update($update);
}
break;
case 'decryptedMessage':
@ -96,8 +84,8 @@ trait ResponseHandler
}
break;
default:
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['unrecognized_dec_msg'].var_export($update, true));
throw new \danog\MadelineProto\ResponseException(\danog\MadelineProto\Lang::$current_lang['unrecognized_dec_msg'] . var_export($update, true));
break;
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -25,7 +25,6 @@ trait SeqNoHandler
if (isset($message['decrypted_message']['in_seq_no'])) {
if (($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2 < $last) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is not increasing');
}
$last = ($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
@ -33,13 +32,10 @@ trait SeqNoHandler
}
if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is too big');
}
return true;
}
public function check_secret_out_seq_no($chat_id, $seqno)
{
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
@ -48,35 +44,31 @@ trait SeqNoHandler
if (isset($message['decrypted_message']['out_seq_no']) && $C < $this->secret_chats[$chat_id]['in_seq_no']) {
if (($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2 !== $C) {
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be '.$C.', is '.(($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2));
throw new \danog\MadelineProto\SecurityException('out_seq_no hole: should be ' . $C . ', is ' . ($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2);
} else {
$C++;
}
}
}
//var_dump($C, $seqno);
if ($seqno < $C) { // <= C
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno '.$seqno]);
if ($seqno < $C) {
// <= C
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno ' . $seqno]);
return false;
}
if ($seqno > $C) { // > C+1
if ($seqno > $C) {
// > C+1
$this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected ('.$seqno.' > '.$C.')!');
throw new \danog\MadelineProto\SecurityException('WARNING: out_seq_no gap detected (' . $seqno . ' > ' . $C . ')!');
}
return true;
}
public function generate_secret_in_seq_no($chat)
{
return $this->secret_chats[$chat]['layer'] > 8 ? ($this->secret_chats[$chat]['in_seq_no'] * 2) + $this->secret_chats[$chat]['in_seq_no_x'] : -1;
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['in_seq_no'] * 2 + $this->secret_chats[$chat]['in_seq_no_x'] : -1;
}
public function generate_secret_out_seq_no($chat)
{
return $this->secret_chats[$chat]['layer'] > 8 ? ($this->secret_chats[$chat]['out_seq_no'] * 2) + $this->secret_chats[$chat]['out_seq_no_x'] : -1;
return $this->secret_chats[$chat]['layer'] > 8 ? $this->secret_chats[$chat]['out_seq_no'] * 2 + $this->secret_chats[$chat]['out_seq_no_x'] : -1;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,9 +10,8 @@ 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;
class SecurityException extends \Exception
{
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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;
/**
@ -18,11 +18,9 @@ namespace danog\MadelineProto;
class Serialization
{
public static $instances = [];
public static function serialize_all($exception)
{
echo $exception.PHP_EOL;
echo $exception . PHP_EOL;
return;
foreach (self::$instances as $instance) {
if (isset($instance->session)) {
@ -30,16 +28,13 @@ class Serialization
}
}
}
public static function realpaths($file)
{
if ($file[0] !== '/') {
$file = getcwd().'/'.$file;
$file = getcwd() . '/' . $file;
}
return ['file' => $file, 'lockfile' => $file.'.lock', 'tempfile' => $file.'.temp.session'];
return ['file' => $file, 'lockfile' => $file . '.lock', 'tempfile' => $file . '.temp.session'];
}
/**
* Serialize API class.
*
@ -64,7 +59,6 @@ class Serialization
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w');
\danog\MadelineProto\Logger::log(['Waiting for exclusive lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_EX);
try {
$wrote = file_put_contents($realpaths['tempfile'], serialize($instance));
rename($realpaths['tempfile'], $realpaths['file']);
@ -72,10 +66,8 @@ class Serialization
flock($realpaths['lockfile'], LOCK_UN);
fclose($realpaths['lockfile']);
}
return $wrote;
}
/**
* Deserialize API class.
*
@ -94,32 +86,28 @@ class Serialization
clearstatcache();
}
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
\danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_SH);
try {
$unserialized = file_get_contents($realpaths['file']);
} finally {
flock($realpaths['lockfile'], LOCK_UN);
fclose($realpaths['lockfile']);
}
$tounserialize = str_replace('O:26:"danog\MadelineProto\Button":', 'O:35:"danog\MadelineProto\TL\Types\Button":', $unserialized);
foreach (['RSA', 'TL\TLMethod', 'TL\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\Types\Button', 'TL\Types\Bytes', 'APIFactory'] as $class) {
class_exists('\danog\MadelineProto\\'.$class);
$tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $unserialized);
foreach (['RSA', 'TL\\TLMethod', 'TL\\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection', 'TL\\Types\\Button', 'TL\\Types\\Bytes', 'APIFactory'] as $class) {
class_exists('\\danog\\MadelineProto\\' . $class);
}
class_exists('\Volatile');
class_exists('\\Volatile');
\danog\MadelineProto\Logger::class_exists();
try {
$unserialized = unserialize($tounserialize);
} catch (\danog\MadelineProto\Bug74586Exception $e) {
$unserialized = \danog\Serialization::unserialize($tounserialize);
} catch (\danog\MadelineProto\Exception $e) {
Logger::log([(string) $e], Logger::ERROR);
if (strpos($e->getMessage(), "Erroneous data format for unserializing 'phpseclib\Math\BigInteger'") === 0) {
$tounserialize = str_replace('phpseclib\Math\BigInteger', 'phpseclib\Math\BigIntegor', $unserialized);
if (strpos($e->getMessage(), "Erroneous data format for unserializing 'phpseclib\\Math\\BigInteger'") === 0) {
$tounserialize = str_replace('phpseclib\\Math\\BigInteger', 'phpseclib\\Math\\BigIntegor', $unserialized);
}
$unserialized = \danog\Serialization::unserialize($tounserialize);
}
@ -136,7 +124,6 @@ class Serialization
$unserialized->session = $filename;
}
self::$instances[spl_object_hash($unserialized)] = $unserialized;
return $unserialized;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL\Conversion;
trait BotAPI
@ -18,7 +18,6 @@ trait BotAPI
{
return html_entity_decode(preg_replace('#< *br */? *>#', "\n", $stuff));
}
public function parse_buttons($rows)
{
$newrows = [];
@ -54,10 +53,8 @@ trait BotAPI
}
$key++;
}
return $newrows;
}
public function parse_reply_markup($markup)
{
if (isset($markup['force_reply']) && $markup['force_reply']) {
@ -86,10 +83,8 @@ trait BotAPI
$markup['rows'] = $this->parse_buttons($markup['inline_keyboard']);
unset($markup['inline_keyboard']);
}
return $markup;
}
public function MTProto_to_botAPI($data, $sent_arguments = [])
{
$newd = [];
@ -97,253 +92,206 @@ trait BotAPI
foreach ($data as $key => $element) {
$newd[$key] = $this->MTProto_to_botAPI($element, $sent_arguments);
}
return $newd;
}
switch ($data['_']) {
case 'updateShortSentMessage':
$newd['message_id'] = $data['id'];
$newd['date'] = $data['date'];
$newd['text'] = $sent_arguments['message'];
if ($data['out']) {
$newd['from'] = $this->get_pwr_chat($this->authorization['user']);
}
$newd['chat'] = $this->get_pwr_chat($sent_arguments['peer']);
if (isset($data['entities'])) {
$newd['entities'] = $this->MTProto_to_botAPI($data['entities'], $sent_arguments);
}
if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
}
return $newd;
$newd['message_id'] = $data['id'];
$newd['date'] = $data['date'];
$newd['text'] = $sent_arguments['message'];
if ($data['out']) {
$newd['from'] = $this->get_pwr_chat($this->authorization['user']);
}
$newd['chat'] = $this->get_pwr_chat($sent_arguments['peer']);
if (isset($data['entities'])) {
$newd['entities'] = $this->MTProto_to_botAPI($data['entities'], $sent_arguments);
}
if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
}
return $newd;
case 'updateNewChannelMessage':
case 'updateNewMessage':
return $this->MTProto_to_botAPI($data['message']);
return $this->MTProto_to_botAPI($data['message']);
case 'message':
$newd['message_id'] = $data['id'];
$newd['date'] = $data['date'];
$newd['text'] = $data['message'];
$newd['post'] = $data['post'];
$newd['silent'] = $data['silent'];
if (isset($data['from_id'])) {
$newd['from'] = $this->get_pwr_chat($data['from_id']);
}
$newd['chat'] = $this->get_pwr_chat($data['to_id']);
if (isset($data['entities'])) {
$newd['entities'] = $this->MTProto_to_botAPI($data['entities'], $sent_arguments);
}
if (isset($data['views'])) {
$newd['views'] = $data['views'];
}
if (isset($data['edit_date'])) {
$newd['edit_date'] = $data['edit_date'];
}
if (isset($data['via_bot_id'])) {
$newd['via_bot'] = $this->get_pwr_chat($data['via_bot_id']);
}
if (isset($data['fwd_from']['from_id'])) {
$newd['froward_from'] = $this->get_pwr_chat($data['fwd_from']['from_id']);
}
if (isset($data['fwd_from']['channel_id'])) {
$newd['forward_from_chat'] = $this->get_pwr_chat($data['fwd_from']['channel_id']);
}
if (isset($data['fwd_from']['date'])) {
$newd['forward_date'] = $data['fwd_from']['date'];
}
if (isset($data['fwd_from']['channel_post'])) {
$newd['forward_from_message_id'] = $data['fwd_from']['channel_post'];
}
if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
}
return $newd;
$newd['message_id'] = $data['id'];
$newd['date'] = $data['date'];
$newd['text'] = $data['message'];
$newd['post'] = $data['post'];
$newd['silent'] = $data['silent'];
if (isset($data['from_id'])) {
$newd['from'] = $this->get_pwr_chat($data['from_id']);
}
$newd['chat'] = $this->get_pwr_chat($data['to_id']);
if (isset($data['entities'])) {
$newd['entities'] = $this->MTProto_to_botAPI($data['entities'], $sent_arguments);
}
if (isset($data['views'])) {
$newd['views'] = $data['views'];
}
if (isset($data['edit_date'])) {
$newd['edit_date'] = $data['edit_date'];
}
if (isset($data['via_bot_id'])) {
$newd['via_bot'] = $this->get_pwr_chat($data['via_bot_id']);
}
if (isset($data['fwd_from']['from_id'])) {
$newd['froward_from'] = $this->get_pwr_chat($data['fwd_from']['from_id']);
}
if (isset($data['fwd_from']['channel_id'])) {
$newd['forward_from_chat'] = $this->get_pwr_chat($data['fwd_from']['channel_id']);
}
if (isset($data['fwd_from']['date'])) {
$newd['forward_date'] = $data['fwd_from']['date'];
}
if (isset($data['fwd_from']['channel_post'])) {
$newd['forward_from_message_id'] = $data['fwd_from']['channel_post'];
}
if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
}
return $newd;
case 'messageEntityMention':
unset($data['_']);
$data['type'] = 'mention';
return $data;
unset($data['_']);
$data['type'] = 'mention';
return $data;
case 'messageEntityHashtag':
unset($data['_']);
$data['type'] = 'hashtag';
return $data;
unset($data['_']);
$data['type'] = 'hashtag';
return $data;
case 'messageEntityBotCommand':
unset($data['_']);
$data['type'] = 'bot_command';
return $data;
unset($data['_']);
$data['type'] = 'bot_command';
return $data;
case 'messageEntityUrl':
unset($data['_']);
$data['type'] = 'url';
return $data;
unset($data['_']);
$data['type'] = 'url';
return $data;
case 'messageEntityEmail':
unset($data['_']);
$data['type'] = 'email';
return $data;
unset($data['_']);
$data['type'] = 'email';
return $data;
case 'messageEntityBold':
unset($data['_']);
$data['type'] = 'bold';
return $data;
unset($data['_']);
$data['type'] = 'bold';
return $data;
case 'messageEntityItalic':
unset($data['_']);
$data['type'] = 'italic';
return $data;
unset($data['_']);
$data['type'] = 'italic';
return $data;
case 'messageEntityCode':
unset($data['_']);
$data['type'] = 'code';
return $data;
unset($data['_']);
$data['type'] = 'code';
return $data;
case 'messageEntityPre':
unset($data['_']);
$data['type'] = 'pre';
return $data;
unset($data['_']);
$data['type'] = 'pre';
return $data;
case 'messageEntityTextUrl':
unset($data['_']);
$data['type'] = 'text_url';
return $data;
unset($data['_']);
$data['type'] = 'text_url';
return $data;
case 'messageEntityMentionName':
unset($data['_']);
$data['type'] = 'text_mention';
$data['user'] = $this->get_pwr_chat($data['user_id']);
unset($data['user_id']);
return $data;
unset($data['_']);
$data['type'] = 'text_mention';
$data['user'] = $this->get_pwr_chat($data['user_id']);
unset($data['user_id']);
return $data;
case 'messageMediaPhoto':
if (isset($data['caption'])) {
$res['caption'] = $data['caption'];
}
$res['photo'] = [];
foreach ($data['photo']['sizes'] as $key => $photo) {
$res['photo'][$key] = $this->photosize_to_botapi($photo, $data['photo']);
}
return $res;
if (isset($data['caption'])) {
$res['caption'] = $data['caption'];
}
$res['photo'] = [];
foreach ($data['photo']['sizes'] as $key => $photo) {
$res['photo'][$key] = $this->photosize_to_botapi($photo, $data['photo']);
}
return $res;
case 'messageMediaEmpty':
return [];
return [];
case 'messageMediaDocument':
$type_name = 'document';
$res = [];
if ($data['document']['thumb']['_'] === 'photoSize') {
$res['thumb'] = $this->photosize_to_botapi($data['document']['thumb'], [], true);
}
foreach ($data['document']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
$res['ext'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
$res['file_name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
$type_name = 'audio';
if ($attribute['voice']) {
$type_name = 'voice';
}
$res['duration'] = $attribute['duration'];
if (isset($attribute['performer'])) {
$res['performer'] = $attribute['performer'];
}
if (isset($attribute['title'])) {
$res['title'] = $attribute['title'];
}
if (isset($attribute['waveform'])) {
$res['title'] = $attribute['waveform'];
}
break;
case 'documentAttributeVideo':
$type_name = $attribute['round_message'] ? 'video_note' : 'video';
$res['width'] = $attribute['w'];
$res['height'] = $attribute['h'];
$res['duration'] = $attribute['duration'];
break;
case 'documentAttributeImageSize':
$res['width'] = $attribute['w'];
$res['height'] = $attribute['h'];
break;
case 'documentAttributeAnimated':
$type_name = 'gif';
$res['animated'] = true;
break;
case 'documentAttributeHasStickers':
$res['has_stickers'] = true;
break;
case 'documentAttributeSticker':
$type_name = 'sticker';
$res['mask'] = $attribute['mask'];
$res['emoji'] = $attribute['alt'];
$res['sticker_set'] = $attribute['stickerset'];
if (isset($attribute['mask_coords'])) {
$res['mask_coords'] = $attribute['mask_coords'];
}
break;
$type_name = 'document';
$res = [];
if ($data['document']['thumb']['_'] === 'photoSize') {
$res['thumb'] = $this->photosize_to_botapi($data['document']['thumb'], [], true);
}
}
if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
$res['file_name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['file_name'] .= ' - '.$audio['performer'];
foreach ($data['document']['attributes'] as $attribute) {
switch ($attribute['_']) {
case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']);
$res['ext'] = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : '';
$res['file_name'] = $pathinfo['filename'];
break;
case 'documentAttributeAudio':
$audio = $attribute;
$type_name = 'audio';
if ($attribute['voice']) {
$type_name = 'voice';
}
$res['duration'] = $attribute['duration'];
if (isset($attribute['performer'])) {
$res['performer'] = $attribute['performer'];
}
if (isset($attribute['title'])) {
$res['title'] = $attribute['title'];
}
if (isset($attribute['waveform'])) {
$res['title'] = $attribute['waveform'];
}
break;
case 'documentAttributeVideo':
$type_name = $attribute['round_message'] ? 'video_note' : 'video';
$res['width'] = $attribute['w'];
$res['height'] = $attribute['h'];
$res['duration'] = $attribute['duration'];
break;
case 'documentAttributeImageSize':
$res['width'] = $attribute['w'];
$res['height'] = $attribute['h'];
break;
case 'documentAttributeAnimated':
$type_name = 'gif';
$res['animated'] = true;
break;
case 'documentAttributeHasStickers':
$res['has_stickers'] = true;
break;
case 'documentAttributeSticker':
$type_name = 'sticker';
$res['mask'] = $attribute['mask'];
$res['emoji'] = $attribute['alt'];
$res['sticker_set'] = $attribute['stickerset'];
if (isset($attribute['mask_coords'])) {
$res['mask_coords'] = $attribute['mask_coords'];
}
break;
}
}
}
if (!isset($res['file_name'])) {
$res['file_name'] = $data['document']['access_hash'];
}
$res['file_name'] .= '_'.$data['document']['id'];
if (isset($res['ext'])) {
$res['file_name'] .= $res['ext'];
unset($res['ext']);
} else {
$res['file_name'] .= $this->get_extension_from_mime($data['document']['mime_type']);
}
$data['document']['_'] = 'bot_'.$type_name;
$res['file_size'] = $data['document']['size'];
$res['mime_type'] = $data['document']['mime_type'];
$res['file_id'] = $this->base64url_encode($this->rle_encode($this->serialize_object(['type' => 'File'], $data['document'], 'File').chr(2)));
return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : ''];
if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
$res['file_name'] = $audio['title'];
if (isset($audio['performer'])) {
$res['file_name'] .= ' - ' . $audio['performer'];
}
}
if (!isset($res['file_name'])) {
$res['file_name'] = $data['document']['access_hash'];
}
$res['file_name'] .= '_' . $data['document']['id'];
if (isset($res['ext'])) {
$res['file_name'] .= $res['ext'];
unset($res['ext']);
} else {
$res['file_name'] .= $this->get_extension_from_mime($data['document']['mime_type']);
}
$data['document']['_'] = 'bot_' . $type_name;
$res['file_size'] = $data['document']['size'];
$res['mime_type'] = $data['document']['mime_type'];
$res['file_id'] = $this->base64url_encode($this->rle_encode($this->serialize_object(['type' => 'File'], $data['document'], 'File') . chr(2)));
return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : ''];
default:
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['botapi_conversion_error'], $data['_']));
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['botapi_conversion_error'], $data['_']));
}
}
public $botapi_params = [
'disable_web_page_preview' => 'no_webpage',
'disable_notification' => 'silent',
'reply_to_message_id' => 'reply_to_msg_id',
'chat_id' => 'peer',
'text' => 'message',
];
public $botapi_params = ['disable_web_page_preview' => 'no_webpage', 'disable_notification' => 'silent', 'reply_to_message_id' => 'reply_to_msg_id', 'chat_id' => 'peer', 'text' => 'message'];
public function botAPI_to_MTProto($arguments)
{
foreach ($this->botapi_params as $bot => $mtproto) {
@ -358,83 +306,74 @@ trait BotAPI
if (isset($arguments['parse_mode'])) {
$arguments = $this->parse_mode($arguments);
}
return $arguments;
}
public function parse_node($node, &$entities, &$nmessage, $recursive = true)
{
switch ($node->nodeName) {
case 'br':
$nmessage .= "\n";
break;
$nmessage .= "\n";
break;
case 'b':
case 'strong':
$text = $this->html_entity_decode($node->textContent);
$entities[] = ['_' => 'messageEntityBold', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text)];
$nmessage .= $text;
break;
$text = $this->html_entity_decode($node->textContent);
$entities[] = ['_' => 'messageEntityBold', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text)];
$nmessage .= $text;
break;
case 'i':
case 'em':
$text = $this->html_entity_decode($node->textContent);
$entities[] = ['_' => 'messageEntityItalic', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text)];
$nmessage .= $text;
break;
$text = $this->html_entity_decode($node->textContent);
$entities[] = ['_' => 'messageEntityItalic', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text)];
$nmessage .= $text;
break;
case 'code':
$text = $this->html_entity_decode($node->textContent);
$entities[] = ['_' => 'messageEntityCode', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text)];
$nmessage .= $text;
break;
$text = $this->html_entity_decode($node->textContent);
$entities[] = ['_' => 'messageEntityCode', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text)];
$nmessage .= $text;
break;
case 'pre':
$text = $this->html_entity_decode($node->textContent);
$language = $node->getAttribute('language');
if ($language === null) {
$language = '';
}
$entities[] = ['_' => 'messageEntityPre', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'language' => $language];
$nmessage .= $text;
break;
$text = $this->html_entity_decode($node->textContent);
$language = $node->getAttribute('language');
if ($language === null) {
$language = '';
}
$entities[] = ['_' => 'messageEntityPre', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'language' => $language];
$nmessage .= $text;
break;
case 'p':
foreach ($node->childNodes as $node) {
$this->parse_node($node, $entities, $nmessage);
}
break;
case 'a':
$text = $this->html_entity_decode($node->textContent);
$href = $node->getAttribute('href');
if (preg_match('|mention:|', $href)) {
$mention = $this->get_info(str_replace('mention:', '', $href));
if (!isset($mention['InputUser'])) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['peer_not_in_db']);
}
$entities[] = ['_' => 'inputMessageEntityMentionName', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'user_id' => $mention['InputUser']];
} elseif (preg_match('|buttonurl:|', $href)) {
if (!isset($entities['buttons'])) {
$entities['buttons'] = [];
}
if (preg_match('|:new|', substr($href, -4))) {
$entities['buttons'][] = ['_' => 'keyboardButtonUrl', 'text' => $text, 'url' => str_replace('buttonurl:', '', str_replace(':new', '', $href)), 'new' => true];
} else {
$entities['buttons'][] = ['_' => 'keyboardButtonUrl', 'text' => $text, 'url' => str_replace('buttonurl:', '', $href)];
foreach ($node->childNodes as $node) {
$this->parse_node($node, $entities, $nmessage);
}
break;
} else {
$entities[] = ['_' => 'messageEntityTextUrl', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'url' => $href];
}
$nmessage .= $text;
break;
case 'a':
$text = $this->html_entity_decode($node->textContent);
$href = $node->getAttribute('href');
if (preg_match('|mention:|', $href)) {
$mention = $this->get_info(str_replace('mention:', '', $href));
if (!isset($mention['InputUser'])) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['peer_not_in_db']);
}
$entities[] = ['_' => 'inputMessageEntityMentionName', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'user_id' => $mention['InputUser']];
} elseif (preg_match('|buttonurl:|', $href)) {
if (!isset($entities['buttons'])) {
$entities['buttons'] = [];
}
if (preg_match('|:new|', substr($href, -4))) {
$entities['buttons'][] = ['_' => 'keyboardButtonUrl', 'text' => $text, 'url' => str_replace('buttonurl:', '', str_replace(':new', '', $href)), 'new' => true];
} else {
$entities['buttons'][] = ['_' => 'keyboardButtonUrl', 'text' => $text, 'url' => str_replace('buttonurl:', '', $href)];
}
break;
} else {
$entities[] = ['_' => 'messageEntityTextUrl', 'offset' => mb_strlen($nmessage), 'length' => mb_strlen($text), 'url' => $href];
}
$nmessage .= $text;
break;
default:
$nmessage .= $this->html_entity_decode($node->nodeValue);
break;
$nmessage .= $this->html_entity_decode($node->nodeValue);
break;
}
}
public function parse_mode($arguments)
{
if (isset($arguments['parse_mode']['_'])) {
@ -446,7 +385,6 @@ trait BotAPI
}
if (preg_match('/html/i', $arguments['parse_mode'])) {
$nmessage = '';
try {
$arguments['message'] = $this->html_fixtags($arguments['message']);
$dom = new \DOMDocument();
@ -467,10 +405,8 @@ trait BotAPI
}
$arguments['message'] = $nmessage;
}
return $arguments;
}
public function split_to_chunks($text)
{
$max_length = 4096;
@ -487,17 +423,15 @@ trait BotAPI
$i = 0;
$message = [''];
foreach ($text_arr as $word) {
if (strlen($message[$i].$word) <= $max_length) {
if (strlen($message[$i] . $word) <= $max_length) {
$message[$i] .= $word;
} else {
$i++;
$message[$i] = $word;
}
}
return $message;
}
public function multipleExplodeKeepDelimiters($delimiters, $string)
{
$initialArray = explode(chr(1), str_replace($delimiters, chr(1), $string));
@ -506,17 +440,15 @@ trait BotAPI
foreach ($initialArray as $item) {
$delimOffset += strlen($item);
if (strlen($item) > 0) {
$finalArray[] = $item.($delimOffset < strlen($string) ? $string[$delimOffset] : '');
$finalArray[] = $item . ($delimOffset < strlen($string) ? $string[$delimOffset] : '');
}
$delimOffset++;
}
return $finalArray;
}
public function html_fixtags($text)
{
preg_match_all("#(.*?)(<(a|b|strong|em|i|code|pre)[^>]*>)([^<]*?)(<\/\\3>)(.*)?#is", $text, $matches, PREG_SET_ORDER);
preg_match_all("#(.*?)(<(a|b|strong|em|i|code|pre)[^>]*>)([^<]*?)(<\\/\\3>)(.*)?#is", $text, $matches, PREG_SET_ORDER);
if ($matches) {
$last = count($matches) - 1;
foreach ($matches as $val) {
@ -528,17 +460,15 @@ trait BotAPI
$text = str_replace($val[6], $this->html_fixtags($val[6]), $text);
}
}
preg_match_all("#<a href=\x22(.+?)\x22>#is", $text, $matches);
preg_match_all("#<a href=\"(.+?)\">#is", $text, $matches);
foreach ($matches[1] as $match) {
$text = str_replace($match, htmlentities($match), $text);
}
return $text;
} else {
return htmlentities($text);
}
}
public function build_rows($button_list)
{
$end = false;
@ -559,12 +489,10 @@ trait BotAPI
$end = true;
}
}
if ($end) {
$row = ['_' => 'keyboardButtonRow', 'buttons' => $buttons];
$rows[] = $row;
}
return ['_' => 'replyInlineMarkup', 'rows' => $rows];
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL\Conversion;
trait BotAPIFiles
@ -18,12 +18,10 @@ trait BotAPIFiles
{
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
public function base64url_encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
public function rle_decode($string)
{
$new = '';
@ -38,11 +36,9 @@ trait BotAPIFiles
$last = $cur;
}
}
$string = $new.$last;
$string = $new . $last;
return $string;
}
public function rle_encode($string)
{
$new = '';
@ -53,34 +49,23 @@ trait BotAPIFiles
$count++;
} else {
if ($count > 0) {
$new .= $null.chr($count);
$new .= $null . chr($count);
$count = 0;
}
$new .= $cur;
}
}
return $new;
}
public function photosize_to_botapi($photo, $message_media, $thumbnail = false)
{
$ext = $this->get_extension_from_location(['_' => 'inputFileLocation', 'volume_id' => $photo['location']['volume_id'], 'local_id' => $photo['location']['local_id'], 'secret' => $photo['location']['secret'], 'dc_id' => $photo['location']['dc_id']], '.jpg');
$photo['location']['access_hash'] = isset($message_media['access_hash']) ? $message_media['access_hash'] : 0;
$photo['location']['id'] = isset($message_media['id']) ? $message_media['id'] : 0;
$photo['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo';
$data = $this->serialize_object(['type' => 'File'], $photo['location'], 'File').chr(2);
return [
'file_id' => $this->base64url_encode($this->rle_encode($data)),
'width' => $photo['w'],
'height' => $photo['h'],
'file_size' => isset($photo['size']) ? $photo['size'] : strlen($photo['bytes']),
'mime_type' => 'image/jpeg',
'file_name' => $photo['location']['volume_id'].'_'.$photo['location']['local_id'].$ext,
];
$data = $this->serialize_object(['type' => 'File'], $photo['location'], 'File') . chr(2);
return ['file_id' => $this->base64url_encode($this->rle_encode($data)), 'width' => $photo['w'], 'height' => $photo['h'], 'file_size' => isset($photo['size']) ? $photo['size'] : strlen($photo['bytes']), 'mime_type' => 'image/jpeg', 'file_name' => $photo['location']['volume_id'] . '_' . $photo['location']['local_id'] . $ext];
}
public function unpack_file_id($file_id)
{
$file_id = $this->rle_decode($this->base64url_decode($file_id));
@ -92,72 +77,52 @@ trait BotAPIFiles
switch ($deserialized['_']) {
case 'bot_thumbnail':
case 'bot_photo':
$constructor = ['_' => 'photo', 'sizes' => []];
$constructor['id'] = $deserialized['id'];
$constructor['access_hash'] = $deserialized['access_hash'];
unset($deserialized['id']);
unset($deserialized['access_hash']);
unset($deserialized['_']);
$constructor['sizes'][0]['location'] = $deserialized;
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => ''];
return $res;
$constructor = ['_' => 'photo', 'sizes' => []];
$constructor['id'] = $deserialized['id'];
$constructor['access_hash'] = $deserialized['access_hash'];
unset($deserialized['id']);
unset($deserialized['access_hash']);
unset($deserialized['_']);
$constructor['sizes'][0]['location'] = $deserialized;
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => ''];
return $res;
case 'bot_voice':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_video':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_video_note':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_document':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => []]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => []]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_sticker':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeSticker']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeSticker']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_gif':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAnimated']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAnimated']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
case 'bot_audio':
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res;
default:
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['file_type_invalid'], $type));
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['file_type_invalid'], $type));
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,9 +10,8 @@ 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\TL\Conversion;
class Exception extends \Exception
{
}
}

View File

@ -11,7 +11,6 @@ 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\TL\Conversion;
/**
@ -23,13 +22,11 @@ trait Extension
{
foreach (self::ALL_MIMES as $key => $value) {
if (array_search($mime, (array) $value) !== false) {
return '.'.$key;
return '.' . $key;
}
}
return '';
}
public function get_extension_from_location($location, $default)
{
return $default;
@ -38,15 +35,24 @@ trait Extension
return $default;
}
switch ($res['type']['_']) {
case 'storage.fileJpeg': return '.jpg';
case 'storage.fileGif': return '.gif';
case 'storage.filePng': return '.png';
case 'storage.filePdf': return '.pdf';
case 'storage.fileMp3': return '.mp3';
case 'storage.fileMov': return '.mov';
case 'storage.fileMp4': return '.mp4';
case 'storage.fileWebp': return '.webp';
default: return $default;
case 'storage.fileJpeg':
return '.jpg';
case 'storage.fileGif':
return '.gif';
case 'storage.filePng':
return '.png';
case 'storage.filePdf':
return '.pdf';
case 'storage.fileMp3':
return '.mp3';
case 'storage.fileMov':
return '.mov';
case 'storage.fileMp4':
return '.mp4';
case 'storage.fileWebp':
return '.webp';
default:
return $default;
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL\Conversion;
trait TD
@ -21,7 +21,6 @@ trait TD
}
if (!isset($params['ID'])) {
array_walk($params, [$this, 'tdcli_to_td']);
return $params;
}
foreach ($params as $key => $value) {
@ -33,46 +32,41 @@ trait TD
}
$params['_'] = lcfirst($params['ID']);
unset($params['ID']);
return $params;
}
public function td_to_mtproto($params)
{
$newparams = ['_' => self::REVERSE[$params['_']]];
foreach (self::TD_PARAMS_CONVERSION[$newparams['_']] as $td => $mtproto) {
if (is_array($mtproto)) {
switch (end($mtproto)) {
case 'choose_message_content':
switch ($params[$td]['_']) {
case 'inputMessageText':
$params[$td]['_'] = 'messages.sendMessage';
if (isset($params['disable_web_page_preview'])) {
$newparams['no_webpage'] = $params[$td]['disable_web_page_preview'];
switch ($params[$td]['_']) {
case 'inputMessageText':
$params[$td]['_'] = 'messages.sendMessage';
if (isset($params['disable_web_page_preview'])) {
$newparams['no_webpage'] = $params[$td]['disable_web_page_preview'];
}
$newparams = array_merge($params[$td], $newparams);
break;
default:
throw new Exception(\danog\MadelineProto\Lang::$current_lang['non_text_conversion']);
}
$newparams = array_merge($params[$td], $newparams);
break;
default: throw new Exception(\danog\MadelineProto\Lang::$current_lang['non_text_conversion']);
}
break;
default:
$newparams[$mtproto[0]] = isset($params[$td]) ? $params[$td] : null;
if (is_array($newparams[$mtproto[0]])) {
$newparams[$mtproto[0]] = $this->mtproto_to_td($newparams[$mtproto[0]]);
}
$newparams[$mtproto[0]] = isset($params[$td]) ? $params[$td] : null;
if (is_array($newparams[$mtproto[0]])) {
$newparams[$mtproto[0]] = $this->mtproto_to_td($newparams[$mtproto[0]]);
}
}
}
}
return $newparams;
}
public function mtproto_to_tdcli($params)
{
return $this->td_to_tdcli($this->mtproto_to_td($params));
}
public function mtproto_to_td(&$params)
{
if (!is_array($params)) {
@ -80,7 +74,6 @@ trait TD
}
if (!isset($params['_'])) {
array_walk($params, [$this, 'mtproto_to_td']);
return $params;
}
$newparams = ['_' => $params['_']];
@ -93,69 +86,67 @@ trait TD
} else {
switch (end($mtproto)) {
case 'choose_chat_id_from_botapi':
$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;
$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'];
break;
$newparams[$td] = ['_' => $params['out'] ? 'messageIsSuccessfullySent' : 'messageIsIncoming'];
break;
case 'choose_can_edit':
$newparams[$td] = !isset($params['fwd_from']) && $params['out'];
break;
$newparams[$td] = !isset($params['fwd_from']) && $params['out'];
break;
case 'choose_can_delete':
$newparams[$td] = $params['out'];
break;
$newparams[$td] = $params['out'];
break;
case 'choose_forward_info':
if (isset($params['fwd_from'])) {
$newparams[$td] = ['_' => 'messageForwardedFromUser'];
if (isset($params['fwd_from']['channel_id'])) {
$newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => '-100'.$params['fwd_from']['channel_id']];
if (isset($params['fwd_from'])) {
$newparams[$td] = ['_' => 'messageForwardedFromUser'];
if (isset($params['fwd_from']['channel_id'])) {
$newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => '-100' . $params['fwd_from']['channel_id']];
}
$newparams[$td]['date'] = $params['fwd_from']['date'];
if (isset($params['fwd_from']['channel_post'])) {
$newparams[$td]['channel_post'] = $params['fwd_from']['channel_post'];
}
if (isset($params['fwd_from']['from_id'])) {
$newparams[$td]['sender_user_id'] = $params['fwd_from']['from_id'];
}
} else {
$newparams[$td] = null;
}
$newparams[$td]['date'] = $params['fwd_from']['date'];
if (isset($params['fwd_from']['channel_post'])) {
$newparams[$td]['channel_post'] = $params['fwd_from']['channel_post'];
}
if (isset($params['fwd_from']['from_id'])) {
$newparams[$td]['sender_user_id'] = $params['fwd_from']['from_id'];
}
} else {
$newparams[$td] = null;
}
break;
break;
case 'choose_ttl':
$newparams[$td] = isset($params['ttl']) ? $params['ttl'] : 0;
break;
$newparams[$td] = isset($params['ttl']) ? $params['ttl'] : 0;
break;
case 'choose_ttl_expires_in':
$newparams[$td] = $newparams['ttl'] - microtime(true);
break;
$newparams[$td] = $newparams['ttl'] - microtime(true);
break;
case 'choose_message_content':
if ($params['message'] !== '') {
$newparams[$td] = ['_' => 'messageText', 'text' => $params['message']];
if (isset($params['media']['_']) && $params['media']['_'] === 'messageMediaWebPage') {
$newparams[$td]['web_page'] = $this->mtproto_to_td($params['media']['webpage']);
if ($params['message'] !== '') {
$newparams[$td] = ['_' => 'messageText', 'text' => $params['message']];
if (isset($params['media']['_']) && $params['media']['_'] === 'messageMediaWebPage') {
$newparams[$td]['web_page'] = $this->mtproto_to_td($params['media']['webpage']);
}
if (isset($params['entities'])) {
$newparams[$td]['entities'] = $params['entities'];
}
} else {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['non_text_conversion']);
}
if (isset($params['entities'])) {
$newparams[$td]['entities'] = $params['entities'];
}
} else {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['non_text_conversion']);
}
break;
break;
default:
if (isset($mtproto[1])) {
$newparams[$td] = isset($params[$mtproto[0]][$mtproto[1]]) ? $params[$mtproto[0]][$mtproto[1]] : null;
} else {
$newparams[$td] = isset($params[$mtproto[0]]) ? $params[$mtproto[0]] : null;
}
if (is_array($newparams[$td])) {
$newparams[$td] = $this->mtproto_to_td($newparams[$td]);
}
if (isset($mtproto[1])) {
$newparams[$td] = isset($params[$mtproto[0]][$mtproto[1]]) ? $params[$mtproto[0]][$mtproto[1]] : null;
} else {
$newparams[$td] = isset($params[$mtproto[0]]) ? $params[$mtproto[0]] : null;
}
if (is_array($newparams[$td])) {
$newparams[$td] = $this->mtproto_to_td($newparams[$td]);
}
}
}
}
return $newparams;
}
public function td_to_tdcli($params)
{
if (!is_array($params)) {
@ -167,12 +158,11 @@ trait TD
$newparams['ID'] = ucfirst($value);
} else {
if (!is_numeric($key) && !preg_match('/_^/', $key)) {
$key = $key.'_';
$key = $key . '_';
}
$newparams[$key] = $this->td_to_tdcli($value);
}
}
return $newparams;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,21 +10,18 @@ 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\TL;
class Exception extends \Exception
{
use PrettyException;
public function __toString()
{
return get_class($this).($this->message !== '' ? ': ' : '').$this->message.PHP_EOL.@file_get_contents(__DIR__.'/../../../.git/refs/heads/master').PHP_EOL.'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):'.PHP_EOL.PHP_EOL.$this->getTLTrace().PHP_EOL;
return get_class($this) . ($this->message !== '' ? ': ' : '') . $this->message . PHP_EOL . @file_get_contents(__DIR__ . '/../../../.git/refs/heads/master') . PHP_EOL . 'TL Trace (YOU ABSOLUTELY MUST READ THE TEXT BELOW):' . PHP_EOL . PHP_EOL . $this->getTLTrace() . PHP_EOL;
}
public function __construct($message, $file = '')
{
parent::__construct($message);
$this->prettify_tl($file);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,25 +10,22 @@ 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\TL;
trait PrettyException
{
public $tl_trace;
public function getTLTrace()
{
return $this->tl_trace;
}
public function prettify_tl($init = '')
{
$tl = false;
foreach (array_reverse($this->getTrace()) as $k => $frame) {
if (isset($frame['function']) && in_array($frame['function'], ['serialize_params', 'serialize_object'])) {
if ($frame['args'][2] !== '') {
$this->tl_trace .= $tl ? "['".$frame['args'][2]."']" : "While serializing: \t".$frame['args'][2].PHP_EOL;
$this->tl_trace .= $tl ? "['" . $frame['args'][2] . "']" : "While serializing: \t" . $frame['args'][2] . PHP_EOL;
$tl = true;
}
} else {
@ -37,15 +35,15 @@ trait PrettyException
if (isset($frame['function']) && ($frame['function'] === 'handle_rpc_error' && $k === count($this->getTrace()) - 1) || $frame['function'] === 'unserialize') {
continue;
}
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']).'('.$frame['line'].'):', 20)."\t" : '';
$this->tl_trace .= isset($frame['function']) ? $frame['function'].'(' : '';
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']) . '(' . $frame['line'] . '):', 20) . "\t" : '';
$this->tl_trace .= isset($frame['function']) ? $frame['function'] . '(' : '';
$this->tl_trace .= isset($frame['args']) ? substr(json_encode($frame['args']), 1, -1) : '';
$this->tl_trace .= ')';
$this->tl_trace .= PHP_EOL;
$tl = false;
}
}
$this->tl_trace .= $init !== '' ? "['".$init."']" : '';
$this->tl_trace .= $init !== '' ? "['" . $init . "']" : '';
$this->tl_trace = implode(PHP_EOL, array_reverse(explode(PHP_EOL, $this->tl_trace)));
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL;
trait TL
@ -20,7 +20,6 @@ trait TL
public $td_constructors;
public $td_methods;
public $td_descriptions;
public function construct_tl($files)
{
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['TL_loading']], \danog\MadelineProto\Logger::VERBOSE);
@ -72,7 +71,7 @@ trait TL
}
continue;
}
$line = preg_replace(['|//.*|', '|^\s+$|'], '', $line);
$line = preg_replace(['|//.*|', '|^\\s+$|'], '', $line);
if ($line === '') {
continue;
}
@ -84,49 +83,24 @@ trait TL
$type = 'constructors';
continue;
}
if (preg_match('|^===\d*===|', $line)) {
$layer = (int) preg_replace('|\D*|', '', $line);
if (preg_match('|^===\\d*===|', $line)) {
$layer = (int) preg_replace('|\\D*|', '', $line);
continue;
}
if (preg_match('/^vector#/', $line)) {
continue;
}
if (preg_match('/ \?= /', $line)) {
if (preg_match('/ \\?= /', $line)) {
continue;
}
$name = preg_replace(['/#.*/', '/\s.*/'], '', $line);
$name = preg_replace(['/#.*/', '/\\s.*/'], '', $line);
if (in_array($name, ['bytes', 'int128', 'int256', 'int512'])) {
continue;
}
$clean = preg_replace([
'/:bytes /',
'/;/',
'/#[a-f0-9]+ /',
'/ [a-zA-Z0-9_]+\:flags\.[0-9]+\?true/',
'/[<]/',
'/[>]/',
'/ /',
'/^ /',
'/ $/',
'/\?bytes /',
'/{/',
'/}/',
], [
':string ',
'',
' ',
'',
' ',
' ',
' ',
'',
'',
'?string ',
'',
'', ], $line);
$clean = preg_replace(['/:bytes /', '/;/', '/#[a-f0-9]+ /', '/ [a-zA-Z0-9_]+\\:flags\\.[0-9]+\\?true/', '/[<]/', '/[>]/', '/ /', '/^ /', '/ $/', '/\\?bytes /', '/{/', '/}/'], [':string ', '', ' ', '', ' ', ' ', ' ', '', '', '?string ', '', ''], $line);
$id = hash('crc32b', $clean);
if (preg_match('/^[^\s]+#/', $line)) {
$nid = str_pad(preg_replace(['/^[^#]+#/', '/\s.+/'], '', $line), 8, '0', \STR_PAD_LEFT);
if (preg_match('/^[^\\s]+#/', $line)) {
$nid = str_pad(preg_replace(['/^[^#]+#/', '/\\s.+/'], '', $line), 8, '0', \STR_PAD_LEFT);
if ($id !== $nid && $scheme_type !== 'botAPI') {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['crc32_mismatch'], $id, $nid, $line)], \danog\MadelineProto\Logger::ERROR);
}
@ -140,11 +114,11 @@ trait TL
$TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name;
$TL_dict[$type][$key]['id'] = strrev(hex2bin($id));
$TL_dict[$type][$key]['params'] = [];
$TL_dict[$type][$key]['type'] = preg_replace(['/.+\s/', '/;/'], '', $line);
$TL_dict[$type][$key]['type'] = preg_replace(['/.+\\s/', '/;/'], '', $line);
if ($layer !== null) {
$TL_dict[$type][$key]['layer'] = $layer;
}
foreach (explode(' ', preg_replace(['/^[^\s]+\s/', '/=\s[^\s]+/', '/\s$/'], '', $line)) as $param) {
foreach (explode(' ', preg_replace(['/^[^\\s]+\\s/', '/=\\s[^\\s]+/', '/\\s$/'], '', $line)) as $param) {
if ($param === '') {
continue;
}
@ -165,7 +139,7 @@ trait TL
}
}
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'].$file);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['src_file_invalid'] . $file);
}
$orig = $this->encrypted_layer;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_obj']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
@ -173,12 +147,11 @@ trait TL
if ($scheme_type === 'secret') {
$this->encrypted_layer = max($this->encrypted_layer, $elem['layer']);
}
$this->{($scheme_type === 'td' ? 'td_' : '').'constructors'}->add($elem, $scheme_type);
$this->{($scheme_type === 'td' ? 'td_' : '') . 'constructors'}->add($elem, $scheme_type);
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_methods']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($TL_dict['methods'] as $elem) {
$this->{($scheme_type === 'td' ? 'td_' : '').'methods'}->add($elem);
$this->{($scheme_type === 'td' ? 'td_' : '') . 'methods'}->add($elem);
if ($scheme_type === 'secret') {
$this->encrypted_layer = max($this->encrypted_layer, $elem['layer']);
}
@ -210,7 +183,6 @@ trait TL
}
}
}
public function get_method_namespaces()
{
$res = [];
@ -218,25 +190,20 @@ trait TL
$a = key($pair);
$res[$a] = $a;
}
return $res;
}
public function get_methods_namespaced()
{
return $this->methods->method_namespace;
}
public function deserialize_bool($id)
{
$tl_elem = $this->constructors->find_by_id($id);
if ($tl_elem === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['bool_error']);
}
return $tl_elem['predicate'] === 'boolTrue';
}
public function serialize_object($type, $object, $ctx, $layer = -1)
{
switch ($type['type']) {
@ -244,19 +211,16 @@ trait TL
if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
}
return $this->pack_signed_int($object);
case '#':
if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
}
return $this->pack_unsigned_int($object);
case 'long':
if (is_object($object)) {
return str_pad(strrev($object->toBytes()), 8, chr(0));
}
if (is_string($object) && strlen($object) === 8) {
return $object;
}
@ -266,25 +230,21 @@ trait TL
if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
}
return $this->pack_signed_long($object);
case 'int128':
if (strlen($object) !== 16) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_16']);
}
return (string) $object;
case 'int256':
if (strlen($object) !== 32) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_32']);
}
return (string) $object;
case 'int512':
if (strlen($object) !== 64) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_64']);
}
return (string) $object;
case 'double':
return $this->pack_double($object);
@ -292,24 +252,22 @@ trait TL
if (!is_string($object)) {
throw new Exception("You didn't provide a valid string");
}
$object = pack('C*', ...unpack('C*', $object));
$l = strlen($object);
$concat = '';
if ($l <= 253) {
$concat .= chr($l);
$concat .= $object;
$concat .= pack('@'.$this->posmod((-$l - 1), 4));
$concat .= pack('@' . $this->posmod(-$l - 1, 4));
} else {
$concat .= chr(254);
$concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object;
$concat .= pack('@'.$this->posmod(-$l, 4));
$concat .= pack('@' . $this->posmod(-$l, 4));
}
return $concat;
case 'bytes':
if (!is_string($object) && !($object instanceof \danog\MadelineProto\TL\Types\Bytes)) {
if (!is_string($object) && !$object instanceof \danog\MadelineProto\TL\Types\Bytes) {
throw new Exception("You didn't provide a valid string");
}
$l = strlen($object);
@ -317,17 +275,16 @@ trait TL
if ($l <= 253) {
$concat .= chr($l);
$concat .= $object;
$concat .= pack('@'.$this->posmod((-$l - 1), 4));
$concat .= pack('@' . $this->posmod(-$l - 1, 4));
} else {
$concat .= chr(254);
$concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object;
$concat .= pack('@'.$this->posmod(-$l, 4));
$concat .= pack('@' . $this->posmod(-$l, 4));
}
return $concat;
case 'Bool':
return $this->constructors->find_by_predicate(((bool) $object) ? 'boolTrue' : 'boolFalse')['id'];
return $this->constructors->find_by_predicate((bool) $object ? 'boolTrue' : 'boolFalse')['id'];
case 'true':
return;
case '!X':
@ -341,7 +298,6 @@ trait TL
foreach ($object as $k => $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k);
}
return $concat;
case 'vector':
if (!is_array($object)) {
@ -351,16 +307,14 @@ trait TL
foreach ($object as $k => $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k);
}
return $concat;
case 'Object':
if (is_string($object)) {
return $object;
}
}
$auto = false;
if ((!is_array($object) || (isset($object['_']) && $this->constructors->find_by_predicate($object['_'])['type'] !== $type['type'])) && in_array($type['type'], ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) {
if ((!is_array($object) || isset($object['_']) && $this->constructors->find_by_predicate($object['_'])['type'] !== $type['type']) && in_array($type['type'], ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) {
$object = $this->get_info($object);
if (!isset($object[$type['type']])) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['peer_not_in_db']);
@ -369,7 +323,6 @@ trait TL
}
if (!isset($object['_'])) {
$constructorData = $this->constructors->find_by_predicate($type['type'], $layer);
if ($constructorData === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['predicate_not_set']);
}
@ -377,22 +330,17 @@ trait TL
$object['_'] = $constructorData['predicate'];
}
$predicate = $object['_'];
$constructorData = $this->constructors->find_by_predicate($predicate, $layer);
if ($constructorData === false) {
\danog\MadelineProto\Logger::log([$object], \danog\MadelineProto\Logger::FATAL_ERROR);
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error'], $predicate));
}
if ($bare = ($type['type'] != '' && $type['type'][0] === '%')) {
if ($bare = $type['type'] != '' && $type['type'][0] === '%') {
$type['type'] = substr($type['type'], 1);
}
if ($predicate === $type['type'] && !$auto) {
$bare = true;
}
if ($predicate === 'messageEntityMentionName') {
$constructorData = $this->constructors->find_by_predicate('inputMessageEntityMentionName');
}
@ -400,24 +348,19 @@ trait TL
if (!$bare) {
$concat = $constructorData['id'];
}
return $concat.$this->serialize_params($constructorData, $object, '', $layer);
return $concat . $this->serialize_params($constructorData, $object, '', $layer);
}
public function serialize_method($method, $arguments)
{
$tl = $this->methods->find_by_method($method);
if ($tl === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['method_not_found'].$method);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['method_not_found'] . $method);
}
return $tl['id'].$this->serialize_params($tl, $arguments, $method);
return $tl['id'] . $this->serialize_params($tl, $arguments, $method);
}
public function serialize_params($tl, $arguments, $ctx, $layer = -1)
{
$serialized = '';
$arguments = $this->botAPI_to_MTProto($arguments);
$flags = 0;
foreach ($tl['params'] as $cur_flag) {
@ -425,17 +368,17 @@ trait TL
switch ($cur_flag['type']) {
case 'true':
case 'false':
$flags = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']]) ? ($flags | $cur_flag['pow']) : ($flags & ~$cur_flag['pow']);
$flags = isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']] ? $flags | $cur_flag['pow'] : $flags & ~$cur_flag['pow'];
unset($arguments[$cur_flag['name']]);
break;
case 'Bool':
$arguments[$cur_flag['name']] = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']]) && (($flags & $cur_flag['pow']) != 0);
$arguments[$cur_flag['name']] = isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']] && ($flags & $cur_flag['pow']) != 0;
if (($flags & $cur_flag['pow']) === 0) {
unset($arguments[$cur_flag['name']]);
}
break;
default:
$flags = (isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']] !== null) ? ($flags | $cur_flag['pow']) : ($flags & ~$cur_flag['pow']);
$flags = isset($arguments[$cur_flag['name']]) && $arguments[$cur_flag['name']] !== null ? $flags | $cur_flag['pow'] : $flags & ~$cur_flag['pow'];
break;
}
}
@ -448,7 +391,7 @@ trait TL
continue;
}
if ($current_argument['name'] === 'random_bytes') {
$serialized .= $this->serialize_object(['type' => 'bytes'], $this->random(15 + (4 * (random_int(0, PHP_INT_MAX) % 3))), 'random_bytes');
$serialized .= $this->serialize_object(['type' => 'bytes'], $this->random(15 + 4 * (random_int(0, PHP_INT_MAX) % 3)), 'random_bytes');
continue;
}
if ($current_argument['name'] === 'data' && isset($arguments['message'])) {
@ -472,11 +415,10 @@ trait TL
}
}
}
if ($id = $this->constructors->find_by_predicate(lcfirst($current_argument['type']).'Empty')) {
if ($id = $this->constructors->find_by_predicate(lcfirst($current_argument['type']) . 'Empty')) {
$serialized .= $id['id'];
continue;
}
throw new Exception(\danog\MadelineProto\Lang::$current_lang['params_missing'], $current_argument['name']);
}
if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputEncryptedChat') {
@ -488,14 +430,11 @@ trait TL
if ($current_argument['type'] === 'DataJSON') {
$arguments[$current_argument['name']] = ['_' => 'dataJSON', 'data' => json_encode($arguments[$current_argument['name']])];
}
//\danog\MadelineProto\Logger::log(['Serializing '.$current_argument['name'].' of type '.$current_argument['type']);
$serialized .= $this->serialize_object($current_argument, $arguments[$current_argument['name']], $current_argument['name'], $layer);
}
return $serialized;
}
public function get_length($stream, $type = ['type' => ''])
{
if (is_string($stream)) {
@ -507,10 +446,8 @@ trait TL
throw new Exception(\danog\MadelineProto\Lang::$current_lang['stream_handle_invalid']);
}
$this->deserialize($stream, $type);
return ftell($stream);
}
/**
* :type stream: io.BytesIO object.
*/
@ -524,7 +461,6 @@ trait TL
} elseif (!is_resource($stream)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['stream_handle_invalid']);
}
switch ($type['type']) {
case 'Bool':
return $this->deserialize_bool(stream_get_contents($stream, 4));
@ -536,7 +472,6 @@ trait TL
if (isset($type['idstrlong'])) {
return stream_get_contents($stream, 8);
}
return \danog\MadelineProto\Logger::$bigint || isset($type['strlong']) ? stream_get_contents($stream, 8) : $this->unpack_signed_long(stream_get_contents($stream, 8));
case 'double':
return $this->unpack_double(stream_get_contents($stream, 8));
@ -553,7 +488,7 @@ trait TL
throw new Exception(\danog\MadelineProto\Lang::$current_lang['length_too_big']);
}
if ($l === 254) {
$long_len = unpack('V', stream_get_contents($stream, 3).chr(0))[1];
$long_len = unpack('V', stream_get_contents($stream, 3) . chr(0))[1];
$x = stream_get_contents($stream, $long_len);
$resto = $this->posmod(-$long_len, 4);
if ($resto > 0) {
@ -569,17 +504,14 @@ trait TL
if (!is_string($x)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['deserialize_not_str']);
}
return $type['type'] === 'bytes' ? (new Types\Bytes($x)) : $x;
return $type['type'] === 'bytes' ? new Types\Bytes($x) : $x;
case 'Vector t':
$id = stream_get_contents($stream, 4);
$constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) {
$constructorData = $this->methods->find_by_id($id);
$constructorData['predicate'] = 'method_'.$constructorData['method'];
$constructorData['predicate'] = 'method_' . $constructorData['method'];
}
if ($constructorData === false) {
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
}
@ -590,7 +522,7 @@ trait TL
case 'vector':
break;
default:
throw new Exception(\danog\MadelineProto\Lang::$current_lang['vector_invalid'].$constructorData['predicate']);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['vector_invalid'] . $constructorData['predicate']);
}
case 'vector':
$count = unpack('V', stream_get_contents($stream, 4))[1];
@ -599,14 +531,13 @@ trait TL
for ($i = 0; $i < $count; $i++) {
$result[] = $this->deserialize($stream, $type);
}
return $result;
}
if ($type['type'] != '' && $type['type'][0] === '%') {
$checkType = substr($type['type'], 1);
$constructorData = $this->constructors->find_by_type($checkType);
if ($constructorData === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['constructor_not_found'].$checkType);
throw new Exception(\danog\MadelineProto\Lang::$current_lang['constructor_not_found'] . $checkType);
}
} else {
$constructorData = $this->constructors->find_by_predicate($type['type']);
@ -618,7 +549,7 @@ trait TL
if ($constructorData === false) {
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
}
$constructorData['predicate'] = 'method_'.$constructorData['method'];
$constructorData['predicate'] = 'method_' . $constructorData['method'];
}
}
}
@ -626,14 +557,12 @@ trait TL
if (!isset($type['subtype'])) {
$type['subtype'] = '';
}
return $this->deserialize(gzdecode($this->deserialize($stream, ['type' => 'bytes'])), ['type' => '', 'datacenter' => $type['datacenter'], 'subtype' => $type['subtype']]);
}
if ($constructorData['type'] === 'Vector t') {
$constructorData['datacenter'] = $type['datacenter'];
$constructorData['subtype'] = isset($type['subtype']) ? $type['subtype'] : '';
$constructorData['type'] = 'vector';
return $this->deserialize($stream, $constructorData);
}
if ($constructorData['predicate'] === 'boolTrue') {
@ -670,7 +599,6 @@ trait TL
if (in_array($arg['name'], ['peer_tag', 'file_token', 'cdn_key', 'cdn_iv'])) {
$arg['type'] = 'string';
}
if ($x['_'] === 'rpc_result' && $arg['name'] === 'result' && isset($this->datacenter->sockets[$type['datacenter']]->new_outgoing[$x['req_msg_id']]['type']) && stripos($this->datacenter->sockets[$type['datacenter']]->new_outgoing[$x['req_msg_id']]['type'], '<') !== false) {
$arg['subtype'] = preg_replace(['|Vector[<]|', '|[>]|'], '', $this->datacenter->sockets[$type['datacenter']]->new_outgoing[$x['req_msg_id']]['type']);
}
@ -686,13 +614,13 @@ trait TL
}
}
}
if (isset($x['flags'])) { // I don't think we need this anymore
if (isset($x['flags'])) {
// I don't think we need this anymore
unset($x['flags']);
}
if ($x['_'] === 'dataJSON') {
return json_decode($x['data'], true);
}
if ($x['_'] === 'message' && isset($x['reply_markup']['rows'])) {
foreach ($x['reply_markup']['rows'] as $key => $row) {
foreach ($row['buttons'] as $bkey => $button) {
@ -700,7 +628,6 @@ trait TL
}
}
}
return $x;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL;
class TLConstructor
@ -17,7 +17,6 @@ class TLConstructor
use \danog\Serializable;
use \danog\MadelineProto\Tools;
use TLParams;
public $by_id = [];
public $by_predicate_and_layer = [];
public $layers = [];
@ -25,16 +24,14 @@ class TLConstructor
//public $params = [];
//public $layer = [];
//public $key = 0;
public function __sleep()
{
return ['by_predicate_and_layer', 'by_id', 'layers'];
}
public function add($json_dict, $scheme_type)
{
$predicate = (string) ((($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message') ? 'MT' : '').$json_dict['predicate']);
$this->by_id[$json_dict['id']] = ['predicate' => $predicate, 'params' => $json_dict['params'], 'type' => (($scheme_type === 'mtproto' && $json_dict['type'] === 'Message') ? 'MT' : '').$json_dict['type']];
$predicate = (string) (($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message' ? 'MT' : '') . $json_dict['predicate']);
$this->by_id[$json_dict['id']] = ['predicate' => $predicate, 'params' => $json_dict['params'], 'type' => ($scheme_type === 'mtproto' && $json_dict['type'] === 'Message' ? 'MT' : '') . $json_dict['type']];
if ($scheme_type === 'secret') {
$this->by_id[$json_dict['id']]['layer'] = $json_dict['layer'];
$this->layers[$json_dict['layer']] = $json_dict['layer'];
@ -42,34 +39,30 @@ class TLConstructor
} else {
$json_dict['layer'] = '';
}
$this->by_predicate_and_layer[$predicate.$json_dict['layer']] = $json_dict['id'];
$this->by_predicate_and_layer[$predicate . $json_dict['layer']] = $json_dict['id'];
$this->parse_params($json_dict['id'], $scheme_type === 'mtproto');
}
public function find_by_type($type)
{
foreach ($this->by_id as $id => $constructor) {
if ($constructor['type'] === $type) {
$constructor['id'] = $id;
$constructor['params'] = $constructor['params'];
return $constructor;
}
}
return false;
}
public function find_by_predicate($predicate, $layer = -1)
{
if ($layer !== -1) {
foreach ($this->layers as $alayer) {
if ($alayer <= $layer) {
if (isset($this->by_predicate_and_layer[$predicate.$alayer])) {
$chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
if (isset($this->by_predicate_and_layer[$predicate . $alayer])) {
$chosenid = $this->by_predicate_and_layer[$predicate . $alayer];
}
} elseif (!isset($chosenid)) {
$chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
$chosenid = $this->by_predicate_and_layer[$predicate . $alayer];
}
}
if (!isset($chosenid)) {
@ -78,30 +71,24 @@ class TLConstructor
$constructor = $this->by_id[$chosenid];
$constructor['id'] = $chosenid;
$constructor['params'] = $constructor['params'];
return $constructor;
}
if (isset($this->by_predicate_and_layer[$predicate])) {
$constructor = $this->by_id[$this->by_predicate_and_layer[$predicate]];
$constructor['id'] = $this->by_predicate_and_layer[$predicate];
$constructor['params'] = $constructor['params'];
return $constructor;
}
return false;
}
public function find_by_id($id)
{
if (isset($this->by_id[$id])) {
$constructor = $this->by_id[$id];
$constructor['id'] = $id;
$constructor['params'] = $constructor['params'];
return $constructor;
}
return false;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL;
class TLMethod
@ -20,48 +20,38 @@ class TLMethod
public $by_id = [];
public $by_method = [];
public $method_namespace = [];
public function __sleep()
{
return ['by_id', 'by_method', 'method_namespace'];
}
public function add($json_dict)
{
$this->by_id[$json_dict['id']] = ['method' => $json_dict['method'], 'type' => $json_dict['type'], 'params' => $json_dict['params']];
$this->by_method[$json_dict['method']] = $json_dict['id'];
$namespace = explode('.', $json_dict['method']);
if (isset($namespace[1])) {
$this->method_namespace[] = [$namespace[0] => $namespace[1]];
}
$this->parse_params($json_dict['id']);
}
public function find_by_id($id)
{
if (isset($this->by_id[$id])) {
$method = $this->by_id[$id];
$method['id'] = $id;
$method['params'] = $method['params'];
return $method;
}
return false;
}
public function find_by_method($method_name)
{
if (isset($this->by_method[$method_name])) {
$method = $this->by_id[$this->by_method[$method_name]];
$method['id'] = $this->by_method[$method_name];
$method['params'] = $method['params'];
return $method;
}
return false;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL;
trait TLParams
@ -17,7 +17,7 @@ trait TLParams
public function parse_params($key, $mtproto = false)
{
foreach ($this->by_id[$key]['params'] as $kkey => $param) {
if (preg_match('/^flags\.\d*\?/', $param['type'])) {
if (preg_match('/^flags\\.\\d*\\?/', $param['type'])) {
$flag = explode('?', explode('flags.', $param['type'])[1]);
$param['pow'] = pow(2, $flag[0]);
$param['type'] = $flag[1];
@ -31,12 +31,12 @@ trait TLParams
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
$param['type'] = 'Vector t';
}
$param['subtype'] = (($mtproto && $param['subtype'] === 'Message') ? 'MT' : '').$param['subtype'];
$param['subtype'] = (($mtproto && $param['subtype'] === '%Message') ? '%MTMessage' : $param['subtype']);
$param['subtype'] = ($mtproto && $param['subtype'] === 'Message' ? 'MT' : '') . $param['subtype'];
$param['subtype'] = $mtproto && $param['subtype'] === '%Message' ? '%MTMessage' : $param['subtype'];
}
$param['type'] = (($mtproto && $param['type'] === 'Message') ? 'MT' : '').$param['type'];
$param['type'] = (($mtproto && $param['type'] === '%Message') ? '%MTMessage' : $param['type']);
$param['type'] = ($mtproto && $param['type'] === 'Message' ? 'MT' : '') . $param['type'];
$param['type'] = $mtproto && $param['type'] === '%Message' ? '%MTMessage' : $param['type'];
$this->by_id[$key]['params'][$kkey] = $param;
}
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\TL\Types;
class Button extends \Volatile implements \JsonSerializable
@ -17,7 +17,6 @@ class Button extends \Volatile implements \JsonSerializable
use \danog\Serializable;
private $info = [];
private $data = [];
public function __magic_construct($API, $message, $button)
{
$this->data = $button;
@ -25,33 +24,31 @@ class Button extends \Volatile implements \JsonSerializable
$this->info['id'] = $message['id'];
$this->info['API'] = $API;
}
public function __sleep()
{
return ['data', 'info'];
}
public function click($donotwait = false)
{
switch ($this->data['_']) {
default: return false;
case 'keyboardButtonUrl': return $this->data['url'];
case 'keyboardButton': return $this->info['API']->method_call('messages.sendMessage', ['peer' => $this->info['peer'], 'message' => $this->data['text'], 'reply_to_msg_id' => $this->info['id']], ['datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonCallback': return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'data' => $this->data['data']], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonGame': return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'game' => true], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
default:
return false;
case 'keyboardButtonUrl':
return $this->data['url'];
case 'keyboardButton':
return $this->info['API']->method_call('messages.sendMessage', ['peer' => $this->info['peer'], 'message' => $this->data['text'], 'reply_to_msg_id' => $this->info['id']], ['datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonCallback':
return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'data' => $this->data['data']], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonGame':
return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'game' => true], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
}
}
public function __debugInfo()
{
return [
'data' => $this->data,
'info' => ['peer' => $this->info['peer'], 'id' => $this->info['id']],
];
return ['data' => $this->data, 'info' => ['peer' => $this->info['peer'], 'id' => $this->info['id']]];
}
public function jsonSerialize()
{
return (array) $this->data;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,31 +10,26 @@ 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\TL\Types;
class Bytes extends \Volatile implements \JsonSerializable
{
use \danog\Serializable;
private $bytes = [];
public function __magic_construct($bytes)
{
$this->bytes = $bytes;
}
public function __sleep()
{
return ['bytes'];
}
public function __toString()
{
return $this->bytes;
}
public function jsonSerialize()
{
return utf8_encode($this->bytes);
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\Threads;
/**
@ -23,39 +23,33 @@ class SocketHandler extends \Threaded implements \Collectable
$this->current = $current;
$this->error = $error;
}
/**
* Reading connection and receiving message from server. Check the CRC32.
*/
public function run()
{
require __DIR__.'/../../../../vendor/autoload.php';
require __DIR__ . '/../../../../vendor/autoload.php';
if ($this->error !== true) {
if ($this->error === -404) {
if ($this->API->datacenter->sockets[$this->current]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['resetting_auth_key']], \danog\MadelineProto\Logger::WARNING);
$this->API->datacenter->sockets[$this->current]->temp_auth_key = null;
$this->API->init_authorization();
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['recreate_temp_auth_key']);
}
}
throw new \danog\MadelineProto\RPCErrorException($this->error, $this->error);
}
$this->API->handle_messages($this->current);
$this->setGarbage();
}
protected $garbage = false;
public function setGarbage():void
public function setGarbage()
{
$this->garbage = true;
}
public function isGarbage():bool
public function isGarbage()
{
return $this->garbage;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\Threads;
/**
@ -18,34 +18,27 @@ namespace danog\MadelineProto\Threads;
class SocketReader extends \Threaded implements \Collectable
{
public $ready = false;
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([\danog\MadelineProto\Lang::$current_lang['shutting_down_reader_pool'].$this->current], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['shutting_down_reader_pool'] . $this->current], \danog\MadelineProto\Logger::NOTICE);
}
/**
* Reading connection and receiving message from server. Check the CRC32.
*/
public function run()
{
require __DIR__.'/../../../../vendor/autoload.php';
require __DIR__ . '/../../../../vendor/autoload.php';
$handler_pool = new \Pool($this->API->settings['threading']['handler_workers']);
$this->ready = true;
while ($this->API->run_workers) {
try {
$this->API->datacenter->sockets[$this->current]->reading = true;
@ -64,16 +57,13 @@ class SocketReader extends \Threaded implements \Collectable
}
$this->setGarbage();
}
public $garbage = false;
public function setGarbage():void
public function setGarbage()
{
$this->garbage = true;
}
public function isGarbage():bool
public function isGarbage()
{
return $this->garbage;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
/**
@ -21,7 +21,6 @@ trait Tools
{
return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length);
}
/**
* posmod(numeric,numeric) : numeric
* Works just like the % (modulus) operator, only returns always a postive number.
@ -29,10 +28,8 @@ trait Tools
public function posmod($a, $b)
{
$resto = $a % $b;
return $resto < 0 ? ($resto + abs($b)) : $resto;
return $resto < 0 ? $resto + abs($b) : $resto;
}
public function array_cast_recursive($array, $force = false)
{
if (!\danog\MadelineProto\Logger::$has_thread && !$force) {
@ -46,28 +43,22 @@ trait Tools
$array[$key] = $this->array_cast_recursive($value, $force);
}
}
return $array;
}
public function unpack_signed_int($value)
{
if (strlen($value) !== 4) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_4']);
}
return unpack('l', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function unpack_signed_long($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_8']);
}
return unpack('q', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function pack_signed_int($value)
{
if ($value > 2147483647) {
@ -77,23 +68,19 @@ trait Tools
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_2147483648'], $value));
}
$res = pack('l', $value);
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function pack_signed_long($value)
{
if ($value > 9223372036854775807) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_bigger_than_9223372036854775807'], $value));
}
if ($value < -9223372036854775808) {
if ($value < -9.223372036854776E+18) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_9223372036854775808'], $value));
}
$res = \danog\MadelineProto\Logger::$bigint ? ($this->pack_signed_int($value)."\0\0\0\0") : (\danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value));
$res = \danog\MadelineProto\Logger::$bigint ? $this->pack_signed_int($value) . "\0\0\0\0" : (\danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value));
return $res;
}
public function pack_unsigned_int($value)
{
if ($value > 4294967295) {
@ -102,26 +89,21 @@ trait Tools
if ($value < 0) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_0'], $value));
}
return pack('V', $value);
}
public function pack_double($value)
{
$res = pack('d', $value);
if (strlen($res) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['encode_double_error']);
}
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function unpack_double($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_8']);
}
return unpack('d', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\VoIP;
/**
@ -21,10 +21,9 @@ namespace danog\MadelineProto\VoIP;
trait AuthKeyHandler
{
private $calls = [];
public function request_call($user)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
array_walk($this->calls, function ($controller, $id) {
@ -32,7 +31,6 @@ trait AuthKeyHandler
$controller->discard();
}
});
$user = $this->get_info($user);
if (!isset($user['InputUser']) || $user['InputUser']['_'] === 'inputUserSelf') {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['peer_not_in_db']);
@ -48,19 +46,15 @@ trait AuthKeyHandler
$res = $this->method_call('phone.requestCall', ['user_id' => $user, 'g_a_hash' => hash('sha256', $g_a->toBytes(), true), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_p2p' => true, 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]);
$this->calls[$res['phone_call']['id']] = $controller = new \danog\MadelineProto\VoIP(true, $user['user_id'], ['_' => 'inputPhoneCall', 'id' => $res['phone_call']['id'], 'access_hash' => $res['phone_call']['access_hash']], $this, \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED, $res['phone_call']['protocol']);
$controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$this->handle_pending_updates();
$this->get_updates_difference();
return $controller;
}
public function accept_call($call)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$controller->discard();
@ -68,48 +62,38 @@ trait AuthKeyHandler
});
if ($this->call_status($call['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_1'], $call['id'])]);
return false;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['accepting_call'], $this->calls[$call['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['generating_b']], \danog\MadelineProto\Logger::VERBOSE);
$b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two));
$g_b = $dh_config['g']->powMod($b, $dh_config['p']);
$this->check_G($g_b, $dh_config['p']);
try {
$res = $this->method_call('phone.acceptCall', ['peer' => $call, 'g_b' => $g_b->toBytes(), 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'udp_p2p' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'CALL_ALREADY_ACCEPTED') {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id'])]);
return true;
}
if ($e->rpc === 'CALL_ALREADY_DECLINED') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['call_already_declined']]);
$this->discard_call($call['id'], 'phoneCallDiscardReasonHangup');
return false;
}
throw $e;
}
$this->calls[$res['phone_call']['id']]->storage['b'] = $b;
$this->handle_pending_updates();
$this->get_updates_difference();
return true;
}
public function confirm_call($params)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$controller->discard();
@ -117,52 +101,34 @@ trait AuthKeyHandler
});
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_2'], $params['id'])]);
return false;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_confirming'], $this->calls[$params['id']]->getOtherID())], \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 = str_pad($params['g_b']->powMod($this->calls[$params['id']]->storage['a'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT);
$res = $this->method_call('phone.confirmCall', ['key_fingerprint' => substr(sha1($key, true), -8), 'peer' => ['id' => $params['id'], 'access_hash' => $params['access_hash'], '_' => 'inputPhoneCall'], 'g_a' => $this->calls[$params['id']]->storage['g_a'], 'protocol' => ['_' => 'phoneCallProtocol', 'udp_reflector' => true, 'min_layer' => 65, 'max_layer' => 65]], ['datacenter' => $this->datacenter->curdc])['phone_call'];
$visualization = [];
$length = new \phpseclib\Math\BigInteger(count($this->emojis));
foreach (str_split(hash('sha256', $key.str_pad($this->calls[$params['id']]->storage['g_a'], 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
$number[0] = chr(ord($number[0]) & 0x7F);
$visualization[] = $this->emojis[(int) ((new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString())];
foreach (str_split(hash('sha256', $key . str_pad($this->calls[$params['id']]->storage['g_a'], 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
$number[0] = chr(ord($number[0]) & 0x7f);
$visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
}
$this->calls[$params['id']]->setVisualization($visualization);
$this->calls[$params['id']]->configuration['shared_config'] = array_merge($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), $this->calls[$params['id']]->configuration['shared_config']);
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$res['connection']], $res['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
$this->calls[$params['id']]->configuration = array_merge([
'recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000,
'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000,
'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER,
'enable_NS' => true,
'enable_AEC' => true,
'enable_AGC' => true,
'auth_key' => $key,
'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET,
], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig();
$res = $this->calls[$params['id']]->startTheMagic();
$this->handle_pending_updates();
return $res;
}
public function complete_call($params)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$controller->discard();
@ -170,11 +136,9 @@ trait AuthKeyHandler
});
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED || !isset($this->calls[$params['id']]->storage['b'])) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_3'], $params['id'])]);
return false;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_completing'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
$dh_config = $this->get_dh_config();
if (hash('sha256', $params['g_a_or_b'], true) != $this->calls[$params['id']]->storage['g_a_hash']) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['invalid_g_a']);
@ -182,82 +146,58 @@ 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 = str_pad($params['g_a_or_b']->powMod($this->calls[$params['id']]->storage['b'], $dh_config['p'])->toBytes(), 256, chr(0), \STR_PAD_LEFT);
if (substr(sha1($key, true), -8) != $params['key_fingerprint']) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_invalid']);
}
$visualization = [];
$length = new \phpseclib\Math\BigInteger(count($this->emojis));
foreach (str_split(hash('sha256', $key.str_pad($params['g_a_or_b']->toBytes(), 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
$number[0] = chr(ord($number[0]) & 0x7F);
$visualization[] = $this->emojis[(int) ((new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString())];
foreach (str_split(hash('sha256', $key . str_pad($params['g_a_or_b']->toBytes(), 256, chr(0), \STR_PAD_LEFT), true), 8) as $number) {
$number[0] = chr(ord($number[0]) & 0x7f);
$visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
}
$this->calls[$params['id']]->setVisualization($visualization);
$this->calls[$params['id']]->configuration['shared_config'] = array_merge($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), $this->calls[$params['id']]->configuration['shared_config']);
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$params['connection']], $params['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
$this->calls[$params['id']]->configuration = array_merge([
'recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000,
'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000,
'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER,
'enable_NS' => true,
'enable_AEC' => true,
'enable_AGC' => true,
'auth_key' => $key,
'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET,
], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig();
return $this->calls[$params['id']]->startTheMagic();
}
public function call_status($id)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$controller->discard();
}
});
if (isset($this->calls[$id])) {
return $this->calls[$id]->getCallState();
}
return \danog\MadelineProto\VoIP::CALL_STATE_NONE;
}
public function get_call($call)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
array_walk($this->calls, function ($controller, $id) {
if ($controller->getCallState() === \danog\MadelineProto\VoIP::CALL_STATE_ENDED) {
$controller->discard();
}
});
return $this->calls[$call];
}
public function discard_call($call, $reason, $rating = [], $need_debug = true)
{
if (!class_exists('\danog\MadelineProto\VoIP')) {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['libtgvoip_required']);
}
if (!isset($this->calls[$call['id']])) {
return;
}
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_discarding'], $call['id'])], \danog\MadelineProto\Logger::VERBOSE);
try {
$res = $this->method_call('phone.discardCall', ['peer' => $call, 'duration' => time() - $this->calls[$call['id']]->whenCreated(), 'connection_id' => $this->calls[$call['id']]->getPreferredRelayID(), 'reason' => $reason], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -269,7 +209,8 @@ trait AuthKeyHandler
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_set_rating'], $call['id'])], \danog\MadelineProto\Logger::VERBOSE);
$this->method_call('phone.setCallRating', ['peer' => $call, 'rating' => $rating['rating'], 'comment' => $rating['comment']], ['datacenter' => $this->datacenter->curdc]);
}
if ($need_debug) {//} && isset($this->calls[$call['id']]->storage['not_modified'])) {
if ($need_debug) {
//} && isset($this->calls[$call['id']]->storage['not_modified'])) {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_debug_saving'], $call['id'])], \danog\MadelineProto\Logger::VERBOSE);
$this->method_call('phone.saveCallDebug', ['peer' => $call, 'debug' => $this->calls[$call['id']]->getDebugLog()], ['datacenter' => $this->datacenter->curdc]);
}
@ -277,7 +218,7 @@ trait AuthKeyHandler
if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict']) {
$this->pwr_update_handler($update);
} else {
in_array($this->settings['updates']['callback'], [['danog\MadelineProto\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['updates']['callback']($update);
in_array($this->settings['updates']['callback'], [['danog\\MadelineProto\\API', 'get_updates_update_handler'], 'get_updates_update_handler']) ? $this->get_updates_update_handler($update) : $this->settings['updates']['callback']($update);
}
unset($this->calls[$call['id']]);
array_walk($this->calls, function ($controller, $id) {
@ -286,4 +227,4 @@ trait AuthKeyHandler
}
});
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,28 +10,20 @@ 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\Wrappers;
trait DialogHandler
{
public function get_dialogs($force = true)
{
if ($force ||
!isset($this->dialog_params['offset_date']) || is_null($this->dialog_params['offset_date']) ||
!isset($this->dialog_params['offset_id']) || is_null($this->dialog_params['offset_id']) ||
!isset($this->dialog_params['offset_peer']) || is_null($this->dialog_params['offset_peer']) ||
!isset($this->dialog_params['count']) || is_null($this->dialog_params['count'])
) {
if ($force || !isset($this->dialog_params['offset_date']) || is_null($this->dialog_params['offset_date']) || !isset($this->dialog_params['offset_id']) || is_null($this->dialog_params['offset_id']) || !isset($this->dialog_params['offset_peer']) || is_null($this->dialog_params['offset_peer']) || !isset($this->dialog_params['count']) || is_null($this->dialog_params['count'])) {
$this->dialog_params = ['limit' => 0, 'offset_date' => 0, 'offset_id' => 0, 'offset_peer' => ['_' => 'inputPeerEmpty'], 'count' => 0];
}
$this->updates_state['sync_loading'] = true;
$res = ['dialogs' => [0], 'count' => 1];
$datacenter = $this->datacenter->curdc;
$peers = [];
$this->postpone_updates = true;
try {
while ($this->dialog_params['count'] < $res['count']) {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['getting_dialogs']]);
@ -52,7 +45,6 @@ trait DialogHandler
$this->postpone_updates = false;
$this->updates_state['sync_loading'] = false;
}
return $peers;
}
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
Copyright 2016-2018 Daniil Gentili
(https://daniil.it)
@ -9,7 +10,6 @@ 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\Wrappers;
/**
@ -30,15 +30,12 @@ trait Login
$this->chats = [];
$this->users = [];
$this->state = [];
if (!$this->method_call('auth.logOut', [], ['datacenter' => $this->datacenter->curdc])) {
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['logout_error']);
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['logout_ok']], \danog\MadelineProto\Logger::NOTICE);
return true;
}
public function bot_login($token)
{
if ($this->authorized === self::LOGGED_IN) {
@ -46,29 +43,19 @@ trait Login
$this->logout();
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_bot']], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call(
'auth.importBotAuthorization',
[
'bot_auth_token' => $token,
'api_id' => $this->settings['app_info']['api_id'],
'api_hash' => $this->settings['app_info']['api_hash'],
], ['datacenter' => $this->datacenter->curdc]
);
$this->authorization = $this->method_call('auth.importBotAuthorization', ['bot_auth_token' => $token, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash']], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN;
$this->authorized_dc = $this->datacenter->curdc;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->updates = [];
$this->updates_key = 0;
if (!isset($this->settings['pwr']['pwr']) || !$this->settings['pwr']['pwr']) {
@file_get_contents('https://api.pwrtelegram.xyz/bot'.$token.'/getme');
@file_get_contents('https://api.pwrtelegram.xyz/bot' . $token . '/getme');
}
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function phone_login($number, $sms_type = 5)
{
if ($this->authorized === self::LOGGED_IN) {
@ -76,29 +63,16 @@ trait Login
$this->logout();
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_code_sending']], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call(
'auth.sendCode',
[
'phone_number' => $number,
'sms_type' => $sms_type,
'api_id' => $this->settings['app_info']['api_id'],
'api_hash' => $this->settings['app_info']['api_hash'],
'lang_code' => $this->settings['app_info']['lang_code'],
], ['datacenter' => $this->datacenter->curdc]
);
$this->authorization = $this->method_call('auth.sendCode', ['phone_number' => $number, 'sms_type' => $sms_type, 'api_id' => $this->settings['app_info']['api_id'], 'api_hash' => $this->settings['app_info']['api_hash'], 'lang_code' => $this->settings['app_info']['lang_code']], ['datacenter' => $this->datacenter->curdc]);
$this->authorized_dc = $this->datacenter->curdc;
$this->authorization['phone_number'] = $number;
//$this->authorization['_'] .= 'MP';
$this->authorized = self::WAITING_CODE;
$this->updates = [];
$this->updates_key = 0;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_code_sent']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function complete_phone_login($code)
{
if ($this->authorized !== self::WAITING_CODE) {
@ -106,21 +80,12 @@ trait Login
}
$this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE);
try {
$authorization = $this->method_call(
'auth.signIn',
[
'phone_number' => $this->authorization['phone_number'],
'phone_code_hash' => $this->authorization['phone_code_hash'],
'phone_code' => $code,
], ['datacenter' => $this->datacenter->curdc]
);
$authorization = $this->method_call('auth.signIn', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $code], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'SESSION_PASSWORD_NEEDED') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_2fa_enabled']], \danog\MadelineProto\Logger::NOTICE);
$this->authorized = self::WAITING_PASSWORD;
$this->authorization = $this->method_call('account.getPassword', [], ['datacenter' => $this->datacenter->curdc]);
//$this->authorization['_'] .= 'MP';
return $this->authorization;
@ -129,22 +94,17 @@ trait Login
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_need_signup']], \danog\MadelineProto\Logger::NOTICE);
$this->authorized = self::WAITING_SIGNUP;
$this->authorization['phone_code'] = $code;
return ['_' => 'account.needSignup'];
}
throw $e;
}
$this->authorized = self::LOGGED_IN;
$this->authorization = $authorization;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function import_authorization($authorization)
{
if ($this->authorized === self::LOGGED_IN) {
@ -152,12 +112,10 @@ trait Login
$this->logout();
}
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_auth_key']], \danog\MadelineProto\Logger::NOTICE);
list($dc_id, $auth_key) = $authorization;
if (!is_array($auth_key)) {
$auth_key = ['auth_key' => $auth_key, 'id' => substr(sha1($auth_key, true), -8), 'server_salt' => ''];
}
$this->authorized_dc = $dc_id;
$this->datacenter->sockets[$dc_id]->session_id = $this->random(8);
$this->datacenter->sockets[$dc_id]->session_in_seq_no = 0;
@ -169,13 +127,10 @@ trait Login
$this->datacenter->sockets[$dc_id]->new_outgoing = [];
$this->datacenter->sockets[$dc_id]->new_incoming = [];
$this->datacenter->sockets[$dc_id]->authorized = true;
$this->authorized = self::LOGGED_IN;
$this->init_authorization();
return $this->get_self();
}
public function export_authorization()
{
if ($this->authorized !== self::LOGGED_IN) {
@ -183,10 +138,8 @@ trait Login
}
$this->get_self();
$this->authorized_dc = $this->datacenter->curdc;
return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']];
}
public function complete_signup($first_name, $last_name)
{
if ($this->authorized !== self::WAITING_SIGNUP) {
@ -194,25 +147,13 @@ trait Login
}
$this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['signing_up']], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call(
'auth.signUp',
[
'phone_number' => $this->authorization['phone_number'],
'phone_code_hash' => $this->authorization['phone_code_hash'],
'phone_code' => $this->authorization['phone_code'],
'first_name' => $first_name,
'last_name' => $last_name,
], ['datacenter' => $this->datacenter->curdc]
);
$this->authorization = $this->method_call('auth.signUp', ['phone_number' => $this->authorization['phone_number'], 'phone_code_hash' => $this->authorization['phone_code_hash'], 'phone_code' => $this->authorization['phone_code'], 'first_name' => $first_name, 'last_name' => $last_name], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['signup_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
public function complete_2fa_login($password)
{
if ($this->authorized !== self::WAITING_PASSWORD) {
@ -220,18 +161,11 @@ trait Login
}
$this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE);
$this->authorization = $this->method_call(
'auth.checkPassword',
[
'password_hash' => hash('sha256', $this->authorization['current_salt'].$password.$this->authorization['current_salt'], true),
], ['datacenter' => $this->datacenter->curdc]
);
$this->authorization = $this->method_call('auth.checkPassword', ['password_hash' => hash('sha256', $this->authorization['current_salt'] . $password . $this->authorization['current_salt'], true)], ['datacenter' => $this->datacenter->curdc]);
$this->authorized = self::LOGGED_IN;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization;
}
}
}