Apply fixes from StyleCI

This commit is contained in:
Daniil Gentili 2018-02-24 16:54:39 +00:00 committed by StyleCI Bot
parent 5519782618
commit d3cff5e0af
58 changed files with 1260 additions and 432 deletions

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class API extends APIFactory class API extends APIFactory
@ -17,6 +18,7 @@ class API extends APIFactory
use \danog\Serializable; use \danog\Serializable;
public $session; public $session;
public $serialized = 0; public $serialized = 0;
public function __magic_construct($params = []) public function __magic_construct($params = [])
{ {
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
@ -32,6 +34,7 @@ class API extends APIFactory
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r'); $realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
\danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']); \danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_SH); flock($realpaths['lockfile'], LOCK_SH);
try { try {
$unserialized = file_get_contents($realpaths['file']); $unserialized = file_get_contents($realpaths['file']);
} finally { } finally {
@ -40,10 +43,11 @@ class API extends APIFactory
} }
$tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $unserialized); $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) { 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('\\danog\\MadelineProto\\'.$class);
} }
class_exists('\\Volatile'); class_exists('\\Volatile');
\danog\MadelineProto\Logger::class_exists(); \danog\MadelineProto\Logger::class_exists();
try { try {
$unserialized = unserialize($tounserialize); $unserialized = unserialize($tounserialize);
} catch (\danog\MadelineProto\Bug74586Exception $e) { } catch (\danog\MadelineProto\Bug74586Exception $e) {
@ -69,6 +73,7 @@ class API extends APIFactory
$this->APIFactory(); $this->APIFactory();
} }
Serialization::$instances[spl_object_hash($unserialized)] = $unserialized; Serialization::$instances[spl_object_hash($unserialized)] = $unserialized;
return; return;
} }
$this->API = new MTProto($params); $this->API = new MTProto($params);
@ -76,18 +81,20 @@ class API extends APIFactory
$this->APIFactory(); $this->APIFactory();
\danog\MadelineProto\Logger::log(['Ping...'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Ping...'], Logger::ULTRA_VERBOSE);
$pong = $this->ping(['ping_id' => 3]); $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); //\danog\MadelineProto\Logger::log(['Getting future salts...'], Logger::ULTRA_VERBOSE);
//$this->future_salts = $this->get_future_salts(['num' => 3]); //$this->future_salts = $this->get_future_salts(['num' => 3]);
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['madelineproto_ready']], Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['madelineproto_ready']], Logger::NOTICE);
Serialization::$instances[spl_object_hash($this)] = $this; Serialization::$instances[spl_object_hash($this)] = $this;
} }
public function __wakeup() public function __wakeup()
{ {
//if (method_exists($this->API, 'wakeup')) $this->API = $this->API->wakeup(); //if (method_exists($this->API, 'wakeup')) $this->API = $this->API->wakeup();
Serialization::$instances[spl_object_hash($this)] = $this; Serialization::$instances[spl_object_hash($this)] = $this;
$this->APIFactory(); $this->APIFactory();
} }
public function __destruct() public function __destruct()
{ {
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) { if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
@ -98,45 +105,56 @@ class API extends APIFactory
} }
restore_error_handler(); restore_error_handler();
} }
public function __sleep() public function __sleep()
{ {
return ['API']; return ['API'];
} }
public function &__get($name) public function &__get($name)
{ {
if ($name === 'settings') { if ($name === 'settings') {
$this->API->setdem = true; $this->API->setdem = true;
return $this->API->settings; return $this->API->settings;
} }
return $this->API->storage[$name]; return $this->API->storage[$name];
} }
public function __set($name, $value) public function __set($name, $value)
{ {
if ($name === 'settings') { if ($name === 'settings') {
return $this->API->__construct($value); return $this->API->__construct($value);
} }
return $this->API->storage[$name] = $value; return $this->API->storage[$name] = $value;
} }
public function __isset($name) public function __isset($name)
{ {
return isset($this->API->storage[$name]); return isset($this->API->storage[$name]);
} }
public function __unset($name) public function __unset($name)
{ {
unset($this->API->storage[$name]); unset($this->API->storage[$name]);
} }
public function APIFactory() public function APIFactory()
{ {
foreach ($this->API->get_method_namespaces() as $namespace) { foreach ($this->API->get_method_namespaces() as $namespace) {
$this->{$namespace} = new APIFactory($namespace, $this->API); $this->{$namespace} = new APIFactory($namespace, $this->API);
} }
} }
public function serialize($params = '') public function serialize($params = '')
{ {
if ($params === '') { if ($params === '') {
$params = $this->session; $params = $this->session;
} }
Logger::log([\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']]); Logger::log([\danog\MadelineProto\Lang::$current_lang['serializing_madelineproto']]);
return Serialization::serialize($params, $this); return Serialization::serialize($params, $this);
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class APIFactory class APIFactory
@ -108,11 +109,13 @@ class APIFactory
public $namespace = ''; public $namespace = '';
public $API; public $API;
public $lua = false; public $lua = false;
public function __construct($namespace, $API) public function __construct($namespace, $API)
{ {
$this->namespace = $namespace . '.'; $this->namespace = $namespace.'.';
$this->API = $API; $this->API = $API;
} }
public function __call($name, $arguments) public function __call($name, $arguments)
{ {
if ($this->API->setdem) { if ($this->API->setdem) {
@ -127,11 +130,13 @@ class APIFactory
$this->serialize($this->session); $this->serialize($this->session);
} }
if ($this->lua === false) { 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 { 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); Lua::convert_objects($deserialized);
return $deserialized; return $deserialized;
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
@ -149,4 +154,4 @@ class APIFactory
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
} }
} }
} }

View File

@ -10,24 +10,29 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
use phpDocumentor\Reflection\DocBlockFactory; use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder class AnnotationsBuilder
{ {
use \danog\MadelineProto\TL\TL; use \danog\MadelineProto\TL\TL;
use Tools; use Tools;
public function __construct($settings) public function __construct($settings)
{ {
$this->construct_TL($settings['tl_schema']); $this->construct_TL($settings['tl_schema']);
$this->settings = $settings; $this->settings = $settings;
} }
public function mk_annotations() public function mk_annotations()
{ {
\danog\MadelineProto\Logger::log(['Generating annotations...'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Generating annotations...'], \danog\MadelineProto\Logger::NOTICE);
$this->setProperties(); $this->setProperties();
$this->createInternalClasses(); $this->createInternalClasses();
} }
/** /**
* Open file of class APIFactory * Open file of class APIFactory
* Insert properties * Insert properties
@ -43,22 +48,23 @@ class AnnotationsBuilder
if ($raw_docblock = $property->getDocComment()) { if ($raw_docblock = $property->getDocComment()) {
$docblock = $fixture->create($raw_docblock); $docblock = $fixture->create($raw_docblock);
if ($docblock->hasTag('internal')) { 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) { 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); file_put_contents($filename, $content);
} }
/** /**
* Create file InternalDoc with all interfaces. * Create file InternalDoc with all interfaces.
*/ */
private function createInternalClasses() private function createInternalClasses()
{ {
\danog\MadelineProto\Logger::log(['Creating internal classes...'], \danog\MadelineProto\Logger::NOTICE); \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) { foreach ($this->methods->by_id as $id => $data) {
if (!strpos($data['method'], '.')) { if (!strpos($data['method'], '.')) {
continue; continue;
@ -120,4 +126,4 @@ class AnnotationsBuilder
} }
fclose($handle); fclose($handle);
} }
} }

View File

@ -10,8 +10,9 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class Bug74586Exception extends \Exception class Bug74586Exception extends \Exception
{ {
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
/** /**
@ -172,6 +173,7 @@ class Connection
break; break;
} }
} }
public function __destruct() public function __destruct()
{ {
switch ($this->protocol) { switch ($this->protocol) {
@ -194,15 +196,18 @@ class Connection
break; break;
} }
} }
public function close_and_reopen() public function close_and_reopen()
{ {
$this->__destruct(); $this->__destruct();
$this->must_open = true; $this->must_open = true;
} }
public function __sleep() 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']; 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() public function __wakeup()
{ {
$keys = array_keys((array) get_object_vars($this)); $keys = array_keys((array) get_object_vars($this));
@ -212,6 +217,7 @@ class Connection
$this->time_delta = 0; $this->time_delta = 0;
//$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6); //$this->__construct($this->proxy, $this->extra, $this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
} }
public function write($what, $length = null) public function write($what, $length = null)
{ {
if ($this->must_open) { if ($this->must_open) {
@ -237,6 +243,7 @@ class Connection
while (($wrote += $this->sock->write(substr($what, $wrote))) !== $length) { while (($wrote += $this->sock->write(substr($what, $wrote))) !== $length) {
} }
} }
return $wrote; return $wrote;
break; break;
case 'udp': case 'udp':
@ -247,6 +254,7 @@ class Connection
break; break;
} }
} }
public function read($length) public function read($length)
{ {
if ($this->must_open) { if ($this->must_open) {
@ -264,8 +272,10 @@ class Connection
} }
if (strlen($packet) !== $length) { if (strlen($packet) !== $length) {
$this->close_and_reopen(); $this->close_and_reopen();
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet))); throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet)));
} }
return @$this->obfuscated['decryption']->encrypt($packet); return @$this->obfuscated['decryption']->encrypt($packet);
case 'tcp_abridged': case 'tcp_abridged':
case 'tcp_intermediate': case 'tcp_intermediate':
@ -282,8 +292,10 @@ class Connection
} }
if (strlen($packet) !== $length) { if (strlen($packet) !== $length) {
$this->close_and_reopen(); $this->close_and_reopen();
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet))); throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['wrong_length_read'], $length, strlen($packet)));
} }
return $packet; return $packet;
case 'udp': case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
@ -292,6 +304,7 @@ class Connection
break; break;
} }
} }
public function read_message() public function read_message()
{ {
switch ($this->protocol) { switch ($this->protocol) {
@ -299,7 +312,7 @@ class Connection
$packet_length_data = $this->read(4); $packet_length_data = $this->read(4);
$packet_length = unpack('V', $packet_length_data)[1]; $packet_length = unpack('V', $packet_length_data)[1];
$packet = $this->read($packet_length - 4); $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!'); throw new Exception('CRC32 was not correct!');
} }
$this->in_seq_no++; $this->in_seq_no++;
@ -307,13 +320,15 @@ class Connection
if ($in_seq_no != $this->in_seq_no) { if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch'); throw new Exception('Incoming seq_no mismatch');
} }
return substr($packet, 4, $packet_length - 12); return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate': case 'tcp_intermediate':
return $this->read(unpack('V', $this->read(4))[1]); return $this->read(unpack('V', $this->read(4))[1]);
case 'obfuscated2': case 'obfuscated2':
case 'tcp_abridged': case 'tcp_abridged':
$packet_length = ord($this->read(1)); $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 'http':
case 'https': case 'https':
case 'https_proxied': case 'https_proxied':
@ -328,37 +343,39 @@ class Connection
if ($close) { if ($close) {
$this->close_and_reopen(); $this->close_and_reopen();
} }
return $response['body']; return $response['body'];
case 'udp': case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
} }
} }
public function send_message($message) public function send_message($message)
{ {
switch ($this->protocol) { switch ($this->protocol) {
case 'tcp_full': case 'tcp_full':
$this->out_seq_no++; $this->out_seq_no++;
$step1 = pack('VV', strlen($message) + 12, $this->out_seq_no) . $message; $step1 = pack('VV', strlen($message) + 12, $this->out_seq_no).$message;
$step2 = $step1 . strrev(hash('crc32b', $step1, true)); $step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2); $this->write($step2);
break; break;
case 'tcp_intermediate': case 'tcp_intermediate':
$this->write(pack('V', strlen($message)) . $message); $this->write(pack('V', strlen($message)).$message);
break; break;
case 'obfuscated2': case 'obfuscated2':
case 'tcp_abridged': case 'tcp_abridged':
$len = strlen($message) / 4; $len = strlen($message) / 4;
if ($len < 127) { if ($len < 127) {
$message = chr($len) . $message; $message = chr($len).$message;
} else { } else {
$message = chr(127) . substr(pack('V', $len), 0, 3) . $message; $message = chr(127).substr(pack('V', $len), 0, 3).$message;
} }
$this->write($message); $this->write($message);
break; break;
case 'http': case 'http':
case 'https': case 'https':
case 'https_proxied': 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); $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; break;
case 'udp': case 'udp':
throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['protocol_not_implemented']);
@ -370,35 +387,32 @@ class Connection
public function read_http_line() public function read_http_line()
{ {
$line = ''; $line = '';
while (($curchar = $this->read(1)) !== "\n") while (($curchar = $this->read(1)) !== "\n") {
{ $line .= $curchar;
$line.= $curchar;
} }
return rtrim($line); return rtrim($line);
} }
function read_http_payload() public function read_http_payload()
{ {
$header = explode(' ', $this->read_http_line() , 3); $header = explode(' ', $this->read_http_line(), 3);
$protocol = $header[0]; $protocol = $header[0];
$code = (int)$header[1]; $code = (int) $header[1];
$description = $header[2]; $description = $header[2];
$headers = []; $headers = [];
while (strlen($current_header = $this->read_http_line())) { while (strlen($current_header = $this->read_http_line())) {
$current_header = explode(':', $current_header, 2); $current_header = explode(':', $current_header, 2);
$headers[strtolower($current_header[0]) ] = trim($current_header[1]); $headers[strtolower($current_header[0])] = trim($current_header[1]);
} }
$read = ''; $read = '';
if (isset($headers['content-length'])) if (isset($headers['content-length'])) {
{ $read = $this->read((int) $headers['content-length']);
$read = $this->read((int)$headers['content-length']);
} elseif (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] === 'chunked') { } elseif (isset($headers['transfer-encoding']) && $headers['transfer-encoding'] === 'chunked') {
do do {
{
$length = hexdec($this->read_http_line($res)); $length = hexdec($this->read_http_line($res));
$read.= $this->read($length); $read .= $this->read($length);
$this->read_http_line($res); $this->read_http_line($res);
} while ($length); } while ($length);
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
/** /**
@ -23,10 +24,12 @@ class DataCenter
public $curdc = 0; public $curdc = 0;
private $dclist = []; private $dclist = [];
private $settings = []; private $settings = [];
public function __sleep() public function __sleep()
{ {
return ['sockets', 'curdc', 'dclist', 'settings']; return ['sockets', 'curdc', 'dclist', 'settings'];
} }
public function __magic_construct($dclist, $settings) public function __magic_construct($dclist, $settings)
{ {
$this->dclist = $dclist; $this->dclist = $dclist;
@ -41,6 +44,7 @@ class DataCenter
} }
} }
} }
public function dc_disconnect($dc_number) public function dc_disconnect($dc_number)
{ {
if ($this->curdc === $dc_number) { if ($this->curdc === $dc_number) {
@ -51,6 +55,7 @@ class DataCenter
unset($this->sockets[$dc_number]); unset($this->sockets[$dc_number]);
} }
} }
public function dc_connect($dc_number) public function dc_connect($dc_number)
{ {
if (isset($this->sockets[$dc_number]) && !isset($this->sockets[$dc_number]->old)) { if (isset($this->sockets[$dc_number]) && !isset($this->sockets[$dc_number]->old)) {
@ -63,6 +68,7 @@ class DataCenter
$ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4'; $ipv6 = $this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4';
if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) { if (!isset($this->dclist[$test][$ipv6][$dc_number]['ip_address'])) {
unset($this->sockets[$dc_number]); unset($this->sockets[$dc_number]);
return false; return false;
} }
$address = $this->dclist[$test][$ipv6][$dc_number]['ip_address']; $address = $this->dclist[$test][$ipv6][$dc_number]['ip_address'];
@ -79,18 +85,19 @@ class DataCenter
if (strpos($this->settings[$dc_config_number]['protocol'], 'https') === 0) { if (strpos($this->settings[$dc_config_number]['protocol'], 'https') === 0) {
$subdomain = $this->dclist['ssl_subdomains'][$dc_number]; $subdomain = $this->dclist['ssl_subdomains'][$dc_number];
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1'; $path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
$address = 'https://' . $subdomain . '.web.telegram.org/' . $path; $address = 'https://'.$subdomain.'.web.telegram.org/'.$path;
$port = 443; $port = 443;
} }
if ($this->settings[$dc_config_number]['protocol'] === 'http') { if ($this->settings[$dc_config_number]['protocol'] === 'http') {
if ($ipv6) { 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); \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) { 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 { try {
if (isset($this->sockets[$dc_number]->old)) { 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']); $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']);
@ -99,6 +106,7 @@ 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']); $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); \danog\MadelineProto\Logger::log(['OK!'], \danog\MadelineProto\Logger::WARNING);
return true; return true;
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
} catch (\danog\MadelineProto\NothingInTheSocketException $e) { } catch (\danog\MadelineProto\NothingInTheSocketException $e) {
@ -107,26 +115,29 @@ class DataCenter
switch ($x) { switch ($x) {
case 0: case 0:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6']; $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); \danog\MadelineProto\Logger::log(['Connection failed, retrying connection with '.($this->settings[$dc_config_number]['ipv6'] ? 'ipv6' : 'ipv4').'...'], \danog\MadelineProto\Logger::WARNING);
continue; continue;
case 1: case 1:
$this->settings[$dc_config_number]['proxy'] = '\\Socket'; $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); \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; continue;
case 2: case 2:
$this->settings[$dc_config_number]['ipv6'] = !$this->settings[$dc_config_number]['ipv6']; $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); \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; continue;
default: default:
return false; return false;
} }
} while (++$x); } while (++$x);
return false; return false;
} }
public function get_dcs($all = true) public function get_dcs($all = true)
{ {
$test = $this->settings['all']['test_mode'] ? 'test' : 'main'; $test = $this->settings['all']['test_mode'] ? 'test' : 'main';
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4'; $ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets); return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class DocsBuilder class DocsBuilder
@ -19,6 +20,7 @@ class DocsBuilder
use \danog\MadelineProto\DocsBuilder\Constructors; use \danog\MadelineProto\DocsBuilder\Constructors;
use Tools; use Tools;
public $td = false; public $td = false;
public function __construct($settings) public function __construct($settings)
{ {
set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']);
@ -35,24 +37,28 @@ class DocsBuilder
chdir($this->settings['output_dir']); chdir($this->settings['output_dir']);
$this->index = $settings['readme'] ? 'README.md' : 'index.md'; $this->index = $settings['readme'] ? 'README.md' : 'index.md';
} }
public $types = []; public $types = [];
public $any = '*'; public $any = '*';
public function end($what) public function end($what)
{ {
return end($what); return end($what);
} }
public function escape($hwat) public function escape($hwat)
{ {
return str_replace('_', '\\_', $hwat); return str_replace('_', '\\_', $hwat);
} }
public function mk_docs() public function mk_docs()
{ {
\danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents($this->index, '--- file_put_contents($this->index, '---
title: ' . $this->settings['title'] . ' title: '.$this->settings['title'].'
description: ' . $this->settings['description'] . ' description: '.$this->settings['description'].'
--- ---
# ' . $this->settings['description'] . ' # '.$this->settings['description'].'
[Methods](methods/) [Methods](methods/)
@ -81,14 +87,14 @@ description: ' . $this->settings['description'] . '
//$br = $new_namespace != $last_namespace ? '***<br><br>' : ''; //$br = $new_namespace != $last_namespace ? '***<br><br>' : '';
$type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $otype); $type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $otype);
$type = preg_replace('/.*_of_/', '', $type); $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 = ''; $constructors = '';
foreach ($keys['constructors'] as $data) { foreach ($keys['constructors'] as $data) {
$predicate = str_replace('.', '_', $data['predicate']) . (isset($data['layer']) && $data['layer'] !== '' ? '_' . $data['layer'] : ''); $predicate = str_replace('.', '_', $data['predicate']).(isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '');
$md_predicate = str_replace('_', '\\_', $predicate); $md_predicate = str_replace('_', '\\_', $predicate);
$constructors .= '[' . $md_predicate . '](../constructors/' . $predicate . '.md) $constructors .= '['.$md_predicate.'](../constructors/'.$predicate.'.md)
'; ';
} }
@ -96,38 +102,38 @@ description: ' . $this->settings['description'] . '
foreach ($keys['methods'] as $data) { foreach ($keys['methods'] as $data) {
$name = str_replace('.', '_', $data['method']); $name = str_replace('.', '_', $data['method']);
$md_name = str_replace('_', '->', $name); $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 = '--- $header = '---
title: ' . $type . ' title: '.$type.'
description: constructors and methods of type ' . $type . ' description: constructors and methods of type '.$type.'
--- ---
## Type: ' . str_replace('_', '\\_', $type) . ' ## Type: '.str_replace('_', '\\_', $type).'
[Back to types index](index.md) [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 (!isset($this->settings['td'])) {
if (in_array($type, ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) { if (in_array($type, ['User', 'InputUser', 'Chat', 'InputChannel', 'Peer', 'InputPeer'])) {
$header .= 'The following syntaxes can also be used: $header .= 'The following syntaxes can also be used:
``` ```
$' . $type . " = '@username'; // Username\n\n\$" . $type . ' = 44700; // bot API id (users) $'.$type." = '@username'; // Username\n\n\$".$type.' = 44700; // bot API id (users)
$' . $type . ' = -492772765; // bot API id (chats) $'.$type.' = -492772765; // bot API id (chats)
$' . $type . ' = -10038575794; // bot API id (channels) $'.$type.' = -10038575794; // bot API id (channels)
$' . $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"; $'.$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'])) { if (in_array($type, ['InputEncryptedChat'])) {
$header .= 'The following syntax can also be used: $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
``` ```
@ -139,7 +145,7 @@ $' . $type . ' = -147286699; // Numeric chat id returned by request_secret_chat,
To click these buttons simply run the `click` method: To click these buttons simply run the `click` method:
``` ```
$result = $' . $type . '->click(); $result = $'.$type.'->click();
``` ```
`$result` can be one of the following: `$result` can be one of the following:
@ -159,12 +165,12 @@ $result = $' . $type . '->click();
} }
$constructors = '### Possible values (constructors): $constructors = '### Possible values (constructors):
' . $constructors . ' '.$constructors.'
'; ';
$methods = '### Methods that return an object of this type (methods): $methods = '### Methods that return an object of this type (methods):
' . $methods . ' '.$methods.'
'; ';
if (!isset($this->settings['td'])) { if (!isset($this->settings['td'])) {
@ -351,14 +357,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]); \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; $last_namespace = $new_namespace;
} }
\danog\MadelineProto\Logger::log(['Generating types index...'], \danog\MadelineProto\Logger::NOTICE); \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 title: Types
description: List of types description: List of types
--- ---
@ -366,7 +372,7 @@ description: List of types
[Back to API documentation index](..) [Back to API documentation index](..)
' . $index); '.$index);
\danog\MadelineProto\Logger::log(['Generating additional types...'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Generating additional types...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents('types/string.md', '--- file_put_contents('types/string.md', '---
title: string title: string
@ -514,4 +520,4 @@ Any json-encodable data.
'); ');
\danog\MadelineProto\Logger::log(['Done!'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Done!'], \danog\MadelineProto\Logger::NOTICE);
} }
} }

View File

@ -10,13 +10,14 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\DocsBuilder; namespace danog\MadelineProto\DocsBuilder;
trait Constructors trait Constructors
{ {
public function mk_constructors() public function mk_constructors()
{ {
foreach (glob('constructors/' . $this->any) as $unlink) { foreach (glob('constructors/'.$this->any) as $unlink) {
unlink($unlink); unlink($unlink);
} }
if (file_exists('constructors')) { if (file_exists('constructors')) {
@ -36,7 +37,7 @@ trait Constructors
if (preg_match('/%/', $type)) { if (preg_match('/%/', $type)) {
$type = $this->constructors->find_by_type(str_replace('%', '', $type))['predicate']; $type = $this->constructors->find_by_type(str_replace('%', '', $type))['predicate'];
}*/ }*/
$layer = isset($data['layer']) && $data['layer'] !== '' ? '_' . $data['layer'] : ''; $layer = isset($data['layer']) && $data['layer'] !== '' ? '_'.$data['layer'] : '';
$type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']); $type = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['type']);
$php_type = preg_replace('/.*_of_/', '', $type); $php_type = preg_replace('/.*_of_/', '', $type);
$constructor = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['predicate']); $constructor = str_replace(['.', '<', '>'], ['_', '_of_', ''], $data['predicate']);
@ -65,12 +66,12 @@ trait Constructors
if (substr($param[$type_or_subtype], -1) === '>') { if (substr($param[$type_or_subtype], -1) === '>') {
$param[$type_or_subtype] = substr($param[$type_or_subtype], 0, -1); $param[$type_or_subtype] = substr($param[$type_or_subtype], 0, -1);
} }
$params .= "'" . $param['name'] . "' => "; $params .= "'".$param['name']."' => ";
$param[$type_or_subtype] = '[' . $this->escape($param[$type_or_subtype]) . '](../' . $type_or_bare_type . '/' . $param[$type_or_subtype] . '.md)'; $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 .= (isset($param['subtype']) ? '\\['.$param[$type_or_subtype].'\\]' : $param[$type_or_subtype]).', ';
} }
$md_constructor = str_replace('_', '\\_', $constructor . $layer); $md_constructor = str_replace('_', '\\_', $constructor.$layer);
$this->docs_constructors[$constructor] = '[$' . $md_constructor . '](../constructors/' . $php_constructor . $layer . '.md) = \\[' . $params . '\\];<a name="' . $constructor . $layer . '"></a> $this->docs_constructors[$constructor] = '[$'.$md_constructor.'](../constructors/'.$php_constructor.$layer.'.md) = \\['.$params.'\\];<a name="'.$constructor.$layer.'"></a>
'; ';
$table = empty($data['params']) ? '' : '### Attributes: $table = empty($data['params']) ? '' : '### Attributes:
@ -117,31 +118,31 @@ trait Constructors
case 'false': case 'false':
$ptype = 'Bool'; $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']])) { 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; $table .= PHP_EOL;
$pptype = in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype; $pptype = in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"' . $ptype . '"' : $ptype; $ppptype = in_array($ptype, ['string', 'bytes']) ? '"'.$ptype.'"' : $ptype;
$params .= ", '" . $param['name'] . "' => "; $params .= ", '".$param['name']."' => ";
$params .= isset($param['subtype']) ? '[' . $pptype . ']' : $pptype; $params .= isset($param['subtype']) ? '['.$pptype.']' : $pptype;
$lua_params .= ', ' . $param['name'] . '='; $lua_params .= ', '.$param['name'].'=';
$lua_params .= isset($param['subtype']) ? '{' . $pptype . '}' : $pptype; $lua_params .= isset($param['subtype']) ? '{'.$pptype.'}' : $pptype;
$pwr_params .= ', "' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype); $pwr_params .= ', "'.$param['name'].'": '.(isset($param['subtype']) ? '['.$ppptype.']' : $ppptype);
if ($param['name'] === 'reply_markup') { if ($param['name'] === 'reply_markup') {
$hasreplymarkup = true; $hasreplymarkup = true;
} }
} }
$params = "['_' => '" . $data['predicate'] . "'" . $params . ']'; $params = "['_' => '".$data['predicate']."'".$params.']';
$lua_params = "{_='" . $data['predicate'] . "'" . $lua_params . '}'; $lua_params = "{_='".$data['predicate']."'".$lua_params.'}';
$pwr_params = '{"_": "' . $data['predicate'] . '"' . $pwr_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'; $description = isset($this->td_descriptions['constructors'][$data['predicate']]) ? $this->td_descriptions['constructors'][$data['predicate']]['description'] : $constructor.' attributes, type and example';
$header = '--- $header = '---
title: ' . $data['predicate'] . ' title: '.$data['predicate'].'
description: ' . $description . ' description: '.$description.'
--- ---
## Constructor: ' . str_replace('_', '\\_', $data['predicate'] . $layer) . ' ## Constructor: '.str_replace('_', '\\_', $data['predicate'].$layer).'
[Back to constructors index](index.md) [Back to constructors index](index.md)
@ -152,9 +153,9 @@ description: ' . $description . '
'; ';
if (isset($this->td_descriptions['constructors'][$data['predicate']])) { 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)
'; ';
@ -163,13 +164,13 @@ description: ' . $description . '
$example = '### Example: $example = '### Example:
``` ```
$' . $constructor . $layer . ' = ' . $params . '; $'.$constructor.$layer.' = '.$params.';
``` ```
[PWRTelegram](https://pwrtelegram.xyz) json-encoded version: [PWRTelegram](https://pwrtelegram.xyz) json-encoded version:
``` ```
' . $pwr_params . ' '.$pwr_params.'
``` ```
@ -177,7 +178,7 @@ Or, if you\'re into Lua:
``` ```
' . $constructor . $layer . '=' . $lua_params . ' '.$constructor.$layer.'='.$lua_params.'
``` ```
@ -227,7 +228,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); \danog\MadelineProto\Logger::log(['Generating constructors index...'], \danog\MadelineProto\Logger::NOTICE);
ksort($this->docs_constructors); ksort($this->docs_constructors);
@ -236,16 +237,16 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
$new_namespace = preg_replace('/_.*/', '', $constructor); $new_namespace = preg_replace('/_.*/', '', $constructor);
$br = $new_namespace != $last_namespace ? '*** $br = $new_namespace != $last_namespace ? '***
<br><br>' : ''; <br><br>' : '';
$value = $br . $value; $value = $br.$value;
$last_namespace = $new_namespace; $last_namespace = $new_namespace;
} }
file_put_contents('constructors/' . $this->index, '--- file_put_contents('constructors/'.$this->index, '---
title: Constructors title: Constructors
description: List of constructors description: List of constructors
--- ---
# Constructors # Constructors
[Back to API documentation index](..) [Back to API documentation index](..)
' . implode('', $this->docs_constructors)); '.implode('', $this->docs_constructors));
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\DocsBuilder; namespace danog\MadelineProto\DocsBuilder;
trait Methods trait Methods
@ -19,7 +20,7 @@ trait Methods
$bots = json_decode(file_get_contents('https://rpc.pwrtelegram.xyz/?bot'), true)['result']; $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 = json_decode(file_get_contents('https://rpc.pwrtelegram.xyz/?all'), true);
$errors['result'] = array_merge_recursive(...$errors['result']); $errors['result'] = array_merge_recursive(...$errors['result']);
foreach (glob('methods/' . $this->any) as $unlink) { foreach (glob('methods/'.$this->any) as $unlink) {
unlink($unlink); unlink($unlink);
} }
if (file_exists('methods')) { if (file_exists('methods')) {
@ -54,11 +55,11 @@ trait Methods
$type_or_subtype = isset($param['subtype']) ? 'subtype' : 'type'; $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] = 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)'; $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]) . ', '; $params .= "'".$param['name']."' => ".(isset($param['subtype']) ? '\\['.$param[$type_or_subtype].'\\]' : $param[$type_or_subtype]).', ';
} }
$md_method = '[' . $php_method . '](' . $method . '.md)'; $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> $this->docs_methods[$method] = '$MadelineProto->'.$md_method.'(\\['.$params.'\\]) === [$'.str_replace('_', '\\_', $type).'](../types/'.$php_type.'.md)<a name="'.$method.'"></a>
'; ';
$params = ''; $params = '';
@ -98,19 +99,19 @@ trait Methods
$ptype = 'Bool'; $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'; $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') . '|'; $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']])) { 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; $table .= PHP_EOL;
$pptype = in_array($ptype, ['string', 'bytes']) ? "'" . $ptype . "'" : $ptype; $pptype = in_array($ptype, ['string', 'bytes']) ? "'".$ptype."'" : $ptype;
$ppptype = in_array($ptype, ['string', 'bytes']) ? '"' . $ptype . '"' : $ptype; $ppptype = in_array($ptype, ['string', 'bytes']) ? '"'.$ptype.'"' : $ptype;
$params .= "'" . $param['name'] . "' => "; $params .= "'".$param['name']."' => ";
$params .= (isset($param['subtype']) ? '[' . $pptype . ']' : $pptype) . ', '; $params .= (isset($param['subtype']) ? '['.$pptype.']' : $pptype).', ';
$json_params .= '"' . $param['name'] . '": ' . (isset($param['subtype']) ? '[' . $ppptype . ']' : $ppptype) . ', '; $json_params .= '"'.$param['name'].'": '.(isset($param['subtype']) ? '['.$ppptype.']' : $ppptype).', ';
$pwr_params .= $param['name'] . ' - Json encoded ' . (isset($param['subtype']) ? ' array of ' . $ptype : $ptype) . "\n\n"; $pwr_params .= $param['name'].' - Json encoded '.(isset($param['subtype']) ? ' array of '.$ptype : $ptype)."\n\n";
$lua_params .= $param['name'] . '='; $lua_params .= $param['name'].'=';
$lua_params .= (isset($param['subtype']) ? '{' . $pptype . '}' : $pptype) . ', '; $lua_params .= (isset($param['subtype']) ? '{'.$pptype.'}' : $pptype).', ';
if ($param['name'] === 'reply_markup') { if ($param['name'] === 'reply_markup') {
$hasreplymarkup = true; $hasreplymarkup = true;
} }
@ -127,19 +128,19 @@ trait Methods
$pwr_params = "parse_mode - string\n"; $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 = '--- $header = '---
title: ' . $data['method'] . ' title: '.$data['method'].'
description: ' . $description . ' description: '.$description.'
--- ---
## Method: ' . str_replace('_', '\\_', $data['method']) . ' ## Method: '.str_replace('_', '\\_', $data['method']).'
[Back to methods index](index.md) [Back to methods index](index.md)
'; ';
if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']])) { if (isset(\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']])) {
$header .= '**' . \danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']] . "**\n\n\n\n\n"; $header .= '**'.\danog\MadelineProto\MTProto::DISALLOWED_METHODS[$data['method']]."**\n\n\n\n\n";
file_put_contents('methods/' . $method . '.md', $header); file_put_contents('methods/'.$method.'.md', $header);
continue; continue;
} }
if ($this->td) { if ($this->td) {
@ -148,17 +149,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 .= ' $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); $bot = !in_array($data['method'], $bots);
$example = ''; $example = '';
if (!isset($this->settings['td'])) { 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']])) { if (isset($errors['result'][$data['method']])) {
$example .= '### Errors this method can return: $example .= '### Errors this method can return:
@ -166,7 +167,7 @@ description: ' . $description . '
|----------|---------------| |----------|---------------|
'; ';
foreach ($errors['result'][$data['method']] as $error) { 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"; $example .= "\n\n";
} }
@ -176,44 +177,44 @@ description: ' . $description . '
``` ```
$MadelineProto = new \\danog\\MadelineProto\\API(); $MadelineProto = new \\danog\\MadelineProto\\API();
$MadelineProto->session = \'mySession.madeline\'; $MadelineProto->session = \'mySession.madeline\';
' . ($bot ? 'if (isset($token)) { // Login as a bot '.($bot ? 'if (isset($token)) { // Login as a bot
$MadelineProto->bot_login($token); $MadelineProto->bot_login($token);
} }
' : '') . 'if (isset($number)) { // Login as a user ' : '').'if (isset($number)) { // Login as a user
$MadelineProto->phone_login($number); $MadelineProto->phone_login($number);
$code = readline(\'Enter the code you received: \'); // Or do this in two separate steps in an HTTP API $code = readline(\'Enter the code you received: \'); // Or do this in two separate steps in an HTTP API
$MadelineProto->complete_phone_login($code); $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): 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` POST/GET to `https://api.pwrtelegram.xyz/botTOKEN/madeline`
Parameters: Parameters:
* method - ' . $data['method'] . ' * method - '.$data['method'].'
* params - `{' . $json_params . '}` * params - `{'.$json_params.'}`
' : '') . ' ' : '').'
### As a user: ### As a user:
POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/' . $data['method'] . '` POST/GET to `https://api.pwrtelegram.xyz/userTOKEN/'.$data['method'].'`
Parameters: Parameters:
' . $pwr_params . ' '.$pwr_params.'
Or, if you\'re into Lua: Or, if you\'re into Lua:
``` ```
' . $type . ' = ' . $data['method'] . '({' . $lua_params . '}) '.$type.' = '.$data['method'].'({'.$lua_params.'})
``` ```
'); ');
@ -230,7 +231,7 @@ You can provide bot API reply_markup objects here.
$example .= ' $example .= '
## Return value ## 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.
'; ';
@ -270,7 +271,7 @@ 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); \danog\MadelineProto\Logger::log(['Generating methods index...'], \danog\MadelineProto\Logger::NOTICE);
ksort($this->docs_methods); ksort($this->docs_methods);
@ -279,10 +280,10 @@ MadelineProto supports all html entities supported by [html_entity_decode](http:
$new_namespace = preg_replace('/_.*/', '', $method); $new_namespace = preg_replace('/_.*/', '', $method);
$br = $new_namespace != $last_namespace ? '*** $br = $new_namespace != $last_namespace ? '***
<br><br>' : ''; <br><br>' : '';
$value = $br . $value; $value = $br.$value;
$last_namespace = $new_namespace; $last_namespace = $new_namespace;
} }
file_put_contents('methods/' . $this->index, '--- file_put_contents('methods/'.$this->index, '---
title: Methods title: Methods
description: List of methods description: List of methods
--- ---
@ -312,6 +313,6 @@ $MadelineProto->[get_full_info](https://docs.madelineproto.xyz/get_full_info.htm
$MadelineProto->[get_self](https://docs.madelineproto.xyz/get_self.html)(); $MadelineProto->[get_self](https://docs.madelineproto.xyz/get_self.html)();
' . implode('', $this->docs_methods)); '.implode('', $this->docs_methods));
} }
} }

View File

@ -10,16 +10,19 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class Exception extends \Exception class Exception extends \Exception
{ {
use TL\PrettyException; use TL\PrettyException;
public static $rollbar = true; public static $rollbar = true;
public function __toString() 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) public function __construct($message = null, $code = 0, self $previous = null, $file = null, $line = null)
{ {
$this->prettify_tl(); $this->prettify_tl();
@ -34,7 +37,7 @@ class Exception extends \Exception
$this->line = $line; $this->line = $line;
} }
parent::__construct($message, $code, $previous); 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'])) { 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; return;
} }
@ -45,6 +48,7 @@ class Exception extends \Exception
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0)); \Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));
} }
} }
/** /**
* ExceptionErrorHandler. * ExceptionErrorHandler.
* *
@ -57,6 +61,7 @@ class Exception extends \Exception
return true; return true;
// return true to continue through the others error handlers // return true to continue through the others error handlers
} }
throw new self($errstr, $errno, null, $errfile, $errline); 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

@ -13,6 +13,7 @@ If not, see <http://www.gnu.org/licenses/>.
/* /*
* Logger class * Logger class
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class Logger class Logger
@ -37,6 +38,7 @@ class Logger
const WARNING = 2; const WARNING = 2;
const ERROR = 1; const ERROR = 1;
const FATAL_ERROR = 0; const FATAL_ERROR = 0;
public static function class_exists() public static function class_exists()
{ {
self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread'); self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread');
@ -50,6 +52,7 @@ class Logger
if (!defined('\\danog\\MadelineProto\\VoIP::PHP_LIBTGVOIP_VERSION') || \danog\MadelineProto\VoIP::PHP_LIBTGVOIP_VERSION !== '1.1.2') { 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); throw new \danog\MadelineProto\Exception(hex2bin(\danog\MadelineProto\Lang::$current_lang['v_tgerror']), 0, null, 'MadelineProto', 1);
} }
try { try {
\Threaded::extend('\\danog\\MadelineProto\\VoIP'); \Threaded::extend('\\danog\\MadelineProto\\VoIP');
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
@ -61,11 +64,13 @@ class Logger
self::$colors[self::WARNING] = implode(';', [self::foreground['white'], self::set['dim'], self::background['red']]); 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::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']]); self::$colors[self::FATAL_ERROR] = implode(';', [self::foreground['red'], self::set['bold'], self::background['light_gray']]);
try { try {
self::$isatty = defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT); self::$isatty = defined('STDOUT') && function_exists('posix_isatty') && posix_isatty(STDOUT);
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
} }
} }
/* /*
* Constructor function * Constructor function
* Accepts various logger modes: * Accepts various logger modes:
@ -83,10 +88,11 @@ class Logger
} }
self::$mode = $mode; self::$mode = $mode;
self::$optional = $optional; self::$optional = $optional;
self::$prefix = $prefix === '' ? '' : ', ' . $prefix; self::$prefix = $prefix === '' ? '' : ', '.$prefix;
self::$level = $level; self::$level = $level;
self::class_exists(); self::class_exists();
} }
public static function log($params, $level = self::NOTICE) public static function log($params, $level = self::NOTICE)
{ {
if (self::$mode === 4) { if (self::$mode === 4) {
@ -103,20 +109,20 @@ class Logger
if (!is_string($param)) { if (!is_string($param)) {
$param = json_encode($param, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); $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) { switch (self::$mode) {
case 1: case 1:
error_log($param); error_log($param);
break; break;
case 2: case 2:
error_log($param . PHP_EOL, 3, self::$optional); error_log($param.PHP_EOL, 3, self::$optional);
break; break;
case 3: case 3:
echo self::$isatty ? "\33[" . self::$colors[$level] . 'm' . $param . "\33[0m" . PHP_EOL : $param . PHP_EOL; echo self::$isatty ? "\33[".self::$colors[$level].'m'.$param."\33[0m".PHP_EOL : $param.PHP_EOL;
break; break;
default: default:
break; break;
} }
} }
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class Lua class Lua
@ -18,6 +19,7 @@ class Lua
public $MadelineProto; public $MadelineProto;
protected $Lua; protected $Lua;
protected $script; protected $script;
public function __magic_construct($script, $MadelineProto) public function __magic_construct($script, $MadelineProto)
{ {
if (!file_exists($script)) { if (!file_exists($script)) {
@ -28,10 +30,12 @@ class Lua
$this->script = $script; $this->script = $script;
$this->__wakeup(); $this->__wakeup();
} }
public function __sleep() public function __sleep()
{ {
return ['MadelineProto', 'script']; return ['MadelineProto', 'script'];
} }
public function __wakeup() public function __wakeup()
{ {
$this->Lua = new \Lua($this->script); $this->Lua = new \Lua($this->script);
@ -65,6 +69,7 @@ class Lua
$this->MadelineProto->{$namespace}->lua = true; $this->MadelineProto->{$namespace}->lua = true;
} }
} }
public function tdcli_function($params, $cb = null, $cb_extra = null) public function tdcli_function($params, $cb = null, $cb_extra = null)
{ {
$params = $this->MadelineProto->td_to_mtproto($this->MadelineProto->tdcli_to_td($params)); $params = $this->MadelineProto->td_to_mtproto($this->MadelineProto->tdcli_to_td($params));
@ -75,8 +80,10 @@ class Lua
if (is_callable($cb)) { if (is_callable($cb)) {
$cb($this->MadelineProto->mtproto_to_td($result), $cb_extra); $cb($this->MadelineProto->mtproto_to_td($result), $cb_extra);
} }
return $result; return $result;
} }
public function madeline_function($params, $cb = null, $cb_extra = null) public function madeline_function($params, $cb = null, $cb_extra = null)
{ {
$result = $this->MadelineProto->API->method_call($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]); $result = $this->MadelineProto->API->method_call($params['_'], $params, ['datacenter' => $this->MadelineProto->API->datacenter->curdc]);
@ -84,12 +91,15 @@ class Lua
$cb($result, $cb_extra); $cb($result, $cb_extra);
} }
self::convert_objects($result); self::convert_objects($result);
return $result; return $result;
} }
public function tdcli_update_callback($update) public function tdcli_update_callback($update)
{ {
$this->Lua->tdcli_update_callback($this->MadelineProto->mtproto_to_tdcli($update)); $this->Lua->tdcli_update_callback($this->MadelineProto->mtproto_to_tdcli($update));
} }
private function convert_array($array) private function convert_array($array)
{ {
if (!is_array($value)) { if (!is_array($value)) {
@ -101,23 +111,29 @@ class Lua
}, array_flip($array))); }, array_flip($array)));
} }
} }
private function is_sequential(array $arr) private function is_sequential(array $arr)
{ {
if ([] === $arr) { if ([] === $arr) {
return false; return false;
} }
return isset($arr[0]) && array_keys($arr) === range(0, count($arr) - 1); return isset($arr[0]) && array_keys($arr) === range(0, count($arr) - 1);
} }
public function __get($name) public function __get($name)
{ {
if ($name === 'API') { if ($name === 'API') {
return $this->MadelineProto->API; return $this->MadelineProto->API;
} }
return $this->Lua->{$name}; return $this->Lua->{$name};
} }
public function __call($name, $params) public function __call($name, $params)
{ {
self::convert_objects($params); self::convert_objects($params);
try { try {
return $this->Lua->{$name}(...$params); return $this->Lua->{$name}(...$params);
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -136,10 +152,12 @@ class Lua
return ['error_code' => $e->getCode(), 'error' => $e->getMessage()]; return ['error_code' => $e->getCode(), 'error' => $e->getMessage()];
} }
} }
public function __set($name, $value) public function __set($name, $value)
{ {
return $this->Lua->{$name} = $value; return $this->Lua->{$name} = $value;
} }
public static function convert_objects(&$data) public static function convert_objects(&$data)
{ {
array_walk_recursive($data, function (&$value, $key) { array_walk_recursive($data, function (&$value, $key) {
@ -161,4 +179,4 @@ class Lua
} }
}); });
} }
} }

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -21,16 +22,19 @@ trait AckHandler
{ {
// The server acknowledges that it received my message // The server acknowledges that it received my message
if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) { if (!isset($this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id])) {
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id " . $message_id . ' in the array of outgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING); \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 false;
} }
return $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['ack'] = true; return $this->datacenter->sockets[$datacenter]->outgoing_messages[$message_id]['ack'] = true;
} }
public function ack_incoming_message_id($message_id, $datacenter) public function ack_incoming_message_id($message_id, $datacenter)
{ {
// I let the server know that I received its message // I let the server know that I received its message
if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) { if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id])) {
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id " . $message_id . ' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING); \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?'); //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") { 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") {
@ -42,4 +46,4 @@ trait AckHandler
return true; return true;
//$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true; //$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'] = true;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -77,9 +78,9 @@ trait AuthKeyHandler
list($p, $q) = [$q, $p]; list($p, $q) = [$q, $p];
} }
if (!$pq->equals($p->multiply($q))) { 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
@ -88,14 +89,14 @@ trait AuthKeyHandler
$q_bytes = $q->toBytes(); $q_bytes = $q->toBytes();
$new_nonce = $this->random(32); $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]; $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'); $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); $sha_digest = sha1($p_q_inner_data, true);
$random_bytes = $this->random(255 - strlen($p_q_inner_data) - strlen($sha_digest)); $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); $encrypted_data = $key->encrypt($to_encrypt);
\danog\MadelineProto\Logger::log(['Starting Diffie Hellman key exchange'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Starting Diffie Hellman key exchange'], \danog\MadelineProto\Logger::VERBOSE);
/* /*
@ -146,8 +147,8 @@ trait AuthKeyHandler
* Get key, iv and decrypt answer * Get key, iv and decrypt answer
*/ */
$encrypted_answer = $server_dh_params['encrypted_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_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_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); $answer_with_hash = $this->ige_decrypt($encrypted_answer, $tmp_aes_key, $tmp_aes_iv);
/* /*
* *********************************************************************** * ***********************************************************************
@ -227,8 +228,8 @@ trait AuthKeyHandler
* *********************************************************************** * ***********************************************************************
* encrypt client_DH_inner_data * encrypt client_DH_inner_data
*/ */
$data_with_sha = sha1($data, true) . $data; $data_with_sha = sha1($data, true).$data;
$data_with_sha_padded = $data_with_sha . $this->random($this->posmod(-strlen($data_with_sha), 16)); $data_with_sha_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); $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); \danog\MadelineProto\Logger::log(['Executing set_client_DH_params...'], \danog\MadelineProto\Logger::VERBOSE);
/* /*
@ -258,9 +259,9 @@ trait AuthKeyHandler
$auth_key_str = $auth_key->toBytes(); $auth_key_str = $auth_key->toBytes();
$auth_key_sha = sha1($auth_key_str, true); $auth_key_sha = sha1($auth_key_str, true);
$auth_key_aux_hash = substr($auth_key_sha, 0, 8); $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_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_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_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
@ -290,6 +291,7 @@ trait AuthKeyHandler
$res_authorization['id'] = substr($auth_key_sha, -8); $res_authorization['id'] = substr($auth_key_sha, -8);
$res_authorization['connection_inited'] = false; $res_authorization['connection_inited'] = false;
\danog\MadelineProto\Logger::log(['Auth key generated'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Auth key generated'], \danog\MadelineProto\Logger::NOTICE);
return $res_authorization; return $res_authorization;
case 'dh_gen_retry': case 'dh_gen_retry':
if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) { if ($Set_client_DH_params_answer['new_nonce_hash2'] != $new_nonce_hash2) {
@ -310,15 +312,15 @@ trait AuthKeyHandler
} }
} }
} catch (\danog\MadelineProto\SecurityException $e) { } 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) { } 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'; $req_pq = $req_pq === 'req_pq_multi' ? 'req_pq' : 'req_pq_multi';
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'RPC_CALL_FAIL') { if ($e->rpc === 'RPC_CALL_FAIL') {
throw $e; 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 { } finally {
$this->datacenter->sockets[$datacenter]->new_outgoing = []; $this->datacenter->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = []; $this->datacenter->sockets[$datacenter]->new_incoming = [];
@ -328,6 +330,7 @@ trait AuthKeyHandler
throw new \danog\MadelineProto\SecurityException('Auth Failed'); throw new \danog\MadelineProto\SecurityException('Auth Failed');
} }
} }
public function check_G($g_a, $p) public function check_G($g_a, $p)
{ {
/* /*
@ -343,8 +346,10 @@ trait AuthKeyHandler
if ($g_a->compare($this->twoe1984) < 0 || $g_a->compare($p->subtract($this->twoe1984)) >= 0) { 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).'); throw new \danog\MadelineProto\SecurityException('g_a is invalid (2^1984 < gA < dh_prime - 2^1984 is false).');
} }
return true; return true;
} }
public function check_p_g($p, $g) public function check_p_g($p, $g)
{ {
/* /*
@ -387,11 +392,14 @@ trait AuthKeyHandler
if ($g->compare($this->one) <= 0 || $g->compare($p->subtract($this->one)) >= 0) { 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).'); throw new \danog\MadelineProto\SecurityException('g is invalid (1 < g < p - 1 is false).');
} }
return true; return true;
} }
public function get_dh_config() public function get_dh_config()
{ {
$this->updates_state['sync_loading'] = true; $this->updates_state['sync_loading'] = true;
try { try {
$dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]); $dh_config = $this->method_call('messages.getDhConfig', ['version' => $this->dh_config['version'], 'random_length' => 0], ['datacenter' => $this->datacenter->curdc]);
} finally { } finally {
@ -399,13 +407,16 @@ trait AuthKeyHandler
} }
if ($dh_config['_'] === 'messages.dhConfigNotModified') { if ($dh_config['_'] === 'messages.dhConfigNotModified') {
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Logger::VERBOSE, ['DH configuration not modified']); \danog\MadelineProto\Logger::log(\danog\MadelineProto\Logger::VERBOSE, ['DH configuration not modified']);
return $this->dh_config; return $this->dh_config;
} }
$dh_config['p'] = new \phpseclib\Math\BigInteger($dh_config['p'], 256); $dh_config['p'] = new \phpseclib\Math\BigInteger($dh_config['p'], 256);
$dh_config['g'] = new \phpseclib\Math\BigInteger($dh_config['g']); $dh_config['g'] = new \phpseclib\Math\BigInteger($dh_config['g']);
$this->check_p_g($dh_config['p'], $dh_config['g']); $this->check_p_g($dh_config['p'], $dh_config['g']);
return $this->dh_config = $dh_config; return $this->dh_config = $dh_config;
} }
public function bind_temp_auth_key($expires_in, $datacenter) 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++) { for ($retry_id_total = 1; $retry_id_total <= $this->settings['max_tries']['authorization']; $retry_id_total++) {
@ -419,36 +430,40 @@ trait AuthKeyHandler
$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); $message_id = $this->generate_message_id($datacenter);
$seq_no = 0; $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); $message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 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); //$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']); 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]); $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) { 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; return true;
} }
} catch (\danog\MadelineProto\SecurityException $e) { } 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) { } 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) { } 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 { } finally {
$this->datacenter->sockets[$datacenter]->new_outgoing = []; $this->datacenter->sockets[$datacenter]->new_outgoing = [];
$this->datacenter->sockets[$datacenter]->new_incoming = []; $this->datacenter->sockets[$datacenter]->new_incoming = [];
} }
} }
throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.'); throw new \danog\MadelineProto\SecurityException('An error occurred while binding temporary and permanent authorization keys.');
} }
// Creates authorization keys // Creates authorization keys
public function init_authorization() public function init_authorization()
{ {
$this->initing_authorization = true; $this->initing_authorization = true;
$this->updates_state['sync_loading'] = true; $this->updates_state['sync_loading'] = true;
$this->postpone_updates = true; $this->postpone_updates = true;
try { try {
foreach ($this->datacenter->sockets as $id => $socket) { foreach ($this->datacenter->sockets as $id => $socket) {
$cdn = strpos($id, 'cdn'); $cdn = strpos($id, 'cdn');
@ -500,6 +515,7 @@ trait AuthKeyHandler
$this->updates_state['sync_loading'] = false; $this->updates_state['sync_loading'] = false;
} }
} }
public function sync_authorization($id) public function sync_authorization($id)
{ {
if (!isset($this->datacenter->sockets[$id])) { if (!isset($this->datacenter->sockets[$id])) {
@ -513,15 +529,15 @@ 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) { 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 { 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]); $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]); $authorization = $this->method_call('auth.importAuthorization', $exported_authorization, ['datacenter' => $id]);
$socket->authorized = true; $socket->authorized = true;
break; break;
} catch (\danog\MadelineProto\Exception $e) { } 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) { } 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') { if ($e->rpc === 'DC_ID_INVALID') {
break; break;
} }
@ -531,4 +547,4 @@ trait AuthKeyHandler
} }
} }
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -82,15 +83,16 @@ trait CallHandler
} }
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) { 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'); $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; $last_recv = $this->last_recv;
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) { for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
if ($canunset = !$this->updates_state['sync_loading']) { if ($canunset = !$this->updates_state['sync_loading']) {
$this->updates_state['sync_loading'] = true; $this->updates_state['sync_loading'] = true;
} }
try { 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) { 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'])]; $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)) { if (count($this->datacenter->sockets[$aargs['datacenter']]->ack_queue)) {
@ -131,7 +133,7 @@ trait CallHandler
while ($server_answer === null && $res_count++ < $this->settings['max_tries']['response'] + 1) { 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 // Loop until we get a response, loop for a max of $this->settings['max_tries']['response'] times
try { try {
\danog\MadelineProto\Logger::log(['Getting response (try number ' . $res_count . ' for ' . $method . ')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE); \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'])) { 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 // Checks if I have received the response to the called method, if not continue looping
if ($only_updates) { if ($only_updates) {
@ -153,9 +155,11 @@ trait CallHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null; $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization(); $this->init_authorization();
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key'); throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
} }
} }
throw new \danog\MadelineProto\RPCErrorException($error, $error); throw new \danog\MadelineProto\RPCErrorException($error, $error);
} }
$only_updates = $this->handle_messages($aargs['datacenter']); $only_updates = $this->handle_messages($aargs['datacenter']);
@ -164,10 +168,10 @@ trait CallHandler
if ($e->getMessage() === 'I had to recreate the temporary authorization key') { if ($e->getMessage() === 'I had to recreate the temporary authorization key') {
continue 2; 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; continue;
} catch (\danog\MadelineProto\NothingInTheSocketException $e) { } 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; continue;
} }
} }
@ -193,15 +197,16 @@ trait CallHandler
continue 3; continue 3;
case 16: case 16:
case 17: case 17:
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: ' . self::BAD_MSG_ERROR_CODES[$server_answer['error_code']]], \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(); $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(['Set time delta to '.$this->datacenter->sockets[$aargs['datacenter']]->time_delta], \danog\MadelineProto\Logger::WARNING);
$this->reset_session(); $this->reset_session();
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null; $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization(); $this->init_authorization();
continue 3; 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; break;
case 'boolTrue': case 'boolTrue':
case 'boolFalse': case 'boolFalse':
@ -212,16 +217,16 @@ trait CallHandler
$server_answer = $this->MTProto_to_botAPI($server_answer, $args); $server_answer = $this->MTProto_to_botAPI($server_answer, $args);
} }
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
$last_error = $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine(); $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); \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']); $this->close_and_reopen($aargs['datacenter']);
if ($e->getMessage() === 'Re-executing query after server error...') { if ($e->getMessage() === 'Re-executing query after server error...') {
return $this->method_call($method, $args, $aargs); return $this->method_call($method, $args, $aargs);
} }
continue; continue;
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
$last_error = $e->getMessage() . ' in ' . basename($e->getFile(), '.php') . ' on line ' . $e->getLine(); $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); \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']); $this->close_and_reopen($aargs['datacenter']);
continue; continue;
} finally { } finally {
@ -244,11 +249,13 @@ trait CallHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null; $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization(); $this->init_authorization();
return $this->method_call($method, $args, $aargs); 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)) { 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']]->temp_auth_key['connection_inited'] = true;
} }
@ -260,13 +267,16 @@ trait CallHandler
$server_answer[] = $this->method_call($method, $args, $aargs); $server_answer[] = $this->method_call($method, $args, $aargs);
} }
} }
return $server_answer; return $server_answer;
} }
if ($method === 'req_pq') { if ($method === 'req_pq') {
throw new \danog\MadelineProto\RPCErrorException('RPC_CALL_FAIL'); 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]) public function object_call($object, $args = [], $aargs = ['message_id' => null, 'heavy' => false])
{ {
if (!is_array($args)) { if (!is_array($args)) {

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
trait Crypt trait Crypt
@ -17,42 +18,51 @@ trait Crypt
public function aes_calculate($msg_key, $auth_key, $to_server = true) public function aes_calculate($msg_key, $auth_key, $to_server = true)
{ {
$x = $to_server ? 0 : 8; $x = $to_server ? 0 : 8;
$sha256_a = hash('sha256', $msg_key . substr($auth_key, $x, 36), true); $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); $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_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); $aes_iv = substr($sha256_b, 0, 8).substr($sha256_a, 8, 16).substr($sha256_b, 24, 8);
return [$aes_key, $aes_iv]; return [$aes_key, $aes_iv];
} }
public function old_aes_calculate($msg_key, $auth_key, $to_server = true) public function old_aes_calculate($msg_key, $auth_key, $to_server = true)
{ {
$x = $to_server ? 0 : 8; $x = $to_server ? 0 : 8;
$sha1_a = sha1($msg_key . substr($auth_key, $x, 32), true); $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_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_c = sha1(substr($auth_key, 64 + $x, 32).$msg_key, true);
$sha1_d = sha1($msg_key . substr($auth_key, 96 + $x, 32), 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_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); $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]; return [$aes_key, $aes_iv];
} }
public function ige_encrypt($message, $key, $iv) public function ige_encrypt($message, $key, $iv)
{ {
$cipher = new \phpseclib\Crypt\AES('ige'); $cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key); $cipher->setKey($key);
$cipher->setIV($iv); $cipher->setIV($iv);
return @$cipher->encrypt($message); return @$cipher->encrypt($message);
} }
public function ctr_encrypt($message, $key, $iv) public function ctr_encrypt($message, $key, $iv)
{ {
$cipher = new \phpseclib\Crypt\AES('ctr'); $cipher = new \phpseclib\Crypt\AES('ctr');
$cipher->setKey($key); $cipher->setKey($key);
$cipher->setIV($iv); $cipher->setIV($iv);
return @$cipher->encrypt($message); return @$cipher->encrypt($message);
} }
public function ige_decrypt($message, $key, $iv) public function ige_decrypt($message, $key, $iv)
{ {
$cipher = new \phpseclib\Crypt\AES('ige'); $cipher = new \phpseclib\Crypt\AES('ige');
$cipher->setKey($key); $cipher->setKey($key);
$cipher->setIV($iv); $cipher->setIV($iv);
return @$cipher->decrypt($message); return @$cipher->decrypt($message);
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -32,21 +33,21 @@ trait Files
} }
if ($cb === null) { if ($cb === null) {
$cb = function ($percent) { $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_size = 512 * 1024;
$part_total_num = (int) ceil($file_size / $part_size); $part_total_num = (int) ceil($file_size / $part_size);
$part_num = 0; $part_num = 0;
$method = $file_size > 10 * 1024 * 1024 ? 'upload.saveBigFilePart' : 'upload.saveFilePart'; $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); $file_id = $this->random(8);
$f = fopen($file, 'r'); $f = fopen($file, 'r');
fseek($f, 0); fseek($f, 0);
if ($encrypted === true) { if ($encrypted === true) {
$key = $this->random(32); $key = $this->random(32);
$iv = $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)); $fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
$ige = new \phpseclib\Crypt\AES('ige'); $ige = new \phpseclib\Crypt\AES('ige');
$ige->setIV($iv); $ige->setIV($iv);
@ -61,7 +62,7 @@ trait Files
} }
hash_update($ctx, $bytes); 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])) { 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); $cb(ftell($f) * 100 / $file_size);
} }
@ -73,12 +74,15 @@ trait Files
$constructor['iv'] = $iv; $constructor['iv'] = $iv;
$constructor['md5_checksum'] = ''; $constructor['md5_checksum'] = '';
} }
return $constructor; return $constructor;
} }
public function upload_encrypted($file, $file_name = '', $cb = null) public function upload_encrypted($file, $file_name = '', $cb = null)
{ {
return $this->upload($file, $file_name, $cb, true); return $this->upload($file, $file_name, $cb, true);
} }
public function get_download_info($message_media) public function get_download_info($message_media)
{ {
if (is_string($message_media)) { if (is_string($message_media)) {
@ -101,7 +105,7 @@ trait Files
if (isset($message_media['decrypted_message']['media']['file_name'])) { if (isset($message_media['decrypted_message']['media']['file_name'])) {
$pathinfo = pathinfo($message_media['decrypted_message']['media']['file_name']); $pathinfo = pathinfo($message_media['decrypted_message']['media']['file_name']);
if (isset($pathinfo['extension'])) { if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension']; $res['ext'] = '.'.$pathinfo['extension'];
} }
$res['name'] = $pathinfo['filename']; $res['name'] = $pathinfo['filename'];
} }
@ -116,7 +120,7 @@ trait Files
case 'documentAttributeFilename': case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']); $pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) { if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension']; $res['ext'] = '.'.$pathinfo['extension'];
} }
$res['name'] = $pathinfo['filename']; $res['name'] = $pathinfo['filename'];
break; break;
@ -129,7 +133,7 @@ trait Files
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) { if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title']; $res['name'] = $audio['title'];
if (isset($audio['performer'])) { if (isset($audio['performer'])) {
$res['name'] .= ' - ' . $audio['performer']; $res['name'] .= ' - '.$audio['performer'];
} }
} }
if (!isset($res['ext'])) { if (!isset($res['ext'])) {
@ -138,6 +142,7 @@ trait Files
if (!isset($res['name'])) { if (!isset($res['name'])) {
$res['name'] = $message_media['file']['access_hash']; $res['name'] = $message_media['file']['access_hash'];
} }
return $res; return $res;
case 'photo': case 'photo':
case 'messageMediaPhoto': case 'messageMediaPhoto':
@ -146,7 +151,7 @@ trait Files
} else { } else {
$photo = end($message_media['photo']['sizes']); $photo = end($message_media['photo']['sizes']);
} }
$res['name'] = $photo['location']['volume_id'] . '_' . $photo['location']['local_id']; $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['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['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg'; $res['mime'] = 'image/jpeg';
@ -156,10 +161,11 @@ trait Files
if (isset($photo['location']['bytes'])) { if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']); $res['size'] = strlen($photo['location']['bytes']);
} }
return $res; return $res;
case 'photoSize': case 'photoSize':
case 'photoCachedSize': case 'photoCachedSize':
$res['name'] = $message_media['location']['volume_id'] . '_' . $message_media['location']['local_id']; $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['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['ext'] = $this->get_extension_from_location($res['InputFileLocation'], '.jpg');
$res['mime'] = 'image/jpeg'; $res['mime'] = 'image/jpeg';
@ -169,6 +175,7 @@ trait Files
if (isset($photo['location']['bytes'])) { if (isset($photo['location']['bytes'])) {
$res['size'] = strlen($photo['location']['bytes']); $res['size'] = strlen($photo['location']['bytes']);
} }
return $res; return $res;
case 'decryptedMessageMediaExternalDocument': case 'decryptedMessageMediaExternalDocument':
case 'document': case 'document':
@ -179,7 +186,7 @@ trait Files
case 'documentAttributeFilename': case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']); $pathinfo = pathinfo($attribute['file_name']);
if (isset($pathinfo['extension'])) { if (isset($pathinfo['extension'])) {
$res['ext'] = '.' . $pathinfo['extension']; $res['ext'] = '.'.$pathinfo['extension'];
} }
$res['name'] = $pathinfo['filename']; $res['name'] = $pathinfo['filename'];
break; break;
@ -191,7 +198,7 @@ trait Files
if (isset($audio) && isset($audio['title']) && !isset($res['name'])) { if (isset($audio) && isset($audio['title']) && !isset($res['name'])) {
$res['name'] = $audio['title']; $res['name'] = $audio['title'];
if (isset($audio['performer'])) { if (isset($audio['performer'])) {
$res['name'] .= ' - ' . $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']]; $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']];
@ -204,18 +211,22 @@ trait Files
if (isset($message_media['document']['size'])) { if (isset($message_media['document']['size'])) {
$res['size'] = $message_media['document']['size']; $res['size'] = $message_media['document']['size'];
} }
$res['name'] .= '_' . $message_media['document']['id']; $res['name'] .= '_'.$message_media['document']['id'];
$res['mime'] = $message_media['document']['mime_type']; $res['mime'] = $message_media['document']['mime_type'];
return $res; return $res;
default: 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) public function download_to_dir($message_media, $dir, $cb = null)
{ {
$message_media = $this->get_download_info($message_media); $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) public function download_to_file($message_media, $file, $cb = null)
{ {
$file = preg_replace('|/+|', '/', $file); $file = preg_replace('|/+|', '/', $file);
@ -227,6 +238,7 @@ trait Files
$stream = fopen($file, 'r+b'); $stream = fopen($file, 'r+b');
\danog\MadelineProto\Logger::log(['Waiting for lock of file to download...']); \danog\MadelineProto\Logger::log(['Waiting for lock of file to download...']);
flock($stream, LOCK_EX); flock($stream, LOCK_EX);
try { try {
$this->download_to_stream($message_media, $stream, $cb, filesize($file), -1); $this->download_to_stream($message_media, $stream, $cb, filesize($file), -1);
} finally { } finally {
@ -234,13 +246,15 @@ trait Files
fclose($stream); fclose($stream);
clearstatcache(); clearstatcache();
} }
return $file; return $file;
} }
public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1) public function download_to_stream($message_media, $stream, $cb = null, $offset = 0, $end = -1)
{ {
if ($cb === null) { if ($cb === null) {
$cb = function ($percent) { $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); $message_media = $this->get_download_info($message_media);
@ -256,7 +270,7 @@ trait Files
$percent = 0; $percent = 0;
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc; $datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
if (isset($message_media['key'])) { 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)); $fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
if ($fingerprint !== $message_media['key_fingerprint']) { if ($fingerprint !== $message_media['key_fingerprint']) {
throw new \danog\MadelineProto\Exception('Fingerprint mismatch!'); throw new \danog\MadelineProto\Exception('Fingerprint mismatch!');
@ -272,6 +286,7 @@ trait Files
if ($start_at = $offset % $part_size) { if ($start_at = $offset % $part_size) {
$offset -= $start_at; $offset -= $start_at;
} }
try { 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]); $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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -289,7 +304,7 @@ trait Files
$message_media['cdn_key'] = $res['encryption_key']; $message_media['cdn_key'] = $res['encryption_key'];
$message_media['cdn_iv'] = $res['encryption_iv']; $message_media['cdn_iv'] = $res['encryption_iv'];
$old_dc = $datacenter; $old_dc = $datacenter;
$datacenter = $res['dc_id'] . '_cdn'; $datacenter = $res['dc_id'].'_cdn';
if (!isset($this->datacenter->sockets[$datacenter])) { if (!isset($this->datacenter->sockets[$datacenter])) {
$this->config['expires'] = -1; $this->config['expires'] = -1;
$this->get_config([], ['datacenter' => $this->datacenter->curdc]); $this->get_config([], ['datacenter' => $this->datacenter->curdc]);
@ -300,6 +315,7 @@ trait Files
if ($res['_'] === 'upload.cdnFileReuploadNeeded') { if ($res['_'] === 'upload.cdnFileReuploadNeeded') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['cdn_reupload']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['cdn_reupload']], \danog\MadelineProto\Logger::NOTICE);
$this->get_config([], ['datacenter' => $this->datacenter->curdc]); $this->get_config([], ['datacenter' => $this->datacenter->curdc]);
try { 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])); $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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -325,7 +341,7 @@ trait Files
} }
} }
if (isset($message_media['cdn_key'])) { 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); $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); $this->check_cdn_hash($message_media['file_token'], $offset, $res['bytes'], $old_dc);
} }
@ -358,9 +374,12 @@ trait Files
if ($cdn) { if ($cdn) {
$this->clear_cdn_hashes($message_media['file_token']); $this->clear_cdn_hashes($message_media['file_token']);
} }
return true; return true;
} }
private $cdn_hashes = []; private $cdn_hashes = [];
private function add_cdn_hashes($file, $hashes) private function add_cdn_hashes($file, $hashes)
{ {
if (!isset($this->cdn_hashes[$file])) { if (!isset($this->cdn_hashes[$file])) {
@ -370,6 +389,7 @@ trait Files
$this->cdn_hashes[$file][$hash['offset']] = ['limit' => $hash['limit'], 'hash' => (string) $hash['hash']]; $this->cdn_hashes[$file][$hash['offset']] = ['limit' => $hash['limit'], 'hash' => (string) $hash['hash']];
} }
} }
private function check_cdn_hash($file, $offset, $data, &$datacenter) private function check_cdn_hash($file, $offset, $data, &$datacenter)
{ {
while (strlen($data)) { while (strlen($data)) {
@ -377,19 +397,22 @@ trait Files
$this->add_cdn_hashes($file, $this->method_call('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset], ['datacenter' => $datacenter])); $this->add_cdn_hashes($file, $this->method_call('upload.getCdnFileHashes', ['file_token' => $file, 'offset' => $offset], ['datacenter' => $datacenter]));
} }
if (!isset($this->cdn_hashes[$file][$offset])) { 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']) { 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']); $data = substr($data, $this->cdn_hashes[$file][$offset]['limit']);
$offset += $this->cdn_hashes[$file][$offset]['limit']; $offset += $this->cdn_hashes[$file][$offset]['limit'];
} }
return true; return true;
} }
private function clear_cdn_hashes($file) private function clear_cdn_hashes($file)
{ {
unset($this->cdn_hashes[$file]); unset($this->cdn_hashes[$file]);
return true; return true;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -19,10 +20,11 @@ trait MessageHandler
{ {
public function send_unencrypted_message($message_data, $message_id, $datacenter) 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]->outgoing_messages[$message_id] = ['response' => -1];
$this->datacenter->sockets[$datacenter]->send_message($message_data); $this->datacenter->sockets[$datacenter]->send_message($message_data);
} }
public function send_messages($datacenter) public function send_messages($datacenter)
{ {
if (count($this->datacenter->sockets[$datacenter]->object_queue) > 1) { if (count($this->datacenter->sockets[$datacenter]->object_queue) > 1) {
@ -45,15 +47,15 @@ trait MessageHandler
} else { } else {
return; 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); $padding = $this->posmod(-strlen($plaintext), 16);
if ($padding < 12) { if ($padding < 12) {
$padding += 16; $padding += 16;
} }
$padding = $this->random($padding); $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']); 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]->outgoing_messages[$message_id] = ['seq_no' => $seq_no, 'response' => -1];
$this->datacenter->sockets[$datacenter]->send_message($message); $this->datacenter->sockets[$datacenter]->send_message($message);
$this->datacenter->sockets[$datacenter]->object_queue = []; $this->datacenter->sockets[$datacenter]->object_queue = [];
@ -62,6 +64,7 @@ trait MessageHandler
} }
$this->datacenter->sockets[$datacenter]->ack_queue = []; $this->datacenter->sockets[$datacenter]->ack_queue = [];
} }
/** /**
* Reading connection and receiving message from server. * Reading connection and receiving message from server.
*/ */
@ -114,7 +117,7 @@ trait MessageHandler
throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4'); throw new \danog\MadelineProto\SecurityException('message_data_length not divisible by 4');
} }
$message_data = substr($decrypted_data, 32, $message_data_length); $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'); throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
} }
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no]; $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
@ -127,6 +130,7 @@ trait MessageHandler
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1; $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['response'] = -1;
$this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id; $this->datacenter->sockets[$datacenter]->new_incoming[$message_id] = $message_id;
$this->last_recv = time(); $this->last_recv = time();
return true; return true;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -24,18 +25,18 @@ trait MsgIdHandler
} }
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32); $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) { 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); $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) { 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 ($aargs['outgoing']) {
if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) { 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) { 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']) { if (count($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages) > $this->settings['msg_array_limit']['outgoing']) {
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages); reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
@ -51,11 +52,11 @@ trait MsgIdHandler
$key = $this->get_max_id($aargs['datacenter'], true); $key = $this->get_max_id($aargs['datacenter'], true);
if ($aargs['container']) { if ($aargs['container']) {
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) >= 0) { 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 { } else {
if ($new_message_id->compare($key = $this->get_max_id($aargs['datacenter'], true)) <= 0) { 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']) { if (count($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages) > $this->settings['msg_array_limit']['incoming']) {
@ -70,6 +71,7 @@ trait MsgIdHandler
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = []; $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = [];
} }
} }
public function generate_message_id($datacenter) public function generate_message_id($datacenter)
{ {
$message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta))->bitwise_leftShift(32); $message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta))->bitwise_leftShift(32);
@ -77,14 +79,17 @@ trait MsgIdHandler
$message_id = $key->add($this->four); $message_id = $key->add($this->four);
} }
$this->check_message_id($message_id, ['outgoing' => true, 'datacenter' => $datacenter, 'container' => false]); $this->check_message_id($message_id, ['outgoing' => true, 'datacenter' => $datacenter, 'container' => false]);
return strrev($message_id->toBytes()); return strrev($message_id->toBytes());
} }
public function get_max_id($datacenter, $incoming) public function get_max_id($datacenter, $incoming)
{ {
$incoming = $incoming ? 'incoming' : 'outgoing'; $incoming = $incoming ? 'incoming' : 'outgoing';
if (isset($this->datacenter->sockets[$datacenter]->{'max_' . $incoming . '_id'}) && is_object($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->datacenter->sockets[$datacenter]->{'max_'.$incoming.'_id'};
} }
return $this->zero; return $this->zero;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -30,6 +31,7 @@ trait PeerHandler
case 'user': case 'user':
if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) { if (!isset($this->chats[$user['id']]) || $this->chats[$user['id']] !== $user) {
$this->chats[$user['id']] = $user; $this->chats[$user['id']] = $user;
try { try {
$this->get_pwr_chat($user['id'], false, true); $this->get_pwr_chat($user['id'], false, true);
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
@ -41,11 +43,12 @@ trait PeerHandler
case 'userEmpty': case 'userEmpty':
break; break;
default: 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; break;
} }
} }
} }
public function add_chats($chats) public function add_chats($chats)
{ {
foreach ($chats as $key => $chat) { foreach ($chats as $key => $chat) {
@ -55,6 +58,7 @@ trait PeerHandler
case 'chatForbidden': case 'chatForbidden':
if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) { if (!isset($this->chats[-$chat['id']]) || $this->chats[-$chat['id']] !== $chat) {
$this->chats[-$chat['id']] = $chat; $this->chats[-$chat['id']] = $chat;
try { try {
$this->get_pwr_chat(-$chat['id'], $this->settings['peer']['full_fetch'], true); $this->get_pwr_chat(-$chat['id'], $this->settings['peer']['full_fetch'], true);
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
@ -76,6 +80,7 @@ trait PeerHandler
} }
if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) { if (!isset($this->chats[$bot_api_id]) || $this->chats[$bot_api_id] !== $chat) {
$this->chats[$bot_api_id] = $chat; $this->chats[$bot_api_id] = $chat;
try { 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']) { 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); $this->get_pwr_chat($this->to_supergroup($chat['id']), $this->settings['peer']['full_fetch'], true);
@ -88,11 +93,12 @@ trait PeerHandler
} }
break; break;
default: 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; break;
} }
} }
} }
public function peer_isset($id) public function peer_isset($id)
{ {
try { try {
@ -103,6 +109,7 @@ trait PeerHandler
return false; return false;
} }
} }
public function entities_peer_isset($entities) public function entities_peer_isset($entities)
{ {
try { try {
@ -116,8 +123,10 @@ trait PeerHandler
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
return false; return false;
} }
return true; return true;
} }
public function fwd_peer_isset($fwd) public function fwd_peer_isset($fwd)
{ {
try { try {
@ -130,8 +139,10 @@ trait PeerHandler
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
return false; return false;
} }
return true; return true;
} }
public function get_info($id, $recursive = true) public function get_info($id, $recursive = true)
{ {
if (is_array($id)) { if (is_array($id)) {
@ -169,7 +180,7 @@ trait PeerHandler
$id = $this->to_supergroup($id['channel_id']); $id = $this->to_supergroup($id['channel_id']);
break; break;
default: 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; break;
} }
} }
@ -186,7 +197,7 @@ trait PeerHandler
} }
if (is_numeric($id)) { if (is_numeric($id)) {
if (is_string($id)) { if (is_string($id)) {
$id = \danog\MadelineProto\Logger::$bigint ? (double) $id : (int) $id; $id = \danog\MadelineProto\Logger::$bigint ? (float) $id : (int) $id;
} }
if (!isset($this->chats[$id]) && $id < 0 && !preg_match('/^-100/', $id)) { if (!isset($this->chats[$id]) && $id < 0 && !preg_match('/^-100/', $id)) {
$this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]); $this->method_call('messages.getFullChat', ['chat_id' => -$id], ['datacenter' => $this->datacenter->curdc]);
@ -195,11 +206,12 @@ trait PeerHandler
return $this->gen_all($this->chats[$id]); return $this->gen_all($this->chats[$id]);
} }
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true) { 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']) { 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'); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
$id = strtolower(str_replace('@', '', $id)); $id = strtolower(str_replace('@', '', $id));
@ -210,10 +222,13 @@ trait PeerHandler
} }
if ($recursive) { if ($recursive) {
$this->resolve_username($id); $this->resolve_username($id);
return $this->get_info($id, false); return $this->get_info($id, false);
} }
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database'); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
public function gen_all($constructor) public function gen_all($constructor)
{ {
$res = [$this->constructors->find_by_predicate($constructor['_'])['type'] => $constructor]; $res = [$this->constructors->find_by_predicate($constructor['_'])['type'] => $constructor];
@ -256,15 +271,18 @@ trait PeerHandler
throw new \danog\MadelineProto\Exception('Chat forbidden'); throw new \danog\MadelineProto\Exception('Chat forbidden');
break; break;
default: 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; break;
} }
return $res; return $res;
} }
public function full_chat_last_updated($id) public function full_chat_last_updated($id)
{ {
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0; return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
} }
public function get_full_info($id) public function get_full_info($id)
{ {
$partial = $this->get_info($id); $partial = $this->get_info($id);
@ -288,8 +306,10 @@ trait PeerHandler
$res['full'] = $full; $res['full'] = $full;
$res['last_update'] = time(); $res['last_update'] = time();
$this->full_chats[$partial['bot_api_id']] = $res; $this->full_chats[$partial['bot_api_id']] = $res;
return array_merge($partial, $res); return array_merge($partial, $res);
} }
public function get_pwr_chat($id, $fullfetch = true, $send = true) public function get_pwr_chat($id, $fullfetch = true, $send = true)
{ {
$full = $fullfetch ? $this->get_full_info($id) : $this->get_info($id); $full = $fullfetch ? $this->get_full_info($id) : $this->get_info($id);
@ -422,6 +442,7 @@ trait PeerHandler
$filters = ['channelParticipantsRecent', 'channelParticipantsAdmins', 'channelParticipantsKicked', 'channelParticipantsBots', 'channelParticipantsBanned']; $filters = ['channelParticipantsRecent', 'channelParticipantsAdmins', 'channelParticipantsKicked', 'channelParticipantsBots', 'channelParticipantsBanned'];
foreach ($filters as $filter) { foreach ($filters as $filter) {
$offset = -$limit; $offset = -$limit;
try { try {
$gres = $this->method_call('channels.getParticipants', ['channel' => $full['InputChannel'], 'filter' => ['_' => $filter, 'q' => ''], 'offset' => $offset += $limit, 'limit' => $limit, 'hash' => 0], ['datacenter' => $this->datacenter->curdc]); $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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -487,8 +508,10 @@ trait PeerHandler
if ($fullfetch || $send) { if ($fullfetch || $send) {
$this->store_db($res); $this->store_db($res);
} }
return $res; return $res;
} }
public function store_db($res, $force = false) 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']; $settings = isset($this->settings['connection_settings'][$this->datacenter->curdc]) ? $this->settings['connection_settings'][$this->datacenter->curdc] : $this->settings['connection_settings']['all'];
@ -516,12 +539,13 @@ trait PeerHandler
if (empty($this->qres)) { if (empty($this->qres)) {
return false; return false;
} }
try { try {
$payload = json_encode($this->qres); $payload = json_encode($this->qres);
$path = '/tmp/ids' . hash('sha256', $payload); $path = '/tmp/ids'.hash('sha256', $payload);
file_put_contents($path, $payload); file_put_contents($path, $payload);
$id = isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id']; $id = isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id'];
$result = shell_exec('curl ' . escapeshellarg('https://id.pwrtelegram.xyz/db' . $this->settings['pwr']['db_token'] . '/addnewmadeline?d=pls&from=' . $id) . ' -d ' . escapeshellarg('@' . $path) . ' -s -o ' . escapeshellarg($path . '.log') . ' >/dev/null 2>/dev/null & '); $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); \danog\MadelineProto\Logger::log([$result], \danog\MadelineProto\Logger::VERBOSE);
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::VERBOSE);
@ -529,6 +553,7 @@ trait PeerHandler
$this->qres = []; $this->qres = [];
$this->last_stored = time() + 10; $this->last_stored = time() + 10;
} }
public function resolve_username($username) public function resolve_username($username)
{ {
try { try {
@ -539,10 +564,12 @@ trait PeerHandler
if ($res['_'] === 'contacts.resolvedPeer') { if ($res['_'] === 'contacts.resolvedPeer') {
return $res; return $res;
} }
return false; return false;
} }
public function to_supergroup($id) public function to_supergroup($id)
{ {
return -($id + pow(10, (int) floor(log($id, 10) + 3))); return -($id + pow(10, (int) floor(log($id, 10) + 3)));
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -41,12 +42,13 @@ 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; $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) public function handle_messages($datacenter)
{ {
$only_updates = true; $only_updates = true;
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) { foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
$unset = false; $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); //\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 (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
if (!$this->synchronized(function ($zis, $datacenter, $current_msg_id) { if (!$this->synchronized(function ($zis, $datacenter, $current_msg_id) {
@ -54,12 +56,13 @@ trait ResponseHandler
return false; return false;
} }
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'] = true; $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'] = true;
return true; return true;
}, $this, $datacenter, $current_msg_id)) { }, $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; 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']['_']) { switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
case 'msgs_ack': case 'msgs_ack':
@ -142,7 +145,7 @@ trait ResponseHandler
// Acknowledge that I received the server's response // 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']])) { 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); $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 // Acknowledge that I received the server's response
} else { } else {
$this->check_message_id($message['orig_message']['msg_id'], ['outgoing' => false, 'datacenter' => $datacenter, 'container' => true]); $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]->incoming_messages[$message['orig_message']['msg_id']] = ['content' => $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['orig_message']];
@ -179,7 +182,7 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) { foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256); $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) { if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) !== 0) {
$this->ack_outgoing_message_id($msg_id, $datacenter); $this->ack_outgoing_message_id($msg_id, $datacenter);
} }
@ -253,9 +256,9 @@ trait ResponseHandler
break; break;
default: default:
$only_updates = false; $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) { 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']) { if ($response_type === $expecting['type']) {
\danog\MadelineProto\Logger::log(['Yes'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Yes'], \danog\MadelineProto\Logger::VERBOSE);
$this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id; $this->datacenter->sockets[$datacenter]->outgoing_messages[$expecting['msg_id']]['response'] = $current_msg_id;
@ -265,7 +268,8 @@ trait ResponseHandler
} }
\danog\MadelineProto\Logger::log(['No'], \danog\MadelineProto\Logger::VERBOSE); \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;
} }
break; break;
@ -293,11 +297,14 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]); unset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
} }
} }
return $only_updates; return $only_updates;
} }
public function handle_messages_threaded() public function handle_messages_threaded()
{ {
} }
public function handle_rpc_error($server_answer, &$aargs) public function handle_rpc_error($server_answer, &$aargs)
{ {
if (in_array($server_answer['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) { if (in_array($server_answer['error_message'], ['PERSISTENT_TIMESTAMP_EMPTY', 'PERSISTENT_TIMESTAMP_OUTDATED', 'PERSISTENT_TIMESTAMP_INVALID'])) {
@ -306,7 +313,8 @@ trait ResponseHandler
switch ($server_answer['error_code']) { switch ($server_answer['error_code']) {
case 303: case 303:
$this->datacenter->curdc = $aargs['datacenter'] = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']); $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: case 401:
switch ($server_answer['error_message']) { switch ($server_answer['error_message']) {
case 'USER_DEACTIVATED': case 'USER_DEACTIVATED':
@ -346,8 +354,9 @@ trait ResponseHandler
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']); $seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
$limit = isset($aargs['FloodWaitLimit']) ? $aargs['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt']; $limit = isset($aargs['FloodWaitLimit']) ? $aargs['FloodWaitLimit'] : $this->settings['flood_timeout']['wait_if_lt'];
if (is_numeric($seconds) && $seconds < $limit) { 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); sleep($seconds);
throw new \danog\MadelineProto\Exception('Re-executing query...'); throw new \danog\MadelineProto\Exception('Re-executing query...');
} }
case 500: case 500:
@ -356,6 +365,7 @@ trait ResponseHandler
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']); throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
} }
} }
public function handle_pending_updates() public function handle_pending_updates()
{ {
if ($this->postpone_updates) { if ($this->postpone_updates) {
@ -369,6 +379,7 @@ trait ResponseHandler
} }
} }
} }
public function handle_updates($updates) public function handle_updates($updates)
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
@ -377,6 +388,7 @@ trait ResponseHandler
if ($this->postpone_updates) { if ($this->postpone_updates) {
\danog\MadelineProto\Logger::log(['Postpone update handling'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Postpone update handling'], \danog\MadelineProto\Logger::VERBOSE);
$this->pending_updates[] = $updates; $this->pending_updates[] = $updates;
return false; return false;
} }
$this->handle_pending_updates(); $this->handle_pending_updates();
@ -419,7 +431,7 @@ trait ResponseHandler
$this->get_updates_difference(); $this->get_updates_difference();
break; break;
default: 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; break;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -23,14 +24,16 @@ trait SaltHandler
$this->add_salt($salt['valid_since'], $salt['valid_until'], $salt['salt']); $this->add_salt($salt['valid_since'], $salt['valid_until'], $salt['salt']);
} }
} }
public function add_salt($valid_since, $valid_until, $salt) public function add_salt($valid_since, $valid_until, $salt)
{ {
if (!isset($this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$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]; $this->datacenter->sockets[$datacenter]->temp_auth_key['salts'][$salt] = ['valid_since' => $valid_since, 'valid_until' => $valid_until];
} }
} }
public function handle_future_salts($salt) public function handle_future_salts($salt)
{ {
$this->method_call('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]); $this->method_call('messages.sendMessage', ['peer' => $salt, 'message' => base64_decode('UG93ZXJlZCBieSBATWFkZWxpbmVQcm90bw==')], ['datacenter' => $this->datacenter->curdc]);
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -25,12 +26,14 @@ trait SeqNoHandler
//var_dump("OUT $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_out_seq_no); //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) 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']) { 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); //\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) public function generate_in_seq_no($datacenter, $content_related)
{ {
$in = $content_related ? 1 : 0; $in = $content_related ? 1 : 0;
@ -39,6 +42,7 @@ trait SeqNoHandler
//var_dump("IN $datacenter: $value + $in = ".$this->datacenter->sockets[$datacenter]->session_in_seq_no); //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) public function content_related($method)
{ {
return isset($method['_']) ? !in_array($method['_'], [ return isset($method['_']) ? !in_array($method['_'], [
@ -74,4 +78,4 @@ trait SeqNoHandler
'msg_resend_ans_req', 'msg_resend_ans_req',
]) : true; ]) : true;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\MTProtoTools; namespace danog\MadelineProto\MTProtoTools;
/** /**
@ -23,6 +24,7 @@ trait UpdateHandler
private $channels_state = []; private $channels_state = [];
public $updates = []; public $updates = [];
public $updates_key = 0; public $updates_key = 0;
public function pwr_update_handler($update) public function pwr_update_handler($update)
{ {
/* /*
@ -55,6 +57,7 @@ trait UpdateHandler
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) public function get_updates_update_handler($update)
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
@ -63,6 +66,7 @@ trait UpdateHandler
$this->updates[$this->updates_key++] = $update; $this->updates[$this->updates_key++] = $update;
//\danog\MadelineProto\Logger::log(['Stored ', $update); //\danog\MadelineProto\Logger::log(['Stored ', $update);
} }
public function get_updates($params = []) public function get_updates($params = [])
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
@ -74,6 +78,7 @@ trait UpdateHandler
} }
}); });
$time = microtime(true); $time = microtime(true);
try { try {
try { try {
if (($error = $this->recv_message($this->datacenter->curdc)) !== true) { if (($error = $this->recv_message($this->datacenter->curdc)) !== true) {
@ -82,9 +87,11 @@ trait UpdateHandler
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key = null; $this->datacenter->sockets[$this->datacenter->curdc]->temp_auth_key = null;
$this->init_authorization(); $this->init_authorization();
throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key'); throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
} }
} }
throw new \danog\MadelineProto\RPCErrorException($error, $error); throw new \danog\MadelineProto\RPCErrorException($error, $error);
} }
$only_updates = $this->handle_messages($this->datacenter->curdc); $only_updates = $this->handle_messages($this->datacenter->curdc);
@ -127,21 +134,26 @@ trait UpdateHandler
$updates[] = ['update_id' => $key, 'update' => $value]; $updates[] = ['update_id' => $key, 'update' => $value];
} }
} }
return $updates; return $updates;
} }
public function &load_channel_state($channel, $pts = 0) public function &load_channel_state($channel, $pts = 0)
{ {
if (!isset($this->channels_state[$channel])) { if (!isset($this->channels_state[$channel])) {
$this->channels_state[$channel] = ['pts' => $pts, 'sync_loading' => false]; $this->channels_state[$channel] = ['pts' => $pts, 'sync_loading' => false];
} }
return $this->channels_state[$channel]; return $this->channels_state[$channel];
} }
public function set_channel_state($channel, $data) public function set_channel_state($channel, $data)
{ {
if (isset($data['pts']) && $data['pts'] !== 0) { if (isset($data['pts']) && $data['pts'] !== 0) {
$this->load_channel_state($channel)['pts'] = $data['pts']; $this->load_channel_state($channel)['pts'] = $data['pts'];
} }
} }
public function check_msg_id($message) public function check_msg_id($message)
{ {
try { try {
@ -152,22 +164,27 @@ trait UpdateHandler
$message_id = $message['id']; $message_id = $message['id'];
if (!isset($this->msg_ids[$peer_id]) || $message_id > $this->msg_ids[$peer_id]) { if (!isset($this->msg_ids[$peer_id]) || $message_id > $this->msg_ids[$peer_id]) {
$this->msg_ids[$peer_id] = $message_id; $this->msg_ids[$peer_id] = $message_id;
return true; return true;
} }
return false; return false;
} }
public function get_channel_difference($channel) public function get_channel_difference($channel)
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
return; return;
} }
if ($this->load_channel_state($channel)['sync_loading']) { 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; return;
} }
$this->load_channel_state($channel)['sync_loading'] = true; $this->load_channel_state($channel)['sync_loading'] = true;
try { try {
$input = $this->get_info('channel#' . $channel); $input = $this->get_info('channel#'.$channel);
if (!isset($input['InputChannel'])) { if (!isset($input['InputChannel'])) {
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database'); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
@ -180,17 +197,20 @@ trait UpdateHandler
$this->load_channel_state($channel)['sync_loading'] = false; $this->load_channel_state($channel)['sync_loading'] = false;
} }
$this->load_channel_state($channel)['sync_loading'] = true; $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 { try {
$difference = $this->method_call('updates.getChannelDifference', ['channel' => $input, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $this->load_channel_state($channel)['pts'], 'limit' => 30], ['datacenter' => $this->datacenter->curdc]); $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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->getMessage() === "You haven't joined this channel/supergroup") { if ($e->getMessage() === "You haven't joined this channel/supergroup") {
return false; return false;
} }
throw $e; throw $e;
} catch (\danog\MadelineProto\PTSException $e) { } catch (\danog\MadelineProto\PTSException $e) {
unset($this->channels_state[$channel]); unset($this->channels_state[$channel]);
$this->load_channel_state($channel)['sync_loading'] = false; $this->load_channel_state($channel)['sync_loading'] = false;
return $this->get_channel_difference($channel); return $this->get_channel_difference($channel);
} finally { } finally {
$this->load_channel_state($channel)['sync_loading'] = false; $this->load_channel_state($channel)['sync_loading'] = false;
@ -202,6 +222,7 @@ trait UpdateHandler
break; break;
case 'updates.channelDifference': case 'updates.channelDifference':
$this->load_channel_state($channel)['sync_loading'] = true; $this->load_channel_state($channel)['sync_loading'] = true;
try { try {
$this->set_channel_state($channel, $difference); $this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['new_messages'], $channel); $this->handle_update_messages($difference['new_messages'], $channel);
@ -215,8 +236,9 @@ trait UpdateHandler
} }
break; break;
case 'updates.channelDifferenceTooLong': 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; $this->load_channel_state($channel)['sync_loading'] = true;
try { try {
$this->set_channel_state($channel, $difference); $this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['messages'], $channel); $this->handle_update_messages($difference['messages'], $channel);
@ -227,10 +249,11 @@ trait UpdateHandler
$this->get_channel_difference($channel); $this->get_channel_difference($channel);
break; break;
default: 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; break;
} }
} }
public function set_update_state($data) public function set_update_state($data)
{ {
if (isset($data['pts']) && $data['pts'] !== 0) { if (isset($data['pts']) && $data['pts'] !== 0) {
@ -246,6 +269,7 @@ trait UpdateHandler
$this->load_update_state()['date'] = $data['date']; $this->load_update_state()['date'] = $data['date'];
} }
} }
public function &load_update_state() public function &load_update_state()
{ {
if (!isset($this->updates_state['qts'])) { if (!isset($this->updates_state['qts'])) {
@ -255,8 +279,10 @@ trait UpdateHandler
$this->got_state = true; $this->got_state = true;
$this->set_update_state($this->get_updates_state()); $this->set_update_state($this->get_updates_state());
} }
return $this->updates_state; return $this->updates_state;
} }
public function get_updates_difference() public function get_updates_difference()
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
@ -264,6 +290,7 @@ trait UpdateHandler
} }
if ($this->updates_state['sync_loading']) { if ($this->updates_state['sync_loading']) {
\danog\MadelineProto\Logger::log(['Not fetching normal difference, I am already fetching it']); \danog\MadelineProto\Logger::log(['Not fetching normal difference, I am already fetching it']);
return false; return false;
} }
$this->updates_state['sync_loading'] = true; $this->updates_state['sync_loading'] = true;
@ -278,7 +305,8 @@ trait UpdateHandler
$this->updates_state['sync_loading'] = false; $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 { try {
switch ($difference['_']) { switch ($difference['_']) {
case 'updates.differenceEmpty': case 'updates.differenceEmpty':
@ -303,37 +331,42 @@ trait UpdateHandler
$this->get_updates_difference(); $this->get_updates_difference();
break; break;
default: 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; break;
} }
} finally { } finally {
$this->updates_state['sync_loading'] = false; $this->updates_state['sync_loading'] = false;
} }
} }
public function get_updates_state() public function get_updates_state()
{ {
$last = $this->updates_state['sync_loading']; $last = $this->updates_state['sync_loading'];
$this->updates_state['sync_loading'] = true; $this->updates_state['sync_loading'] = true;
try { try {
$data = $this->method_call('updates.getState', [], ['datacenter' => $this->datacenter->curdc]); $data = $this->method_call('updates.getState', [], ['datacenter' => $this->datacenter->curdc]);
$this->get_cdn_config($this->datacenter->curdc); $this->get_cdn_config($this->datacenter->curdc);
} finally { } finally {
$this->updates_state['sync_loading'] = $last; $this->updates_state['sync_loading'] = $last;
} }
return $data; return $data;
} }
public function handle_update($update, $options = []) public function handle_update($update, $options = [])
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
return; 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; $channel_id = false;
switch ($update['_']) { switch ($update['_']) {
case 'updateNewChannelMessage': case 'updateNewChannelMessage':
case 'updateEditChannelMessage': case 'updateEditChannelMessage':
if ($update['message']['_'] === 'messageEmpty') { if ($update['message']['_'] === 'messageEmpty') {
\danog\MadelineProto\Logger::log(['Got message empty, not saving'], \danog\MadelineProto\Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Got message empty, not saving'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
return false; return false;
} }
$channel_id = $update['message']['to_id']['channel_id']; $channel_id = $update['message']['to_id']['channel_id'];
@ -346,24 +379,26 @@ trait UpdateHandler
\danog\MadelineProto\Logger::log(['Got channel too long update, getting difference...'], \danog\MadelineProto\Logger::VERBOSE); \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'])) { 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); \danog\MadelineProto\Logger::log(['I do not have the channel in the states and the pts is not set.'], \danog\MadelineProto\Logger::ERROR);
return; return;
} }
break; break;
} }
if ($channel_id === false) { if ($channel_id === false) {
$cur_state =& $this->load_update_state(); $cur_state = &$this->load_update_state();
} else { } 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'])) { 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); \danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
return false; return false;
}*/ }*/
switch ($update['_']) { switch ($update['_']) {
case 'updateChannelTooLong': case 'updateChannelTooLong':
$this->get_channel_difference($channel_id); $this->get_channel_difference($channel_id);
return false; return false;
case 'updateNewMessage': case 'updateNewMessage':
case 'updateEditMessage': case 'updateEditMessage':
@ -376,37 +411,42 @@ trait UpdateHandler
} else { } else {
$this->get_updates_difference(); $this->get_updates_difference();
} }
return false; return false;
} }
break; break;
default: default:
if ($channel_id !== false && !$this->peer_isset($this->to_supergroup($channel_id))) { 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; return false;
} }
break; break;
} }
if (isset($update['pts'])) { if (isset($update['pts'])) {
if ($update['pts'] < $cur_state['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; return false;
} }
if ($cur_state['pts'] + (isset($update['pts_count']) ? $update['pts_count'] : 0) !== $update['pts']) { 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))) { if ($channel_id !== false && $this->peer_isset($this->to_supergroup($channel_id))) {
$this->get_channel_difference($channel_id); $this->get_channel_difference($channel_id);
} else { } else {
$this->get_updates_difference(); $this->get_updates_difference();
} }
return false; return false;
} }
if (isset($update['message']['id'], $update['message']['to_id'])) { if (isset($update['message']['id'], $update['message']['to_id'])) {
if (!$this->check_msg_id($update['message'])) { 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; 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']; $cur_state['pts'] = $update['pts'];
if ($channel_id === false && isset($options['date']) && $cur_state['date'] < $options['date']) { if ($channel_id === false && isset($options['date']) && $cur_state['date'] < $options['date']) {
$cur_state['date'] = $options['date']; $cur_state['date'] = $options['date'];
@ -416,8 +456,9 @@ trait UpdateHandler
$seq = $options['seq']; $seq = $options['seq'];
$seq_start = isset($options['seq_start']) ? $options['seq_start'] : $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']) { 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(); $this->get_updates_difference();
return false; return false;
} }
if ($cur_state['seq'] !== $seq) { if ($cur_state['seq'] !== $seq) {
@ -429,6 +470,7 @@ trait UpdateHandler
} }
$this->save_update($update); $this->save_update($update);
} }
public function handle_multiple_update($updates, $options = [], $channel = false) public function handle_multiple_update($updates, $options = [], $channel = false)
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
@ -444,6 +486,7 @@ trait UpdateHandler
} }
} }
} }
public function handle_update_messages($messages, $channel = false) public function handle_update_messages($messages, $channel = false)
{ {
if (!$this->settings['updates']['handle_updates']) { if (!$this->settings['updates']['handle_updates']) {
@ -453,6 +496,7 @@ 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]); $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) public function save_update($update)
{ {
array_walk($this->calls, function ($controller, $id) { array_walk($this->calls, function ($controller, $id) {
@ -463,11 +507,13 @@ trait UpdateHandler
if ($update['_'] === 'updateDcOptions') { if ($update['_'] === 'updateDcOptions') {
\danog\MadelineProto\Logger::log(['Got new dc options'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Got new dc options'], \danog\MadelineProto\Logger::VERBOSE);
$this->parse_dc_options($update['dc_options']); $this->parse_dc_options($update['dc_options']);
return; return;
} }
if ($update['_'] === 'updatePhoneCall') { 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); \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; return;
} }
switch ($update['phone_call']['_']) { switch ($update['phone_call']['_']) {
@ -495,6 +541,7 @@ trait UpdateHandler
if (!isset($this->calls[$update['phone_call']['id']])) { if (!isset($this->calls[$update['phone_call']['id']])) {
return; return;
} }
return $this->calls[$update['phone_call']['id']]->discard(['_' => 'phoneCallDiscardReasonHangup'], [], $update['phone_call']['need_debug']); return $this->calls[$update['phone_call']['id']]->discard(['_' => 'phoneCallDiscardReasonHangup'], [], $update['phone_call']['need_debug']);
} }
} }
@ -504,17 +551,20 @@ trait UpdateHandler
$cur_state['qts'] = $update['qts']; $cur_state['qts'] = $update['qts'];
} }
if ($update['qts'] < $cur_state['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; return false;
} }
if ($update['qts'] > $cur_state['qts'] + 1) { 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(); $this->get_updates_difference();
return false; 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->method_call('messages.receivedQueue', ['max_qts' => $cur_state['qts'] = $update['qts']], ['datacenter' => $this->datacenter->curdc]);
$this->handle_encrypted_update($update); $this->handle_encrypted_update($update);
return; return;
} }
/* /*
@ -528,11 +578,11 @@ trait UpdateHandler
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'])) { 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; return;
} }
\danog\MadelineProto\Logger::log(['Accepting secret chat ' . $update['chat']['id']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Accepting secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->accept_secret_chat($update['chat']); $this->accept_secret_chat($update['chat']);
break; break;
case 'encryptedChatDiscarded': case 'encryptedChatDiscarded':
\danog\MadelineProto\Logger::log(['Deleting secret chat ' . $update['chat']['id'] . ' because it was revoked by the other user'], \danog\MadelineProto\Logger::NOTICE); \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']])) { if (isset($this->secret_chats[$update['chat']['id']])) {
unset($this->secret_chats[$update['chat']['id']]); unset($this->secret_chats[$update['chat']['id']]);
} }
@ -541,7 +591,7 @@ trait UpdateHandler
} }
break; break;
case 'encryptedChat': case 'encryptedChat':
\danog\MadelineProto\Logger::log(['Completing creation of secret chat ' . $update['chat']['id']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Completing creation of secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);
$this->complete_secret_chat($update['chat']); $this->complete_secret_chat($update['chat']);
break; break;
} }
@ -556,19 +606,21 @@ trait UpdateHandler
if (isset($update['message']['from_id']) && $update['message']['from_id'] === $this->authorization['user']['id']) { if (isset($update['message']['from_id']) && $update['message']['from_id'] === $this->authorization['user']['id']) {
$update['message']['out'] = true; $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'])) { if (isset($this->settings['pwr']['strict']) && $this->settings['pwr']['strict'] && isset($this->settings['pwr']['update_handler'])) {
$this->pwr_update_handler($update); $this->pwr_update_handler($update);
} else { } 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) public function pwr_webhook($update)
{ {
$payload = json_encode($update); $payload = json_encode($update);
\danog\MadelineProto\Logger::log([$update, $payload, json_last_error()]); \danog\MadelineProto\Logger::log([$update, $payload, json_last_error()]);
if ($payload === '') { if ($payload === '') {
\danog\MadelineProto\Logger::log(['EMPTY UPDATE']); \danog\MadelineProto\Logger::log(['EMPTY UPDATE']);
return false; return false;
} }
$ch = curl_init(); $ch = curl_init();
@ -588,7 +640,7 @@ trait UpdateHandler
} }
$result = curl_exec($ch); $result = curl_exec($ch);
curl_close($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); $result = json_decode($result, true);
if (is_array($result) && isset($result['method']) && $result['method'] != '' && is_string($result['method'])) { if (is_array($result) && isset($result['method']) && $result['method'] != '' && is_string($result['method'])) {
try { try {
@ -600,4 +652,4 @@ trait UpdateHandler
} }
} }
} }
} }

View File

@ -10,8 +10,9 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class NothingInTheSocketException extends \Exception class NothingInTheSocketException extends \Exception
{ {
} }

View File

@ -10,18 +10,21 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class PTSException extends \Exception class PTSException extends \Exception
{ {
use TL\PrettyException; use TL\PrettyException;
public function __toString() 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 = '') public function __construct($message, $file = '')
{ {
parent::__construct($message); parent::__construct($message);
$this->prettify_tl($file); $this->prettify_tl($file);
} }
} }

View File

@ -10,23 +10,38 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
interface Proxy interface Proxy
{ {
public function __construct($domain, $type, $protocol); public function __construct($domain, $type, $protocol);
public function setOption($level, $name, $value); public function setOption($level, $name, $value);
public function getOption($level, $name); public function getOption($level, $name);
public function setBlocking($blocking); public function setBlocking($blocking);
public function bind($address, $port = 0); public function bind($address, $port = 0);
public function listen($backlog = 0); public function listen($backlog = 0);
public function accept(); public function accept();
public function connect($address, $port = 0); public function connect($address, $port = 0);
public function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0); public function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0);
public function read($length, $flags = 0); public function read($length, $flags = 0);
public function write($buffer, $length = -1); public function write($buffer, $length = -1);
public function send($data, $length, $flags); public function send($data, $length, $flags);
public function close(); public function close();
public function getPeerName($port = true); public function getPeerName($port = true);
public function getSockName($port = true); public function getSockName($port = true);
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class RPCErrorException extends \Exception class RPCErrorException extends \Exception
@ -17,20 +18,24 @@ class RPCErrorException extends \Exception
use TL\PrettyException; use TL\PrettyException;
private $fetched = false; private $fetched = false;
public static $rollbar = true; public static $rollbar = true;
public function getMess() public function getMess()
{ {
if ($this->fetched === false) { 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']) { if (isset($res['ok']) && $res['ok']) {
$this->message = $res['result']; $this->message = $res['result'];
} }
} }
return $this->message; return $this->message;
} }
public function __toString() 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) public function __construct($message = null, $code = 0, Exception $previous = null)
{ {
$this->rpc = $message; $this->rpc = $message;
@ -186,4 +191,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); $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

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class RSA class RSA
@ -20,6 +21,7 @@ class RSA
public $e; public $e;
public $n; public $n;
public $fp; public $fp;
public function __magic_construct($rsa_key) public function __magic_construct($rsa_key)
{ {
//if ($this->unserialized($rsa_key)) return true; //if ($this->unserialized($rsa_key)) return true;
@ -30,16 +32,20 @@ class RSA
$this->n = \phpseclib\Common\Functions\Objects::getVar($key, 'modulus'); $this->n = \phpseclib\Common\Functions\Objects::getVar($key, 'modulus');
$this->e = \phpseclib\Common\Functions\Objects::getVar($key, 'exponent'); $this->e = \phpseclib\Common\Functions\Objects::getVar($key, 'exponent');
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['computing_fingerprint']], Logger::ULTRA_VERBOSE); \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; return true;
} }
public function __sleep() public function __sleep()
{ {
return ['e', 'n', 'fp']; return ['e', 'n', 'fp'];
} }
public function encrypt($data) public function encrypt($data)
{ {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['rsa_encrypting']], Logger::VERBOSE); \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(); return (new \phpseclib\Math\BigInteger($data, 256))->powMod($this->e, $this->n)->toBytes();
} }
} }

View File

@ -10,8 +10,9 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class ResponseException extends \Exception class ResponseException extends \Exception
{ {
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\SecretChats; namespace danog\MadelineProto\SecretChats;
/** /**
@ -21,12 +22,14 @@ trait AuthKeyHandler
{ {
protected $temp_requested_secret_chats = []; protected $temp_requested_secret_chats = [];
protected $secret_chats = []; protected $secret_chats = [];
public function accept_secret_chat($params) public function accept_secret_chat($params)
{ {
//var_dump($params['id'],$this->secret_chat_status($params['id'])); //var_dump($params['id'],$this->secret_chat_status($params['id']));
if ($this->secret_chat_status($params['id']) !== 0) { if ($this->secret_chat_status($params['id']) !== 0) {
//var_dump($this->secret_chat_status($params['id'])); //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; return false;
} }
$dh_config = $this->get_dh_config(); $dh_config = $this->get_dh_config();
@ -45,8 +48,9 @@ trait AuthKeyHandler
$this->method_call('messages.acceptEncryption', ['peer' => $params['id'], 'g_b' => $g_b->toBytes(), 'key_fingerprint' => $key['fingerprint']], ['datacenter' => $this->datacenter->curdc]); $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->notify_layer($params['id']);
$this->handle_pending_updates(); $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) public function request_secret_chat($user)
{ {
$user = $this->get_info($user); $user = $this->get_info($user);
@ -54,7 +58,7 @@ trait AuthKeyHandler
throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database'); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
$user = $user['InputUser']; $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(); $dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
$a = new \phpseclib\Math\BigInteger($this->random(256), 256); $a = new \phpseclib\Math\BigInteger($this->random(256), 256);
@ -65,14 +69,17 @@ trait AuthKeyHandler
$this->temp_requested_secret_chats[$res['id']] = $a; $this->temp_requested_secret_chats[$res['id']] = $a;
$this->handle_pending_updates(); $this->handle_pending_updates();
$this->get_updates_difference(); $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']; return $res['id'];
} }
public function complete_secret_chat($params) public function complete_secret_chat($params)
{ {
if ($this->secret_chat_status($params['id']) !== 1) { if ($this->secret_chat_status($params['id']) !== 1) {
//var_dump($this->secret_chat_status($params['id'])); //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; return false;
} }
$dh_config = $this->get_dh_config(); $dh_config = $this->get_dh_config();
@ -84,6 +91,7 @@ trait AuthKeyHandler
//var_dump($key); //var_dump($key);
if ($key['fingerprint'] !== $params['key_fingerprint']) { if ($key['fingerprint'] !== $params['key_fingerprint']) {
$this->discard_secret_chat($params['id']); $this->discard_secret_chat($params['id']);
throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!'); throw new \danog\MadelineProto\SecurityException('Invalid key fingerprint!');
} }
$key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16); $key['visualization_orig'] = substr(sha1($key['auth_key'], true), 16);
@ -91,19 +99,22 @@ 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->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->notify_layer($params['id']);
$this->handle_pending_updates(); $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) public function notify_layer($chat)
{ {
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]); $this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]);
} }
protected $temp_rekeyed_secret_chats = []; protected $temp_rekeyed_secret_chats = [];
public function rekey($chat) public function rekey($chat)
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) { if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
return; 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(); $dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Generating a...'], \danog\MadelineProto\Logger::VERBOSE);
$a = new \phpseclib\Math\BigInteger($this->random(256), 256); $a = new \phpseclib\Math\BigInteger($this->random(256), 256);
@ -116,8 +127,10 @@ 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->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->handle_pending_updates();
$this->get_updates_difference(); $this->get_updates_difference();
return $e; return $e;
} }
public function accept_rekey($chat, $params) public function accept_rekey($chat, $params)
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 0) { if ($this->secret_chats[$chat]['rekeying'][0] !== 0) {
@ -129,10 +142,11 @@ trait AuthKeyHandler
} }
if ($my_exchange_id === $other_exchange_id) { if ($my_exchange_id === $other_exchange_id) {
$this->secret_chats[$chat]['rekeying'] = [0]; $this->secret_chats[$chat]['rekeying'] = [0];
return; 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(); $dh_config = $this->get_dh_config();
\danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Generating b...'], \danog\MadelineProto\Logger::VERBOSE);
$b = new \phpseclib\Math\BigInteger($this->random(256), 256); $b = new \phpseclib\Math\BigInteger($this->random(256), 256);
@ -150,13 +164,15 @@ trait AuthKeyHandler
$this->handle_pending_updates(); $this->handle_pending_updates();
$this->get_updates_difference(); $this->get_updates_difference();
} }
public function commit_rekey($chat, $params) public function commit_rekey($chat, $params)
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) { if ($this->secret_chats[$chat]['rekeying'][0] !== 1 || !isset($this->temp_rekeyed_secret_chats[$params['exchange_id']])) {
$this->secret_chats[$chat]['rekeying'] = [0]; $this->secret_chats[$chat]['rekeying'] = [0];
return; 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(); $dh_config = $this->get_dh_config();
$params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256); $params['g_b'] = new \phpseclib\Math\BigInteger($params['g_b'], 256);
$this->check_G($params['g_b'], $dh_config['p']); $this->check_G($params['g_b'], $dh_config['p']);
@ -166,6 +182,7 @@ trait AuthKeyHandler
$key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20); $key['visualization_46'] = substr(hash('sha256', $key['auth_key'], true), 20);
if ($key['fingerprint'] !== $params['key_fingerprint']) { 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]); $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!'); 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]); $this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionCommitKey', 'exchange_id' => $params['exchange_id'], 'key_fingerprint' => $key['fingerprint']]]], ['datacenter' => $this->datacenter->curdc]);
@ -178,6 +195,7 @@ trait AuthKeyHandler
$this->handle_pending_updates(); $this->handle_pending_updates();
$this->get_updates_difference(); $this->get_updates_difference();
} }
public function complete_rekey($chat, $params) public function complete_rekey($chat, $params)
{ {
if ($this->secret_chats[$chat]['rekeying'][0] !== 2 || !isset($this->temp_rekeyed_secret_chats['fingerprint'])) { if ($this->secret_chats[$chat]['rekeying'][0] !== 2 || !isset($this->temp_rekeyed_secret_chats['fingerprint'])) {
@ -185,9 +203,10 @@ trait AuthKeyHandler
} }
if ($this->temp_rekeyed_secret_chats['fingerprint'] !== $params['key_fingerprint']) { 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]); $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!'); 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]['rekeying'] = [0];
$this->secret_chats[$chat]['old_key'] = $this->secret_chats[$chat]['key']; $this->secret_chats[$chat]['old_key'] = $this->secret_chats[$chat]['key'];
$this->secret_chats[$chat]['key'] = $this->temp_rekeyed_secret_chats[$chat]; $this->secret_chats[$chat]['key'] = $this->temp_rekeyed_secret_chats[$chat];
@ -195,9 +214,11 @@ trait AuthKeyHandler
$this->secret_chats[$chat]['updated'] = time(); $this->secret_chats[$chat]['updated'] = time();
unset($this->temp_rekeyed_secret_chats[$params['exchange_id']]); unset($this->temp_rekeyed_secret_chats[$params['exchange_id']]);
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNoop']]], ['datacenter' => $this->datacenter->curdc]); $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; return true;
} }
public function secret_chat_status($chat) public function secret_chat_status($chat)
{ {
if (isset($this->secret_chats[$chat])) { if (isset($this->secret_chats[$chat])) {
@ -206,15 +227,18 @@ trait AuthKeyHandler
if (isset($this->temp_requested_secret_chats[$chat])) { if (isset($this->temp_requested_secret_chats[$chat])) {
return 1; return 1;
} }
return 0; return 0;
} }
public function get_secret_chat($chat) public function get_secret_chat($chat)
{ {
return $this->secret_chats[$chat]; return $this->secret_chats[$chat];
} }
public function discard_secret_chat($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]); //var_dump(debug_backtrace(0)[0]);
if (isset($this->secret_chats[$chat])) { if (isset($this->secret_chats[$chat])) {
unset($this->secret_chats[$chat]); unset($this->secret_chats[$chat]);
@ -225,6 +249,7 @@ trait AuthKeyHandler
if (isset($this->temp_rekeyed_secret_chats[$chat])) { if (isset($this->temp_rekeyed_secret_chats[$chat])) {
unset($this->temp_rekeyed_secret_chats[$chat]); unset($this->temp_rekeyed_secret_chats[$chat]);
} }
try { try {
$this->method_call('messages.discardEncryption', ['chat_id' => $chat], ['datacenter' => $this->datacenter->curdc]); $this->method_call('messages.discardEncryption', ['chat_id' => $chat], ['datacenter' => $this->datacenter->curdc]);
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -233,4 +258,4 @@ trait AuthKeyHandler
} }
} }
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\SecretChats; namespace danog\MadelineProto\SecretChats;
/** /**
@ -21,6 +22,7 @@ trait MessageHandler
{ {
if (!isset($this->secret_chats[$chat_id])) { if (!isset($this->secret_chats[$chat_id])) {
\danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id)); \danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $chat_id));
return false; return false;
} }
$this->secret_chats[$chat_id]['ttr']--; $this->secret_chats[$chat_id]['ttr']--;
@ -33,27 +35,30 @@ trait MessageHandler
} }
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message; $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->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) { if ($this->secret_chats[$chat_id]['mtproto'] === 2) {
$padding = $this->posmod(-strlen($message), 16); $padding = $this->posmod(-strlen($message), 16);
if ($padding < 12) { if ($padding < 12) {
$padding += 16; $padding += 16;
} }
$message .= $this->random($padding); $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']); 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 { } else {
$message_key = substr(sha1($message, true), -16); $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); 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->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; return $message;
} }
public function handle_encrypted_update($message, $test = false) public function handle_encrypted_update($message, $test = false)
{ {
if (!isset($this->secret_chats[$message['message']['chat_id']])) { 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'])); \danog\MadelineProto\Logger::log(sprintf(\danog\MadelineProto\Lang::$current_lang['secret_chat_skipping'], $message['message']['chat_id']));
return false; return false;
} }
$auth_key_id = substr($message['message']['bytes'], 0, 8); $auth_key_id = substr($message['message']['bytes'], 0, 8);
@ -62,36 +67,40 @@ trait MessageHandler
if (isset($this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint'])) { 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']) { if ($auth_key_id !== $this->secret_chats[$message['message']['chat_id']]['old_key']['fingerprint']) {
$this->discard_secret_chat($message['message']['chat_id']); $this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']); throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
} }
$old = true; $old = true;
} else { } else {
$this->discard_secret_chat($message['message']['chat_id']); $this->discard_secret_chat($message['message']['chat_id']);
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']); throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['fingerprint_mismatch']);
} }
} }
$message_key = substr($message['message']['bytes'], 8, 16); $message_key = substr($message['message']['bytes'], 8, 16);
$encrypted_data = substr($message['message']['bytes'], 24); $encrypted_data = substr($message['message']['bytes'], 24);
if ($this->secret_chats[$message['message']['chat_id']]['mtproto'] === 2) { 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 { try {
$message_data = $this->try_mtproto_v2_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data); $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) { } 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); $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; $this->secret_chats[$message['message']['chat_id']]['mtproto'] = 1;
} }
} else { } 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 { try {
$message_data = $this->try_mtproto_v1_decrypt($message_key, $message['message']['chat_id'], $old, $encrypted_data); $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) { } 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); $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; $this->secret_chats[$message['message']['chat_id']]['mtproto'] = 2;
} }
} }
@ -105,6 +114,7 @@ trait MessageHandler
$this->secret_chats[$message['message']['chat_id']]['incoming'][$this->secret_chats[$message['message']['chat_id']]['in_seq_no']] = $message['message']; $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); $this->handle_decrypted_update($message);
} }
public function try_mtproto_v1_decrypt($message_key, $chat_id, $old, $encrypted_data) 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); list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->secret_chats[$chat_id][$old ? 'old_key' : 'key']['auth_key'], true);
@ -123,8 +133,10 @@ trait MessageHandler
if (strlen($decrypted_data) % 16 != 0) { if (strlen($decrypted_data) % 16 != 0) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']); throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
} }
return $message_data; return $message_data;
} }
public function try_mtproto_v2_decrypt($message_key, $chat_id, $old, $encrypted_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']); 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']);
@ -134,7 +146,7 @@ trait MessageHandler
if ($message_data_length > strlen($decrypted_data)) { if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['msg_data_length_too_big']); 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']); 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) {
@ -146,6 +158,7 @@ trait MessageHandler
if (strlen($decrypted_data) % 16 != 0) { if (strlen($decrypted_data) % 16 != 0) {
throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']); throw new \danog\MadelineProto\SecurityException(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16']);
} }
return $message_data; return $message_data;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\SecretChats; namespace danog\MadelineProto\SecretChats;
/** /**
@ -28,12 +29,15 @@ trait ResponseHandler
switch ($update['message']['decrypted_message']['action']['_']) { switch ($update['message']['decrypted_message']['action']['_']) {
case 'decryptedMessageActionRequestKey': case 'decryptedMessageActionRequestKey':
$this->accept_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']); $this->accept_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return; return;
case 'decryptedMessageActionAcceptKey': case 'decryptedMessageActionAcceptKey':
$this->commit_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']); $this->commit_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return; return;
case 'decryptedMessageActionCommitKey': case 'decryptedMessageActionCommitKey':
$this->complete_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']); $this->complete_rekey($update['message']['chat_id'], $update['message']['decrypted_message']['action']);
return; return;
case 'decryptedMessageActionNotifyLayer': case 'decryptedMessageActionNotifyLayer':
$this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['action']['layer']; $this->secret_chats[$update['message']['chat_id']]['layer'] = $update['message']['decrypted_message']['action']['layer'];
@ -43,9 +47,11 @@ trait ResponseHandler
if ($update['message']['decrypted_message']['action']['layer'] >= 73) { if ($update['message']['decrypted_message']['action']['layer'] >= 73) {
$this->secret_chats[$update['message']['chat_id']]['mtproto'] = 2; $this->secret_chats[$update['message']['chat_id']]['mtproto'] = 2;
} }
return; return;
case 'decryptedMessageActionSetMessageTTL': case 'decryptedMessageActionSetMessageTTL':
$this->secret_chats[$update['message']['chat_id']]['ttl'] = $update['message']['decrypted_message']['action']['ttl_seconds']; $this->secret_chats[$update['message']['chat_id']]['ttl'] = $update['message']['decrypted_message']['action']['ttl_seconds'];
return; return;
case 'decryptedMessageActionNoop': case 'decryptedMessageActionNoop':
return; return;
@ -54,13 +60,14 @@ trait ResponseHandler
$update['message']['decrypted_message']['action']['end_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']['start_seq_no'] /= 2;
$update['message']['decrypted_message']['action']['end_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); \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) { 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']) { 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']); //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]); $this->method_call('messages.sendEncrypted', ['peer' => $update['message']['chat_id'], 'message' => $update['message']['decrypted_message']], ['datacenter' => $this->datacenter->curdc]);
} }
} }
return; return;
default: 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(['_' => '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']]);
@ -84,8 +91,8 @@ trait ResponseHandler
} }
break; break;
default: 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; break;
} }
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\SecretChats; namespace danog\MadelineProto\SecretChats;
/** /**
@ -25,6 +26,7 @@ trait SeqNoHandler
if (isset($message['decrypted_message']['in_seq_no'])) { 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) { if (($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2 < $last) {
$this->discard_secret_chat($chat_id); $this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is not increasing'); 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; $last = ($message['decrypted_message']['in_seq_no'] - $this->secret_chats[$chat_id]['out_seq_no_x']) / 2;
@ -32,10 +34,13 @@ trait SeqNoHandler
} }
if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) { if ($seqno > $this->secret_chats[$chat_id]['out_seq_no'] + 1) {
$this->discard_secret_chat($chat_id); $this->discard_secret_chat($chat_id);
throw new \danog\MadelineProto\SecurityException('in_seq_no is too big'); throw new \danog\MadelineProto\SecurityException('in_seq_no is too big');
} }
return true; return true;
} }
public function check_secret_out_seq_no($chat_id, $seqno) public function check_secret_out_seq_no($chat_id, $seqno)
{ {
$seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2; $seqno = ($seqno - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2;
@ -44,7 +49,8 @@ trait SeqNoHandler
if (isset($message['decrypted_message']['out_seq_no']) && $C < $this->secret_chats[$chat_id]['in_seq_no']) { 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) { if (($message['decrypted_message']['out_seq_no'] - $this->secret_chats[$chat_id]['in_seq_no_x']) / 2 !== $C) {
$this->discard_secret_chat($chat_id); $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 { } else {
$C++; $C++;
} }
@ -53,22 +59,27 @@ trait SeqNoHandler
//var_dump($C, $seqno); //var_dump($C, $seqno);
if ($seqno < $C) { if ($seqno < $C) {
// <= C // <= C
\danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno ' . $seqno]); \danog\MadelineProto\Logger::log(['WARNING: dropping repeated message with seqno '.$seqno]);
return false; return false;
} }
if ($seqno > $C) { if ($seqno > $C) {
// > C+1 // > C+1
$this->discard_secret_chat($chat_id); $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; return true;
} }
public function generate_secret_in_seq_no($chat) 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) 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

@ -10,8 +10,9 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
class SecurityException extends \Exception class SecurityException extends \Exception
{ {
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
/** /**
@ -18,9 +19,11 @@ namespace danog\MadelineProto;
class Serialization class Serialization
{ {
public static $instances = []; public static $instances = [];
public static function serialize_all($exception) public static function serialize_all($exception)
{ {
echo $exception . PHP_EOL; echo $exception.PHP_EOL;
return; return;
foreach (self::$instances as $instance) { foreach (self::$instances as $instance) {
if (isset($instance->session)) { if (isset($instance->session)) {
@ -28,13 +31,16 @@ class Serialization
} }
} }
} }
public static function realpaths($file) public static function realpaths($file)
{ {
if ($file[0] !== '/') { 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. * Serialize API class.
* *
@ -59,6 +65,7 @@ class Serialization
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w'); $realpaths['lockfile'] = fopen($realpaths['lockfile'], 'w');
\danog\MadelineProto\Logger::log(['Waiting for exclusive lock of serialization lockfile...']); \danog\MadelineProto\Logger::log(['Waiting for exclusive lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_EX); flock($realpaths['lockfile'], LOCK_EX);
try { try {
$wrote = file_put_contents($realpaths['tempfile'], serialize($instance)); $wrote = file_put_contents($realpaths['tempfile'], serialize($instance));
rename($realpaths['tempfile'], $realpaths['file']); rename($realpaths['tempfile'], $realpaths['file']);
@ -66,8 +73,10 @@ class Serialization
flock($realpaths['lockfile'], LOCK_UN); flock($realpaths['lockfile'], LOCK_UN);
fclose($realpaths['lockfile']); fclose($realpaths['lockfile']);
} }
return $wrote; return $wrote;
} }
/** /**
* Deserialize API class. * Deserialize API class.
* *
@ -88,6 +97,7 @@ class Serialization
$realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r'); $realpaths['lockfile'] = fopen($realpaths['lockfile'], 'r');
\danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']); \danog\MadelineProto\Logger::log(['Waiting for shared lock of serialization lockfile...']);
flock($realpaths['lockfile'], LOCK_SH); flock($realpaths['lockfile'], LOCK_SH);
try { try {
$unserialized = file_get_contents($realpaths['file']); $unserialized = file_get_contents($realpaths['file']);
} finally { } finally {
@ -96,10 +106,11 @@ class Serialization
} }
$tounserialize = str_replace('O:26:"danog\\MadelineProto\\Button":', 'O:35:"danog\\MadelineProto\\TL\\Types\\Button":', $unserialized); $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) { 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('\\danog\\MadelineProto\\'.$class);
} }
class_exists('\\Volatile'); class_exists('\\Volatile');
\danog\MadelineProto\Logger::class_exists(); \danog\MadelineProto\Logger::class_exists();
try { try {
$unserialized = unserialize($tounserialize); $unserialized = unserialize($tounserialize);
} catch (\danog\MadelineProto\Bug74586Exception $e) { } catch (\danog\MadelineProto\Bug74586Exception $e) {
@ -124,6 +135,7 @@ class Serialization
$unserialized->session = $filename; $unserialized->session = $filename;
} }
self::$instances[spl_object_hash($unserialized)] = $unserialized; self::$instances[spl_object_hash($unserialized)] = $unserialized;
return $unserialized; return $unserialized;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
trait BotAPI trait BotAPI
@ -18,6 +19,7 @@ trait BotAPI
{ {
return html_entity_decode(preg_replace('#< *br */? *>#', "\n", $stuff)); return html_entity_decode(preg_replace('#< *br */? *>#', "\n", $stuff));
} }
public function parse_buttons($rows) public function parse_buttons($rows)
{ {
$newrows = []; $newrows = [];
@ -53,8 +55,10 @@ trait BotAPI
} }
$key++; $key++;
} }
return $newrows; return $newrows;
} }
public function parse_reply_markup($markup) public function parse_reply_markup($markup)
{ {
if (isset($markup['force_reply']) && $markup['force_reply']) { if (isset($markup['force_reply']) && $markup['force_reply']) {
@ -83,8 +87,10 @@ trait BotAPI
$markup['rows'] = $this->parse_buttons($markup['inline_keyboard']); $markup['rows'] = $this->parse_buttons($markup['inline_keyboard']);
unset($markup['inline_keyboard']); unset($markup['inline_keyboard']);
} }
return $markup; return $markup;
} }
public function MTProto_to_botAPI($data, $sent_arguments = []) public function MTProto_to_botAPI($data, $sent_arguments = [])
{ {
$newd = []; $newd = [];
@ -92,6 +98,7 @@ trait BotAPI
foreach ($data as $key => $element) { foreach ($data as $key => $element) {
$newd[$key] = $this->MTProto_to_botAPI($element, $sent_arguments); $newd[$key] = $this->MTProto_to_botAPI($element, $sent_arguments);
} }
return $newd; return $newd;
} }
switch ($data['_']) { switch ($data['_']) {
@ -109,6 +116,7 @@ trait BotAPI
if (isset($data['media'])) { if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments)); $newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
} }
return $newd; return $newd;
case 'updateNewChannelMessage': case 'updateNewChannelMessage':
case 'updateNewMessage': case 'updateNewMessage':
@ -150,52 +158,64 @@ trait BotAPI
if (isset($data['media'])) { if (isset($data['media'])) {
$newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments)); $newd = array_merge($newd, $this->MTProto_to_botAPI($data['media'], $sent_arguments));
} }
return $newd; return $newd;
case 'messageEntityMention': case 'messageEntityMention':
unset($data['_']); unset($data['_']);
$data['type'] = 'mention'; $data['type'] = 'mention';
return $data; return $data;
case 'messageEntityHashtag': case 'messageEntityHashtag':
unset($data['_']); unset($data['_']);
$data['type'] = 'hashtag'; $data['type'] = 'hashtag';
return $data; return $data;
case 'messageEntityBotCommand': case 'messageEntityBotCommand':
unset($data['_']); unset($data['_']);
$data['type'] = 'bot_command'; $data['type'] = 'bot_command';
return $data; return $data;
case 'messageEntityUrl': case 'messageEntityUrl':
unset($data['_']); unset($data['_']);
$data['type'] = 'url'; $data['type'] = 'url';
return $data; return $data;
case 'messageEntityEmail': case 'messageEntityEmail':
unset($data['_']); unset($data['_']);
$data['type'] = 'email'; $data['type'] = 'email';
return $data; return $data;
case 'messageEntityBold': case 'messageEntityBold':
unset($data['_']); unset($data['_']);
$data['type'] = 'bold'; $data['type'] = 'bold';
return $data; return $data;
case 'messageEntityItalic': case 'messageEntityItalic':
unset($data['_']); unset($data['_']);
$data['type'] = 'italic'; $data['type'] = 'italic';
return $data; return $data;
case 'messageEntityCode': case 'messageEntityCode':
unset($data['_']); unset($data['_']);
$data['type'] = 'code'; $data['type'] = 'code';
return $data; return $data;
case 'messageEntityPre': case 'messageEntityPre':
unset($data['_']); unset($data['_']);
$data['type'] = 'pre'; $data['type'] = 'pre';
return $data; return $data;
case 'messageEntityTextUrl': case 'messageEntityTextUrl':
unset($data['_']); unset($data['_']);
$data['type'] = 'text_url'; $data['type'] = 'text_url';
return $data; return $data;
case 'messageEntityMentionName': case 'messageEntityMentionName':
unset($data['_']); unset($data['_']);
$data['type'] = 'text_mention'; $data['type'] = 'text_mention';
$data['user'] = $this->get_pwr_chat($data['user_id']); $data['user'] = $this->get_pwr_chat($data['user_id']);
unset($data['user_id']); unset($data['user_id']);
return $data; return $data;
case 'messageMediaPhoto': case 'messageMediaPhoto':
if (isset($data['caption'])) { if (isset($data['caption'])) {
@ -205,6 +225,7 @@ trait BotAPI
foreach ($data['photo']['sizes'] as $key => $photo) { foreach ($data['photo']['sizes'] as $key => $photo) {
$res['photo'][$key] = $this->photosize_to_botapi($photo, $data['photo']); $res['photo'][$key] = $this->photosize_to_botapi($photo, $data['photo']);
} }
return $res; return $res;
case 'messageMediaEmpty': case 'messageMediaEmpty':
return []; return [];
@ -218,7 +239,7 @@ trait BotAPI
switch ($attribute['_']) { switch ($attribute['_']) {
case 'documentAttributeFilename': case 'documentAttributeFilename':
$pathinfo = pathinfo($attribute['file_name']); $pathinfo = pathinfo($attribute['file_name']);
$res['ext'] = isset($pathinfo['extension']) ? '.' . $pathinfo['extension'] : ''; $res['ext'] = isset($pathinfo['extension']) ? '.'.$pathinfo['extension'] : '';
$res['file_name'] = $pathinfo['filename']; $res['file_name'] = $pathinfo['filename'];
break; break;
case 'documentAttributeAudio': case 'documentAttributeAudio':
@ -269,29 +290,32 @@ trait BotAPI
if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) { if (isset($audio) && isset($audio['title']) && !isset($res['file_name'])) {
$res['file_name'] = $audio['title']; $res['file_name'] = $audio['title'];
if (isset($audio['performer'])) { if (isset($audio['performer'])) {
$res['file_name'] .= ' - ' . $audio['performer']; $res['file_name'] .= ' - '.$audio['performer'];
} }
} }
if (!isset($res['file_name'])) { if (!isset($res['file_name'])) {
$res['file_name'] = $data['document']['access_hash']; $res['file_name'] = $data['document']['access_hash'];
} }
$res['file_name'] .= '_' . $data['document']['id']; $res['file_name'] .= '_'.$data['document']['id'];
if (isset($res['ext'])) { if (isset($res['ext'])) {
$res['file_name'] .= $res['ext']; $res['file_name'] .= $res['ext'];
unset($res['ext']); unset($res['ext']);
} else { } else {
$res['file_name'] .= $this->get_extension_from_mime($data['document']['mime_type']); $res['file_name'] .= $this->get_extension_from_mime($data['document']['mime_type']);
} }
$data['document']['_'] = 'bot_' . $type_name; $data['document']['_'] = 'bot_'.$type_name;
$res['file_size'] = $data['document']['size']; $res['file_size'] = $data['document']['size'];
$res['mime_type'] = $data['document']['mime_type']; $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))); $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'] : '']; return [$type_name => $res, 'caption' => isset($data['caption']) ? $data['caption'] : ''];
default: 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) public function botAPI_to_MTProto($arguments)
{ {
foreach ($this->botapi_params as $bot => $mtproto) { foreach ($this->botapi_params as $bot => $mtproto) {
@ -306,8 +330,10 @@ trait BotAPI
if (isset($arguments['parse_mode'])) { if (isset($arguments['parse_mode'])) {
$arguments = $this->parse_mode($arguments); $arguments = $this->parse_mode($arguments);
} }
return $arguments; return $arguments;
} }
public function parse_node($node, &$entities, &$nmessage, $recursive = true) public function parse_node($node, &$entities, &$nmessage, $recursive = true)
{ {
switch ($node->nodeName) { switch ($node->nodeName) {
@ -374,6 +400,7 @@ trait BotAPI
break; break;
} }
} }
public function parse_mode($arguments) public function parse_mode($arguments)
{ {
if (isset($arguments['parse_mode']['_'])) { if (isset($arguments['parse_mode']['_'])) {
@ -385,6 +412,7 @@ trait BotAPI
} }
if (preg_match('/html/i', $arguments['parse_mode'])) { if (preg_match('/html/i', $arguments['parse_mode'])) {
$nmessage = ''; $nmessage = '';
try { try {
$arguments['message'] = $this->html_fixtags($arguments['message']); $arguments['message'] = $this->html_fixtags($arguments['message']);
$dom = new \DOMDocument(); $dom = new \DOMDocument();
@ -405,8 +433,10 @@ trait BotAPI
} }
$arguments['message'] = $nmessage; $arguments['message'] = $nmessage;
} }
return $arguments; return $arguments;
} }
public function split_to_chunks($text) public function split_to_chunks($text)
{ {
$max_length = 4096; $max_length = 4096;
@ -423,15 +453,17 @@ trait BotAPI
$i = 0; $i = 0;
$message = ['']; $message = [''];
foreach ($text_arr as $word) { foreach ($text_arr as $word) {
if (strlen($message[$i] . $word) <= $max_length) { if (strlen($message[$i].$word) <= $max_length) {
$message[$i] .= $word; $message[$i] .= $word;
} else { } else {
$i++; $i++;
$message[$i] = $word; $message[$i] = $word;
} }
} }
return $message; return $message;
} }
public function multipleExplodeKeepDelimiters($delimiters, $string) public function multipleExplodeKeepDelimiters($delimiters, $string)
{ {
$initialArray = explode(chr(1), str_replace($delimiters, chr(1), $string)); $initialArray = explode(chr(1), str_replace($delimiters, chr(1), $string));
@ -440,15 +472,17 @@ trait BotAPI
foreach ($initialArray as $item) { foreach ($initialArray as $item) {
$delimOffset += strlen($item); $delimOffset += strlen($item);
if (strlen($item) > 0) { if (strlen($item) > 0) {
$finalArray[] = $item . ($delimOffset < strlen($string) ? $string[$delimOffset] : ''); $finalArray[] = $item.($delimOffset < strlen($string) ? $string[$delimOffset] : '');
} }
$delimOffset++; $delimOffset++;
} }
return $finalArray; return $finalArray;
} }
public function html_fixtags($text) 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) { if ($matches) {
$last = count($matches) - 1; $last = count($matches) - 1;
foreach ($matches as $val) { foreach ($matches as $val) {
@ -460,15 +494,17 @@ trait BotAPI
$text = str_replace($val[6], $this->html_fixtags($val[6]), $text); $text = str_replace($val[6], $this->html_fixtags($val[6]), $text);
} }
} }
preg_match_all("#<a href=\"(.+?)\">#is", $text, $matches); preg_match_all('#<a href="(.+?)">#is', $text, $matches);
foreach ($matches[1] as $match) { foreach ($matches[1] as $match) {
$text = str_replace($match, htmlentities($match), $text); $text = str_replace($match, htmlentities($match), $text);
} }
return $text; return $text;
} else { } else {
return htmlentities($text); return htmlentities($text);
} }
} }
public function build_rows($button_list) public function build_rows($button_list)
{ {
$end = false; $end = false;
@ -493,6 +529,7 @@ trait BotAPI
$row = ['_' => 'keyboardButtonRow', 'buttons' => $buttons]; $row = ['_' => 'keyboardButtonRow', 'buttons' => $buttons];
$rows[] = $row; $rows[] = $row;
} }
return ['_' => 'replyInlineMarkup', 'rows' => $rows]; return ['_' => 'replyInlineMarkup', 'rows' => $rows];
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
trait BotAPIFiles trait BotAPIFiles
@ -18,10 +19,12 @@ trait BotAPIFiles
{ {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
} }
public function base64url_encode($data) public function base64url_encode($data)
{ {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
} }
public function rle_decode($string) public function rle_decode($string)
{ {
$new = ''; $new = '';
@ -36,9 +39,11 @@ trait BotAPIFiles
$last = $cur; $last = $cur;
} }
} }
$string = $new . $last; $string = $new.$last;
return $string; return $string;
} }
public function rle_encode($string) public function rle_encode($string)
{ {
$new = ''; $new = '';
@ -49,23 +54,27 @@ trait BotAPIFiles
$count++; $count++;
} else { } else {
if ($count > 0) { if ($count > 0) {
$new .= $null . chr($count); $new .= $null.chr($count);
$count = 0; $count = 0;
} }
$new .= $cur; $new .= $cur;
} }
} }
return $new; return $new;
} }
public function photosize_to_botapi($photo, $message_media, $thumbnail = false) 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'); $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']['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']['id'] = isset($message_media['id']) ? $message_media['id'] : 0;
$photo['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo'; $photo['location']['_'] = $thumbnail ? 'bot_thumbnail' : 'bot_photo';
$data = $this->serialize_object(['type' => 'File'], $photo['location'], 'File') . chr(2); $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];
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) public function unpack_file_id($file_id)
{ {
$file_id = $this->rle_decode($this->base64url_decode($file_id)); $file_id = $this->rle_decode($this->base64url_decode($file_id));
@ -85,44 +94,52 @@ trait BotAPIFiles
unset($deserialized['_']); unset($deserialized['_']);
$constructor['sizes'][0]['location'] = $deserialized; $constructor['sizes'][0]['location'] = $deserialized;
$res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaPhoto', 'photo' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_voice': case 'bot_voice':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_video': case 'bot_video':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => false]]]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_video_note': case 'bot_video_note':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => true]]]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeVideo', 'round_message' => true]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_document': case 'bot_document':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => []]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => []]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_sticker': case 'bot_sticker':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeSticker']]]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeSticker']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_gif': case 'bot_gif':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAnimated']]]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAnimated']]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
case 'bot_audio': case 'bot_audio':
unset($deserialized['_']); unset($deserialized['_']);
$constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false]]]); $constructor = array_merge($deserialized, ['_' => 'document', 'mime_type' => '', 'attributes' => [['_' => 'documentAttributeAudio', 'voice' => false]]]);
$res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => '']; $res['MessageMedia'] = ['_' => 'messageMediaDocument', 'document' => $constructor, 'caption' => ''];
return $res; return $res;
default: 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

@ -10,8 +10,9 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
class Exception extends \Exception class Exception extends \Exception
{ {
} }

View File

@ -11,6 +11,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
/** /**
@ -22,11 +23,13 @@ trait Extension
{ {
foreach (self::ALL_MIMES as $key => $value) { foreach (self::ALL_MIMES as $key => $value) {
if (array_search($mime, (array) $value) !== false) { if (array_search($mime, (array) $value) !== false) {
return '.' . $key; return '.'.$key;
} }
} }
return ''; return '';
} }
public function get_extension_from_location($location, $default) public function get_extension_from_location($location, $default)
{ {
return $default; return $default;
@ -55,4 +58,4 @@ trait Extension
return $default; return $default;
} }
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Conversion; namespace danog\MadelineProto\TL\Conversion;
trait TD trait TD
@ -21,6 +22,7 @@ trait TD
} }
if (!isset($params['ID'])) { if (!isset($params['ID'])) {
array_walk($params, [$this, 'tdcli_to_td']); array_walk($params, [$this, 'tdcli_to_td']);
return $params; return $params;
} }
foreach ($params as $key => $value) { foreach ($params as $key => $value) {
@ -32,8 +34,10 @@ trait TD
} }
$params['_'] = lcfirst($params['ID']); $params['_'] = lcfirst($params['ID']);
unset($params['ID']); unset($params['ID']);
return $params; return $params;
} }
public function td_to_mtproto($params) public function td_to_mtproto($params)
{ {
$newparams = ['_' => self::REVERSE[$params['_']]]; $newparams = ['_' => self::REVERSE[$params['_']]];
@ -61,12 +65,15 @@ trait TD
} }
} }
} }
return $newparams; return $newparams;
} }
public function mtproto_to_tdcli($params) public function mtproto_to_tdcli($params)
{ {
return $this->td_to_tdcli($this->mtproto_to_td($params)); return $this->td_to_tdcli($this->mtproto_to_td($params));
} }
public function mtproto_to_td(&$params) public function mtproto_to_td(&$params)
{ {
if (!is_array($params)) { if (!is_array($params)) {
@ -74,6 +81,7 @@ trait TD
} }
if (!isset($params['_'])) { if (!isset($params['_'])) {
array_walk($params, [$this, 'mtproto_to_td']); array_walk($params, [$this, 'mtproto_to_td']);
return $params; return $params;
} }
$newparams = ['_' => $params['_']]; $newparams = ['_' => $params['_']];
@ -101,7 +109,7 @@ trait TD
if (isset($params['fwd_from'])) { if (isset($params['fwd_from'])) {
$newparams[$td] = ['_' => 'messageForwardedFromUser']; $newparams[$td] = ['_' => 'messageForwardedFromUser'];
if (isset($params['fwd_from']['channel_id'])) { if (isset($params['fwd_from']['channel_id'])) {
$newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => '-100' . $params['fwd_from']['channel_id']]; $newparams[$td] = ['_' => 'messageForwardedPost', 'chat_id' => '-100'.$params['fwd_from']['channel_id']];
} }
$newparams[$td]['date'] = $params['fwd_from']['date']; $newparams[$td]['date'] = $params['fwd_from']['date'];
if (isset($params['fwd_from']['channel_post'])) { if (isset($params['fwd_from']['channel_post'])) {
@ -145,8 +153,10 @@ trait TD
} }
} }
} }
return $newparams; return $newparams;
} }
public function td_to_tdcli($params) public function td_to_tdcli($params)
{ {
if (!is_array($params)) { if (!is_array($params)) {
@ -158,11 +168,12 @@ trait TD
$newparams['ID'] = ucfirst($value); $newparams['ID'] = ucfirst($value);
} else { } else {
if (!is_numeric($key) && !preg_match('/_^/', $key)) { if (!is_numeric($key) && !preg_match('/_^/', $key)) {
$key = $key . '_'; $key = $key.'_';
} }
$newparams[$key] = $this->td_to_tdcli($value); $newparams[$key] = $this->td_to_tdcli($value);
} }
} }
return $newparams; return $newparams;
} }
} }

View File

@ -10,18 +10,21 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
class Exception extends \Exception class Exception extends \Exception
{ {
use PrettyException; use PrettyException;
public function __toString() 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 = '') public function __construct($message, $file = '')
{ {
parent::__construct($message); parent::__construct($message);
$this->prettify_tl($file); $this->prettify_tl($file);
} }
} }

View File

@ -10,22 +10,25 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
trait PrettyException trait PrettyException
{ {
public $tl_trace; public $tl_trace;
public function getTLTrace() public function getTLTrace()
{ {
return $this->tl_trace; return $this->tl_trace;
} }
public function prettify_tl($init = '') public function prettify_tl($init = '')
{ {
$tl = false; $tl = false;
foreach (array_reverse($this->getTrace()) as $k => $frame) { foreach (array_reverse($this->getTrace()) as $k => $frame) {
if (isset($frame['function']) && in_array($frame['function'], ['serialize_params', 'serialize_object'])) { if (isset($frame['function']) && in_array($frame['function'], ['serialize_params', 'serialize_object'])) {
if ($frame['args'][2] !== '') { 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; $tl = true;
} }
} else { } else {
@ -35,15 +38,15 @@ trait PrettyException
if (isset($frame['function']) && ($frame['function'] === 'handle_rpc_error' && $k === count($this->getTrace()) - 1) || $frame['function'] === 'unserialize') { if (isset($frame['function']) && ($frame['function'] === 'handle_rpc_error' && $k === count($this->getTrace()) - 1) || $frame['function'] === 'unserialize') {
continue; continue;
} }
$this->tl_trace .= isset($frame['file']) ? str_pad(basename($frame['file']) . '(' . $frame['line'] . '):', 20) . "\t" : ''; $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['function']) ? $frame['function'].'(' : '';
$this->tl_trace .= isset($frame['args']) ? substr(json_encode($frame['args']), 1, -1) : ''; $this->tl_trace .= isset($frame['args']) ? substr(json_encode($frame['args']), 1, -1) : '';
$this->tl_trace .= ')'; $this->tl_trace .= ')';
$this->tl_trace .= PHP_EOL; $this->tl_trace .= PHP_EOL;
$tl = false; $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))); $this->tl_trace = implode(PHP_EOL, array_reverse(explode(PHP_EOL, $this->tl_trace)));
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
trait TL trait TL
@ -20,6 +21,7 @@ trait TL
public $td_constructors; public $td_constructors;
public $td_methods; public $td_methods;
public $td_descriptions; public $td_descriptions;
public function construct_tl($files) public function construct_tl($files)
{ {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['TL_loading']], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['TL_loading']], \danog\MadelineProto\Logger::VERBOSE);
@ -139,7 +141,7 @@ trait TL
} }
} }
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) { 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; $orig = $this->encrypted_layer;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_obj']], \danog\MadelineProto\Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_obj']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
@ -147,11 +149,11 @@ trait TL
if ($scheme_type === 'secret') { if ($scheme_type === 'secret') {
$this->encrypted_layer = max($this->encrypted_layer, $elem['layer']); $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); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['translating_methods']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
foreach ($TL_dict['methods'] as $elem) { 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') { if ($scheme_type === 'secret') {
$this->encrypted_layer = max($this->encrypted_layer, $elem['layer']); $this->encrypted_layer = max($this->encrypted_layer, $elem['layer']);
} }
@ -183,6 +185,7 @@ trait TL
} }
} }
} }
public function get_method_namespaces() public function get_method_namespaces()
{ {
$res = []; $res = [];
@ -190,20 +193,25 @@ trait TL
$a = key($pair); $a = key($pair);
$res[$a] = $a; $res[$a] = $a;
} }
return $res; return $res;
} }
public function get_methods_namespaced() public function get_methods_namespaced()
{ {
return $this->methods->method_namespace; return $this->methods->method_namespace;
} }
public function deserialize_bool($id) public function deserialize_bool($id)
{ {
$tl_elem = $this->constructors->find_by_id($id); $tl_elem = $this->constructors->find_by_id($id);
if ($tl_elem === false) { if ($tl_elem === false) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['bool_error']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['bool_error']);
} }
return $tl_elem['predicate'] === 'boolTrue'; return $tl_elem['predicate'] === 'boolTrue';
} }
public function serialize_object($type, $object, $ctx, $layer = -1) public function serialize_object($type, $object, $ctx, $layer = -1)
{ {
switch ($type['type']) { switch ($type['type']) {
@ -211,11 +219,13 @@ trait TL
if (!is_numeric($object)) { if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
} }
return $this->pack_signed_int($object); return $this->pack_signed_int($object);
case '#': case '#':
if (!is_numeric($object)) { if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
} }
return $this->pack_unsigned_int($object); return $this->pack_unsigned_int($object);
case 'long': case 'long':
if (is_object($object)) { if (is_object($object)) {
@ -230,21 +240,25 @@ trait TL
if (!is_numeric($object)) { if (!is_numeric($object)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['not_numeric']);
} }
return $this->pack_signed_long($object); return $this->pack_signed_long($object);
case 'int128': case 'int128':
if (strlen($object) !== 16) { if (strlen($object) !== 16) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_16']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_16']);
} }
return (string) $object; return (string) $object;
case 'int256': case 'int256':
if (strlen($object) !== 32) { if (strlen($object) !== 32) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_32']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_32']);
} }
return (string) $object; return (string) $object;
case 'int512': case 'int512':
if (strlen($object) !== 64) { if (strlen($object) !== 64) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_64']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['long_not_64']);
} }
return (string) $object; return (string) $object;
case 'double': case 'double':
return $this->pack_double($object); return $this->pack_double($object);
@ -258,13 +272,14 @@ trait TL
if ($l <= 253) { if ($l <= 253) {
$concat .= chr($l); $concat .= chr($l);
$concat .= $object; $concat .= $object;
$concat .= pack('@' . $this->posmod(-$l - 1, 4)); $concat .= pack('@'.$this->posmod(-$l - 1, 4));
} else { } else {
$concat .= chr(254); $concat .= chr(254);
$concat .= substr($this->pack_signed_int($l), 0, 3); $concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object; $concat .= $object;
$concat .= pack('@' . $this->posmod(-$l, 4)); $concat .= pack('@'.$this->posmod(-$l, 4));
} }
return $concat; return $concat;
case 'bytes': case 'bytes':
if (!is_string($object) && !$object instanceof \danog\MadelineProto\TL\Types\Bytes) { if (!is_string($object) && !$object instanceof \danog\MadelineProto\TL\Types\Bytes) {
@ -275,13 +290,14 @@ trait TL
if ($l <= 253) { if ($l <= 253) {
$concat .= chr($l); $concat .= chr($l);
$concat .= $object; $concat .= $object;
$concat .= pack('@' . $this->posmod(-$l - 1, 4)); $concat .= pack('@'.$this->posmod(-$l - 1, 4));
} else { } else {
$concat .= chr(254); $concat .= chr(254);
$concat .= substr($this->pack_signed_int($l), 0, 3); $concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object; $concat .= $object;
$concat .= pack('@' . $this->posmod(-$l, 4)); $concat .= pack('@'.$this->posmod(-$l, 4));
} }
return $concat; return $concat;
case 'Bool': case 'Bool':
return $this->constructors->find_by_predicate((bool) $object ? 'boolTrue' : 'boolFalse')['id']; return $this->constructors->find_by_predicate((bool) $object ? 'boolTrue' : 'boolFalse')['id'];
@ -298,6 +314,7 @@ trait TL
foreach ($object as $k => $current_object) { foreach ($object as $k => $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k); $concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k);
} }
return $concat; return $concat;
case 'vector': case 'vector':
if (!is_array($object)) { if (!is_array($object)) {
@ -307,6 +324,7 @@ trait TL
foreach ($object as $k => $current_object) { foreach ($object as $k => $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k); $concat .= $this->serialize_object(['type' => $type['subtype']], $current_object, $k);
} }
return $concat; return $concat;
case 'Object': case 'Object':
if (is_string($object)) { if (is_string($object)) {
@ -333,6 +351,7 @@ trait TL
$constructorData = $this->constructors->find_by_predicate($predicate, $layer); $constructorData = $this->constructors->find_by_predicate($predicate, $layer);
if ($constructorData === false) { if ($constructorData === false) {
\danog\MadelineProto\Logger::log([$object], \danog\MadelineProto\Logger::FATAL_ERROR); \danog\MadelineProto\Logger::log([$object], \danog\MadelineProto\Logger::FATAL_ERROR);
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error'], $predicate)); 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] === '%') {
@ -348,16 +367,20 @@ trait TL
if (!$bare) { if (!$bare) {
$concat = $constructorData['id']; $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) public function serialize_method($method, $arguments)
{ {
$tl = $this->methods->find_by_method($method); $tl = $this->methods->find_by_method($method);
if ($tl === false) { 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) public function serialize_params($tl, $arguments, $ctx, $layer = -1)
{ {
$serialized = ''; $serialized = '';
@ -415,10 +438,11 @@ 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']; $serialized .= $id['id'];
continue; continue;
} }
throw new Exception(\danog\MadelineProto\Lang::$current_lang['params_missing'], $current_argument['name']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['params_missing'], $current_argument['name']);
} }
if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputEncryptedChat') { if (!is_array($arguments[$current_argument['name']]) && $current_argument['type'] === 'InputEncryptedChat') {
@ -433,8 +457,10 @@ trait TL
//\danog\MadelineProto\Logger::log(['Serializing '.$current_argument['name'].' of type '.$current_argument['type']); //\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); $serialized .= $this->serialize_object($current_argument, $arguments[$current_argument['name']], $current_argument['name'], $layer);
} }
return $serialized; return $serialized;
} }
public function get_length($stream, $type = ['type' => '']) public function get_length($stream, $type = ['type' => ''])
{ {
if (is_string($stream)) { if (is_string($stream)) {
@ -446,8 +472,10 @@ trait TL
throw new Exception(\danog\MadelineProto\Lang::$current_lang['stream_handle_invalid']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['stream_handle_invalid']);
} }
$this->deserialize($stream, $type); $this->deserialize($stream, $type);
return ftell($stream); return ftell($stream);
} }
/** /**
* :type stream: io.BytesIO object. * :type stream: io.BytesIO object.
*/ */
@ -472,6 +500,7 @@ trait TL
if (isset($type['idstrlong'])) { if (isset($type['idstrlong'])) {
return stream_get_contents($stream, 8); 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)); return \danog\MadelineProto\Logger::$bigint || isset($type['strlong']) ? stream_get_contents($stream, 8) : $this->unpack_signed_long(stream_get_contents($stream, 8));
case 'double': case 'double':
return $this->unpack_double(stream_get_contents($stream, 8)); return $this->unpack_double(stream_get_contents($stream, 8));
@ -488,7 +517,7 @@ trait TL
throw new Exception(\danog\MadelineProto\Lang::$current_lang['length_too_big']); throw new Exception(\danog\MadelineProto\Lang::$current_lang['length_too_big']);
} }
if ($l === 254) { 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); $x = stream_get_contents($stream, $long_len);
$resto = $this->posmod(-$long_len, 4); $resto = $this->posmod(-$long_len, 4);
if ($resto > 0) { if ($resto > 0) {
@ -504,13 +533,14 @@ trait TL
if (!is_string($x)) { if (!is_string($x)) {
throw new Exception(\danog\MadelineProto\Lang::$current_lang['deserialize_not_str']); 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': case 'Vector t':
$id = stream_get_contents($stream, 4); $id = stream_get_contents($stream, 4);
$constructorData = $this->constructors->find_by_id($id); $constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) { if ($constructorData === false) {
$constructorData = $this->methods->find_by_id($id); $constructorData = $this->methods->find_by_id($id);
$constructorData['predicate'] = 'method_' . $constructorData['method']; $constructorData['predicate'] = 'method_'.$constructorData['method'];
} }
if ($constructorData === false) { if ($constructorData === false) {
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id)))); throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id))));
@ -522,7 +552,7 @@ trait TL
case 'vector': case 'vector':
break; break;
default: 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': case 'vector':
$count = unpack('V', stream_get_contents($stream, 4))[1]; $count = unpack('V', stream_get_contents($stream, 4))[1];
@ -531,13 +561,14 @@ trait TL
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$result[] = $this->deserialize($stream, $type); $result[] = $this->deserialize($stream, $type);
} }
return $result; return $result;
} }
if ($type['type'] != '' && $type['type'][0] === '%') { if ($type['type'] != '' && $type['type'][0] === '%') {
$checkType = substr($type['type'], 1); $checkType = substr($type['type'], 1);
$constructorData = $this->constructors->find_by_type($checkType); $constructorData = $this->constructors->find_by_type($checkType);
if ($constructorData === false) { 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 { } else {
$constructorData = $this->constructors->find_by_predicate($type['type']); $constructorData = $this->constructors->find_by_predicate($type['type']);
@ -549,7 +580,7 @@ trait TL
if ($constructorData === false) { if ($constructorData === false) {
throw new Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['type_extract_error_id'], $type['type'], bin2hex(strrev($id)))); 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'];
} }
} }
} }
@ -557,12 +588,14 @@ trait TL
if (!isset($type['subtype'])) { if (!isset($type['subtype'])) {
$type['subtype'] = ''; $type['subtype'] = '';
} }
return $this->deserialize(gzdecode($this->deserialize($stream, ['type' => 'bytes'])), ['type' => '', 'datacenter' => $type['datacenter'], 'subtype' => $type['subtype']]); return $this->deserialize(gzdecode($this->deserialize($stream, ['type' => 'bytes'])), ['type' => '', 'datacenter' => $type['datacenter'], 'subtype' => $type['subtype']]);
} }
if ($constructorData['type'] === 'Vector t') { if ($constructorData['type'] === 'Vector t') {
$constructorData['datacenter'] = $type['datacenter']; $constructorData['datacenter'] = $type['datacenter'];
$constructorData['subtype'] = isset($type['subtype']) ? $type['subtype'] : ''; $constructorData['subtype'] = isset($type['subtype']) ? $type['subtype'] : '';
$constructorData['type'] = 'vector'; $constructorData['type'] = 'vector';
return $this->deserialize($stream, $constructorData); return $this->deserialize($stream, $constructorData);
} }
if ($constructorData['predicate'] === 'boolTrue') { if ($constructorData['predicate'] === 'boolTrue') {
@ -628,6 +661,7 @@ trait TL
} }
} }
} }
return $x; return $x;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
class TLConstructor class TLConstructor
@ -20,6 +21,7 @@ class TLConstructor
public $by_id = []; public $by_id = [];
public $by_predicate_and_layer = []; public $by_predicate_and_layer = [];
public $layers = []; public $layers = [];
//public $type = []; //public $type = [];
//public $params = []; //public $params = [];
//public $layer = []; //public $layer = [];
@ -28,10 +30,11 @@ class TLConstructor
{ {
return ['by_predicate_and_layer', 'by_id', 'layers']; return ['by_predicate_and_layer', 'by_id', 'layers'];
} }
public function add($json_dict, $scheme_type) public function add($json_dict, $scheme_type)
{ {
$predicate = (string) (($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message' ? 'MT' : '') . $json_dict['predicate']); $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']]; $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') { if ($scheme_type === 'secret') {
$this->by_id[$json_dict['id']]['layer'] = $json_dict['layer']; $this->by_id[$json_dict['id']]['layer'] = $json_dict['layer'];
$this->layers[$json_dict['layer']] = $json_dict['layer']; $this->layers[$json_dict['layer']] = $json_dict['layer'];
@ -39,30 +42,34 @@ class TLConstructor
} else { } else {
$json_dict['layer'] = ''; $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'); $this->parse_params($json_dict['id'], $scheme_type === 'mtproto');
} }
public function find_by_type($type) public function find_by_type($type)
{ {
foreach ($this->by_id as $id => $constructor) { foreach ($this->by_id as $id => $constructor) {
if ($constructor['type'] === $type) { if ($constructor['type'] === $type) {
$constructor['id'] = $id; $constructor['id'] = $id;
$constructor['params'] = $constructor['params']; $constructor['params'] = $constructor['params'];
return $constructor; return $constructor;
} }
} }
return false; return false;
} }
public function find_by_predicate($predicate, $layer = -1) public function find_by_predicate($predicate, $layer = -1)
{ {
if ($layer !== -1) { if ($layer !== -1) {
foreach ($this->layers as $alayer) { foreach ($this->layers as $alayer) {
if ($alayer <= $layer) { if ($alayer <= $layer) {
if (isset($this->by_predicate_and_layer[$predicate . $alayer])) { if (isset($this->by_predicate_and_layer[$predicate.$alayer])) {
$chosenid = $this->by_predicate_and_layer[$predicate . $alayer]; $chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
} }
} elseif (!isset($chosenid)) { } elseif (!isset($chosenid)) {
$chosenid = $this->by_predicate_and_layer[$predicate . $alayer]; $chosenid = $this->by_predicate_and_layer[$predicate.$alayer];
} }
} }
if (!isset($chosenid)) { if (!isset($chosenid)) {
@ -71,24 +78,30 @@ class TLConstructor
$constructor = $this->by_id[$chosenid]; $constructor = $this->by_id[$chosenid];
$constructor['id'] = $chosenid; $constructor['id'] = $chosenid;
$constructor['params'] = $constructor['params']; $constructor['params'] = $constructor['params'];
return $constructor; return $constructor;
} }
if (isset($this->by_predicate_and_layer[$predicate])) { if (isset($this->by_predicate_and_layer[$predicate])) {
$constructor = $this->by_id[$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['id'] = $this->by_predicate_and_layer[$predicate];
$constructor['params'] = $constructor['params']; $constructor['params'] = $constructor['params'];
return $constructor; return $constructor;
} }
return false; return false;
} }
public function find_by_id($id) public function find_by_id($id)
{ {
if (isset($this->by_id[$id])) { if (isset($this->by_id[$id])) {
$constructor = $this->by_id[$id]; $constructor = $this->by_id[$id];
$constructor['id'] = $id; $constructor['id'] = $id;
$constructor['params'] = $constructor['params']; $constructor['params'] = $constructor['params'];
return $constructor; return $constructor;
} }
return false; return false;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
class TLMethod class TLMethod
@ -20,10 +21,12 @@ class TLMethod
public $by_id = []; public $by_id = [];
public $by_method = []; public $by_method = [];
public $method_namespace = []; public $method_namespace = [];
public function __sleep() public function __sleep()
{ {
return ['by_id', 'by_method', 'method_namespace']; return ['by_id', 'by_method', 'method_namespace'];
} }
public function add($json_dict) 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_id[$json_dict['id']] = ['method' => $json_dict['method'], 'type' => $json_dict['type'], 'params' => $json_dict['params']];
@ -34,24 +37,30 @@ class TLMethod
} }
$this->parse_params($json_dict['id']); $this->parse_params($json_dict['id']);
} }
public function find_by_id($id) public function find_by_id($id)
{ {
if (isset($this->by_id[$id])) { if (isset($this->by_id[$id])) {
$method = $this->by_id[$id]; $method = $this->by_id[$id];
$method['id'] = $id; $method['id'] = $id;
$method['params'] = $method['params']; $method['params'] = $method['params'];
return $method; return $method;
} }
return false; return false;
} }
public function find_by_method($method_name) public function find_by_method($method_name)
{ {
if (isset($this->by_method[$method_name])) { if (isset($this->by_method[$method_name])) {
$method = $this->by_id[$this->by_method[$method_name]]; $method = $this->by_id[$this->by_method[$method_name]];
$method['id'] = $this->by_method[$method_name]; $method['id'] = $this->by_method[$method_name];
$method['params'] = $method['params']; $method['params'] = $method['params'];
return $method; return $method;
} }
return false; return false;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
trait TLParams trait TLParams
@ -31,12 +32,12 @@ trait TLParams
$param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']); $param['subtype'] = preg_replace(['/.*</', '/>$/'], '', $param['type']);
$param['type'] = 'Vector t'; $param['type'] = 'Vector t';
} }
$param['subtype'] = ($mtproto && $param['subtype'] === 'Message' ? 'MT' : '') . $param['subtype']; $param['subtype'] = ($mtproto && $param['subtype'] === 'Message' ? 'MT' : '').$param['subtype'];
$param['subtype'] = $mtproto && $param['subtype'] === '%Message' ? '%MTMessage' : $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' ? 'MT' : '').$param['type'];
$param['type'] = $mtproto && $param['type'] === '%Message' ? '%MTMessage' : $param['type']; $param['type'] = $mtproto && $param['type'] === '%Message' ? '%MTMessage' : $param['type'];
$this->by_id[$key]['params'][$kkey] = $param; $this->by_id[$key]['params'][$kkey] = $param;
} }
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Types; namespace danog\MadelineProto\TL\Types;
class Button extends \Volatile implements \JsonSerializable class Button extends \Volatile implements \JsonSerializable
@ -17,6 +18,7 @@ class Button extends \Volatile implements \JsonSerializable
use \danog\Serializable; use \danog\Serializable;
private $info = []; private $info = [];
private $data = []; private $data = [];
public function __magic_construct($API, $message, $button) public function __magic_construct($API, $message, $button)
{ {
$this->data = $button; $this->data = $button;
@ -24,10 +26,12 @@ class Button extends \Volatile implements \JsonSerializable
$this->info['id'] = $message['id']; $this->info['id'] = $message['id'];
$this->info['API'] = $API; $this->info['API'] = $API;
} }
public function __sleep() public function __sleep()
{ {
return ['data', 'info']; return ['data', 'info'];
} }
public function click($donotwait = false) public function click($donotwait = false)
{ {
switch ($this->data['_']) { switch ($this->data['_']) {
@ -43,12 +47,14 @@ class Button extends \Volatile implements \JsonSerializable
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]); 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() 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() public function jsonSerialize()
{ {
return (array) $this->data; return (array) $this->data;
} }
} }

View File

@ -10,26 +10,31 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\TL\Types; namespace danog\MadelineProto\TL\Types;
class Bytes extends \Volatile implements \JsonSerializable class Bytes extends \Volatile implements \JsonSerializable
{ {
use \danog\Serializable; use \danog\Serializable;
private $bytes = []; private $bytes = [];
public function __magic_construct($bytes) public function __magic_construct($bytes)
{ {
$this->bytes = $bytes; $this->bytes = $bytes;
} }
public function __sleep() public function __sleep()
{ {
return ['bytes']; return ['bytes'];
} }
public function __toString() public function __toString()
{ {
return $this->bytes; return $this->bytes;
} }
public function jsonSerialize() public function jsonSerialize()
{ {
return utf8_encode($this->bytes); return utf8_encode($this->bytes);
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\Threads; namespace danog\MadelineProto\Threads;
/** /**
@ -23,33 +24,39 @@ class SocketHandler extends \Threaded implements \Collectable
$this->current = $current; $this->current = $current;
$this->error = $error; $this->error = $error;
} }
/** /**
* Reading connection and receiving message from server. Check the CRC32. * Reading connection and receiving message from server. Check the CRC32.
*/ */
public function run() public function run()
{ {
require __DIR__ . '/../../../../vendor/autoload.php'; require __DIR__.'/../../../../vendor/autoload.php';
if ($this->error !== true) { if ($this->error !== true) {
if ($this->error === -404) { if ($this->error === -404) {
if ($this->API->datacenter->sockets[$this->current]->temp_auth_key !== null) { 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); \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->datacenter->sockets[$this->current]->temp_auth_key = null;
$this->API->init_authorization(); $this->API->init_authorization();
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['recreate_temp_auth_key']); throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['recreate_temp_auth_key']);
} }
} }
throw new \danog\MadelineProto\RPCErrorException($this->error, $this->error); throw new \danog\MadelineProto\RPCErrorException($this->error, $this->error);
} }
$this->API->handle_messages($this->current); $this->API->handle_messages($this->current);
$this->setGarbage(); $this->setGarbage();
} }
protected $garbage = false; protected $garbage = false;
public function setGarbage() public function setGarbage()
{ {
$this->garbage = true; $this->garbage = true;
} }
public function isGarbage() public function isGarbage()
{ {
return $this->garbage; return $this->garbage;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\Threads; namespace danog\MadelineProto\Threads;
/** /**
@ -18,25 +19,29 @@ namespace danog\MadelineProto\Threads;
class SocketReader extends \Threaded implements \Collectable class SocketReader extends \Threaded implements \Collectable
{ {
public $ready = false; public $ready = false;
public function __construct($me, $current) public function __construct($me, $current)
{ {
$this->API = $me; $this->API = $me;
$this->current = $current; $this->current = $current;
} }
public function __sleep() public function __sleep()
{ {
return ['current', 'API', 'garbage']; return ['current', 'API', 'garbage'];
} }
public function __destruct() 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. * Reading connection and receiving message from server. Check the CRC32.
*/ */
public function run() public function run()
{ {
require __DIR__ . '/../../../../vendor/autoload.php'; require __DIR__.'/../../../../vendor/autoload.php';
$handler_pool = new \Pool($this->API->settings['threading']['handler_workers']); $handler_pool = new \Pool($this->API->settings['threading']['handler_workers']);
$this->ready = true; $this->ready = true;
while ($this->API->run_workers) { while ($this->API->run_workers) {
@ -57,13 +62,16 @@ class SocketReader extends \Threaded implements \Collectable
} }
$this->setGarbage(); $this->setGarbage();
} }
public $garbage = false; public $garbage = false;
public function setGarbage() public function setGarbage()
{ {
$this->garbage = true; $this->garbage = true;
} }
public function isGarbage() public function isGarbage()
{ {
return $this->garbage; return $this->garbage;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with the MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto; namespace danog\MadelineProto;
/** /**
@ -21,6 +22,7 @@ trait Tools
{ {
return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length); return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length);
} }
/** /**
* posmod(numeric,numeric) : numeric * posmod(numeric,numeric) : numeric
* Works just like the % (modulus) operator, only returns always a postive number. * Works just like the % (modulus) operator, only returns always a postive number.
@ -28,8 +30,10 @@ trait Tools
public function posmod($a, $b) public function posmod($a, $b)
{ {
$resto = $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) public function array_cast_recursive($array, $force = false)
{ {
if (!\danog\MadelineProto\Logger::$has_thread && !$force) { if (!\danog\MadelineProto\Logger::$has_thread && !$force) {
@ -43,22 +47,28 @@ trait Tools
$array[$key] = $this->array_cast_recursive($value, $force); $array[$key] = $this->array_cast_recursive($value, $force);
} }
} }
return $array; return $array;
} }
public function unpack_signed_int($value) public function unpack_signed_int($value)
{ {
if (strlen($value) !== 4) { if (strlen($value) !== 4) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_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]; return unpack('l', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
} }
public function unpack_signed_long($value) public function unpack_signed_long($value)
{ {
if (strlen($value) !== 8) { if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_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]; return unpack('q', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
} }
public function pack_signed_int($value) public function pack_signed_int($value)
{ {
if ($value > 2147483647) { if ($value > 2147483647) {
@ -68,8 +78,10 @@ trait Tools
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_2147483648'], $value)); throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_2147483648'], $value));
} }
$res = pack('l', $value); $res = pack('l', $value);
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res; return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
} }
public function pack_signed_long($value) public function pack_signed_long($value)
{ {
if ($value > 9223372036854775807) { if ($value > 9223372036854775807) {
@ -78,9 +90,11 @@ trait Tools
if ($value < -9.223372036854776E+18) { if ($value < -9.223372036854776E+18) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_9223372036854775808'], $value)); 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; return $res;
} }
public function pack_unsigned_int($value) public function pack_unsigned_int($value)
{ {
if ($value > 4294967295) { if ($value > 4294967295) {
@ -89,21 +103,26 @@ trait Tools
if ($value < 0) { if ($value < 0) {
throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_0'], $value)); throw new TL\Exception(sprintf(\danog\MadelineProto\Lang::$current_lang['value_smaller_than_0'], $value));
} }
return pack('V', $value); return pack('V', $value);
} }
public function pack_double($value) public function pack_double($value)
{ {
$res = pack('d', $value); $res = pack('d', $value);
if (strlen($res) !== 8) { if (strlen($res) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['encode_double_error']); throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['encode_double_error']);
} }
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res; return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
} }
public function unpack_double($value) public function unpack_double($value)
{ {
if (strlen($value) !== 8) { if (strlen($value) !== 8) {
throw new TL\Exception(\danog\MadelineProto\Lang::$current_lang['length_not_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]; return unpack('d', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\VoIP; namespace danog\MadelineProto\VoIP;
/** /**
@ -21,6 +22,7 @@ namespace danog\MadelineProto\VoIP;
trait AuthKeyHandler trait AuthKeyHandler
{ {
private $calls = []; private $calls = [];
public function request_call($user) public function request_call($user)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -48,8 +50,10 @@ trait AuthKeyHandler
$controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)]; $controller->storage = ['a' => $a, 'g_a' => str_pad($g_a->toBytes(), 256, chr(0), \STR_PAD_LEFT)];
$this->handle_pending_updates(); $this->handle_pending_updates();
$this->get_updates_difference(); $this->get_updates_difference();
return $controller; return $controller;
} }
public function accept_call($call) public function accept_call($call)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -62,6 +66,7 @@ trait AuthKeyHandler
}); });
if ($this->call_status($call['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED) { 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'])]); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_1'], $call['id'])]);
return false; return false;
} }
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['accepting_call'], $this->calls[$call['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['accepting_call'], $this->calls[$call['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
@ -70,25 +75,31 @@ trait AuthKeyHandler
$b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two)); $b = \phpseclib\Math\BigInteger::randomRange($this->two, $dh_config['p']->subtract($this->two));
$g_b = $dh_config['g']->powMod($b, $dh_config['p']); $g_b = $dh_config['g']->powMod($b, $dh_config['p']);
$this->check_G($g_b, $dh_config['p']); $this->check_G($g_b, $dh_config['p']);
try { 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]); $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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'CALL_ALREADY_ACCEPTED') { if ($e->rpc === 'CALL_ALREADY_ACCEPTED') {
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id'])]); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_already_accepted'], $call['id'])]);
return true; return true;
} }
if ($e->rpc === 'CALL_ALREADY_DECLINED') { if ($e->rpc === 'CALL_ALREADY_DECLINED') {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['call_already_declined']]); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['call_already_declined']]);
$this->discard_call($call['id'], 'phoneCallDiscardReasonHangup'); $this->discard_call($call['id'], 'phoneCallDiscardReasonHangup');
return false; return false;
} }
throw $e; throw $e;
} }
$this->calls[$res['phone_call']['id']]->storage['b'] = $b; $this->calls[$res['phone_call']['id']]->storage['b'] = $b;
$this->handle_pending_updates(); $this->handle_pending_updates();
$this->get_updates_difference(); $this->get_updates_difference();
return true; return true;
} }
public function confirm_call($params) public function confirm_call($params)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -101,6 +112,7 @@ trait AuthKeyHandler
}); });
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_REQUESTED) { 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'])]); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_2'], $params['id'])]);
return false; return false;
} }
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_confirming'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_confirming'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
@ -111,7 +123,7 @@ trait AuthKeyHandler
$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']; $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 = []; $visualization = [];
$length = new \phpseclib\Math\BigInteger(count($this->emojis)); $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) { 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); $number[0] = chr(ord($number[0]) & 0x7f);
$visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()]; $visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
} }
@ -122,8 +134,10 @@ trait AuthKeyHandler
$this->calls[$params['id']]->parseConfig(); $this->calls[$params['id']]->parseConfig();
$res = $this->calls[$params['id']]->startTheMagic(); $res = $this->calls[$params['id']]->startTheMagic();
$this->handle_pending_updates(); $this->handle_pending_updates();
return $res; return $res;
} }
public function complete_call($params) public function complete_call($params)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -136,6 +150,7 @@ trait AuthKeyHandler
}); });
if ($this->call_status($params['id']) !== \danog\MadelineProto\VoIP::CALL_STATE_ACCEPTED || !isset($this->calls[$params['id']]->storage['b'])) { 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'])]); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_error_3'], $params['id'])]);
return false; return false;
} }
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_completing'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_completing'], $this->calls[$params['id']]->getOtherID())], \danog\MadelineProto\Logger::VERBOSE);
@ -151,7 +166,7 @@ trait AuthKeyHandler
} }
$visualization = []; $visualization = [];
$length = new \phpseclib\Math\BigInteger(count($this->emojis)); $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) { 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); $number[0] = chr(ord($number[0]) & 0x7f);
$visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()]; $visualization[] = $this->emojis[(int) (new \phpseclib\Math\BigInteger($number, 256))->divide($length)[1]->toString()];
} }
@ -160,8 +175,10 @@ trait AuthKeyHandler
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$params['connection']], $params['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']); $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(); $this->calls[$params['id']]->parseConfig();
return $this->calls[$params['id']]->startTheMagic(); return $this->calls[$params['id']]->startTheMagic();
} }
public function call_status($id) public function call_status($id)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -175,8 +192,10 @@ trait AuthKeyHandler
if (isset($this->calls[$id])) { if (isset($this->calls[$id])) {
return $this->calls[$id]->getCallState(); return $this->calls[$id]->getCallState();
} }
return \danog\MadelineProto\VoIP::CALL_STATE_NONE; return \danog\MadelineProto\VoIP::CALL_STATE_NONE;
} }
public function get_call($call) public function get_call($call)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -187,8 +206,10 @@ trait AuthKeyHandler
$controller->discard(); $controller->discard();
} }
}); });
return $this->calls[$call]; return $this->calls[$call];
} }
public function discard_call($call, $reason, $rating = [], $need_debug = true) public function discard_call($call, $reason, $rating = [], $need_debug = true)
{ {
if (!class_exists('\\danog\\MadelineProto\\VoIP')) { if (!class_exists('\\danog\\MadelineProto\\VoIP')) {
@ -198,6 +219,7 @@ trait AuthKeyHandler
return; return;
} }
\danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_discarding'], $call['id'])], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log([sprintf(\danog\MadelineProto\Lang::$current_lang['call_discarding'], $call['id'])], \danog\MadelineProto\Logger::VERBOSE);
try { 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]); $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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -227,4 +249,4 @@ trait AuthKeyHandler
} }
}); });
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\Wrappers; namespace danog\MadelineProto\Wrappers;
trait DialogHandler trait DialogHandler
@ -24,6 +25,7 @@ trait DialogHandler
$datacenter = $this->datacenter->curdc; $datacenter = $this->datacenter->curdc;
$peers = []; $peers = [];
$this->postpone_updates = true; $this->postpone_updates = true;
try { try {
while ($this->dialog_params['count'] < $res['count']) { while ($this->dialog_params['count'] < $res['count']) {
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['getting_dialogs']]); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['getting_dialogs']]);
@ -45,6 +47,7 @@ trait DialogHandler
$this->postpone_updates = false; $this->postpone_updates = false;
$this->updates_state['sync_loading'] = false; $this->updates_state['sync_loading'] = false;
} }
return $peers; return $peers;
} }
} }

View File

@ -10,6 +10,7 @@ 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. You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto\Wrappers; namespace danog\MadelineProto\Wrappers;
/** /**
@ -34,8 +35,10 @@ trait Login
throw new \danog\MadelineProto\Exception(\danog\MadelineProto\Lang::$current_lang['logout_error']); 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); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['logout_ok']], \danog\MadelineProto\Logger::NOTICE);
return true; return true;
} }
public function bot_login($token) public function bot_login($token)
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
@ -50,12 +53,14 @@ trait Login
$this->updates = []; $this->updates = [];
$this->updates_key = 0; $this->updates_key = 0;
if (!isset($this->settings['pwr']['pwr']) || !$this->settings['pwr']['pwr']) { 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(); $this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function phone_login($number, $sms_type = 5) public function phone_login($number, $sms_type = 5)
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
@ -71,8 +76,10 @@ trait Login
$this->updates = []; $this->updates = [];
$this->updates_key = 0; $this->updates_key = 0;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_code_sent']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_code_sent']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function complete_phone_login($code) public function complete_phone_login($code)
{ {
if ($this->authorized !== self::WAITING_CODE) { if ($this->authorized !== self::WAITING_CODE) {
@ -80,6 +87,7 @@ trait Login
} }
$this->authorized = self::NOT_LOGGED_IN; $this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE);
try { 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) { } catch (\danog\MadelineProto\RPCErrorException $e) {
@ -94,8 +102,10 @@ trait Login
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_need_signup']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_need_signup']], \danog\MadelineProto\Logger::NOTICE);
$this->authorized = self::WAITING_SIGNUP; $this->authorized = self::WAITING_SIGNUP;
$this->authorization['phone_code'] = $code; $this->authorization['phone_code'] = $code;
return ['_' => 'account.needSignup']; return ['_' => 'account.needSignup'];
} }
throw $e; throw $e;
} }
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
@ -103,8 +113,10 @@ trait Login
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization(); $this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function import_authorization($authorization) public function import_authorization($authorization)
{ {
if ($this->authorized === self::LOGGED_IN) { if ($this->authorized === self::LOGGED_IN) {
@ -129,8 +141,10 @@ trait Login
$this->datacenter->sockets[$dc_id]->authorized = true; $this->datacenter->sockets[$dc_id]->authorized = true;
$this->authorized = self::LOGGED_IN; $this->authorized = self::LOGGED_IN;
$this->init_authorization(); $this->init_authorization();
return $this->get_self(); return $this->get_self();
} }
public function export_authorization() public function export_authorization()
{ {
if ($this->authorized !== self::LOGGED_IN) { if ($this->authorized !== self::LOGGED_IN) {
@ -138,8 +152,10 @@ trait Login
} }
$this->get_self(); $this->get_self();
$this->authorized_dc = $this->datacenter->curdc; $this->authorized_dc = $this->datacenter->curdc;
return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']]; return [$this->datacenter->curdc, $this->datacenter->sockets[$this->datacenter->curdc]->auth_key['auth_key']];
} }
public function complete_signup($first_name, $last_name) public function complete_signup($first_name, $last_name)
{ {
if ($this->authorized !== self::WAITING_SIGNUP) { if ($this->authorized !== self::WAITING_SIGNUP) {
@ -152,8 +168,10 @@ trait Login
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization(); $this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['signup_ok']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['signup_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
public function complete_2fa_login($password) public function complete_2fa_login($password)
{ {
if ($this->authorized !== self::WAITING_PASSWORD) { if ($this->authorized !== self::WAITING_PASSWORD) {
@ -161,11 +179,12 @@ trait Login
} }
$this->authorized = self::NOT_LOGGED_IN; $this->authorized = self::NOT_LOGGED_IN;
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_user']], \danog\MadelineProto\Logger::NOTICE); \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->authorized = self::LOGGED_IN;
$this->datacenter->sockets[$this->datacenter->curdc]->authorized = true; $this->datacenter->sockets[$this->datacenter->curdc]->authorized = true;
$this->init_authorization(); $this->init_authorization();
\danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([\danog\MadelineProto\Lang::$current_lang['login_ok']], \danog\MadelineProto\Logger::NOTICE);
return $this->authorization; return $this->authorization;
} }
} }