HUGE performance improvements (run composer update), started to work on threading

This commit is contained in:
Daniil Gentili 2017-02-11 15:16:57 +01:00
parent f7f80241f2
commit 81c1bcd208
21 changed files with 517 additions and 182 deletions

View File

@ -4,9 +4,9 @@ group: edge
language: php
php:
- '7.0'
- '7.1'
- nightly
- hhvm
- '5.6'
addons:
apt:

View File

@ -10,7 +10,7 @@ Logo created by [Matthew Hesketh](http://matthewhesketh.com) (thanks again!).
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
This project can run on PHP 7, PHP 5.6 and HHVM, only 64 bit systems are supported ATM.
This project can run on PHP 7 and HHVM, only 64 bit systems are supported ATM.
Also note that MadelineProto will perform better if a big math extension like gmp or bcmath is installed.
@ -68,7 +68,7 @@ $MadelineProto = new \danog\MadelineProto\API();
### Settings
The constructor accepts an optional parameter, which is the settings array. This array contains some other arrays, which are the settings for a specific MadelineProto function.
See https://github.com/danog/MadelineProto/blob/master/src/danog/MadelineProto/MTProto.php#L99 for the default values for the settings\ arrays and explanations for every setting.
See [here](https://github.com/danog/MadelineProto/blob/master/src/danog/MadelineProto/MTProto.php#L99) for the default values for the settings arrays and explanations for every setting.
You can provide part of any subsetting array, that way the remaining arrays will be automagically set to default and undefined values of specified subsetting arrays will be set to the default values.
Example:
@ -324,9 +324,13 @@ The same operation should be done when serializing to another destination manual
MadelineProto can throw three different exceptions:
* \danog\MadelineProto\Exception - Default exception, thrown when a php error occures and in a lot of other cases
* \danog\MadelineProto\RPCErrorException - Thrown when an RPC error occurres (an error received via the mtproto API)
* \danog\MadelineProto\TL\Exception - Thrown on TL serialization/deserialization errors
* \danog\MadelineProto\NothingInTheSocketException - Thrown if no data can be read from the TCP socket
## Contributing

View File

@ -5,11 +5,19 @@
"license": "AGPLV3",
"homepage": "https://daniil.it/MadelineProto",
"keywords": ["telegram", "mtproto", "protocol", "bytes", "messenger", "client", "PHP", "video", "stickers", "audio", "files", "GB"],
"repositories": [
{
"type": "git",
"url": "https://github.com/danog/phpseclib"
}
],
"require": {
"php": ">=5.6.0",
"danog/phpstruct": "^1.2",
"phpseclib/phpseclib": "^2.0.4",
"vlucas/phpdotenv": "^2.4"
"danog/phpstruct": "dev-fast",
"phpseclib/phpseclib": "dev-master",
"vlucas/phpdotenv": "^2.4",
"krakjoe/pthreads-polyfill": "dev-master"
},
"require-dev": {
"phpdocumentor/reflection-docblock": "^3.1"

View File

@ -14,7 +14,7 @@ Logo created by [Matthew Hesketh](http://matthewhesketh.com) (thanks again!).
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).
This project can run on PHP 7, PHP 5.6 and HHVM, only 64 bit systems are supported ATM.
This project can run on PHP 7 and HHVM, only 64 bit systems are supported ATM.
Also note that MadelineProto will perform better if a big math extension like gmp or bcmath is installed.
@ -72,7 +72,7 @@ $MadelineProto = new \danog\MadelineProto\API();
### Settings
The constructor accepts an optional parameter, which is the settings array. This array contains some other arrays, which are the settings for a specific MadelineProto function.
See https://github.com/danog/MadelineProto/blob/master/src/danog/MadelineProto/MTProto.php#L99 for the default values for the settings\ arrays and explanations for every setting.
See [here](https://github.com/danog/MadelineProto/blob/master/src/danog/MadelineProto/MTProto.php#L99) for the default values for the settings arrays and explanations for every setting.
You can provide part of any subsetting array, that way the remaining arrays will be automagically set to default and undefined values of specified subsetting arrays will be set to the default values.
Example:
@ -328,9 +328,13 @@ The same operation should be done when serializing to another destination manual
MadelineProto can throw three different exceptions:
* \danog\MadelineProto\Exception - Default exception, thrown when a php error occures and in a lot of other cases
* \danog\MadelineProto\RPCErrorException - Thrown when an RPC error occurres (an error received via the mtproto API)
* \danog\MadelineProto\TL\Exception - Thrown on TL serialization/deserialization errors
* \danog\MadelineProto\NothingInTheSocketException - Thrown if no data can be read from the TCP socket
## Contributing

185
pipesbot.php Executable file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env php
<?php
require 'vendor/autoload.php';
$settings = [];
$MadelineProto = false;
try {
$MadelineProto = \danog\MadelineProto\Serialization::deserialize('bot.madeline');
} catch (\danog\MadelineProto\Exception $e) {
}
$uMadelineProto = \danog\MadelineProto\Serialization::deserialize('pwr.madeline');
if (file_exists('token.php') && $MadelineProto === false) {
include_once 'token.php';
$MadelineProto = new \danog\MadelineProto\API($settings);
$authorization = $MadelineProto->bot_login($token);
\danog\MadelineProto\Logger::log([$authorization], \danog\MadelineProto\Logger::NOTICE);
}
if ($uMadelineProto === false) {
echo 'Loading MadelineProto...'.PHP_EOL;
$uMadelineProto = new \danog\MadelineProto\API(['updates' => ['handle_updates' => false]]);
$sentCode = $uMadelineProto->phone_login(readline());
\danog\MadelineProto\Logger::log([$sentCode], \danog\MadelineProto\Logger::NOTICE);
echo 'Enter the code you received: ';
$code = fgets(STDIN, (isset($sentCode['type']['length']) ? $sentCode['type']['length'] : 5) + 1);
$authorization = $uMadelineProto->complete_phone_login($code);
\danog\MadelineProto\Logger::log([$authorization], \danog\MadelineProto\Logger::NOTICE);
if ($authorization['_'] === 'account.noPassword') {
throw new \danog\MadelineProto\Exception('2FA is enabled but no password is set!');
}
if ($authorization['_'] === 'account.password') {
\danog\MadelineProto\Logger::log(['2FA is enabled'], \danog\MadelineProto\Logger::NOTICE);
$authorization = $uMadelineProto->complete_2fa_login(readline('Please enter your password (hint '.$authorization['hint'].'): '));
}
echo 'Serializing MadelineProto to session.madeline...'.PHP_EOL;
echo 'Wrote '.\danog\MadelineProto\Serialization::serialize('session.madeline', $uMadelineProto).' bytes'.PHP_EOL;
}
function inputify(&$stuff) {
$stuff['_'] = 'input'.ucfirst($stuff['_']);
return $stuff;
}
function translatetext (&$value) {
inputify($value);
if (isset($value['entities'])) {
foreach ($value['entities'] as &$entity) {
if ($entity['_'] === 'messageEntityMentionName') inputify($entity);
}
}
if (isset($value['geo'])) {
$value['geo_point'] = inputify($value['geo']);
}
}
function translate (&$value, $key) {
switch ($value['_']) {
case 'botInlineResult':
$value['_'] = 'inputBotInlineResult';
translatetext($value['send_message']);
return $value;
case 'botInlineMediaResult':
if (isset($value['game'])) throw new \danog\MadelineProto\RPCErrorException('Games are not supported.');
if (isset($value['photo'])) $value['_'] = 'inputBotInlineResultPhoto';
if (isset($value['document'])) $value['_'] = 'inputBotInlineResultDocument';
translatetext($value['send_message']);
return $value;
}
}
$offset = 0;
$start = "This bot can create a pipeline between inline bots.
To use it, simply type an inline query with the following syntax:
Query | @ainlinebot:1 | @binlinebot:lel | @inlinebot \$
This will make an inline query with text \"Query\" to @ainlinebot, take the first result if it's a text message (entities will be ignored, if it's a media message you will be redirected here), then it will make an inline query to @binlinebot with the text received out of the first bot, select the result that is a text message with the word \"lel\" in it (regexes are supported), and finally pipe it to @inlinebot, fetch all results and return them to you.
Note that the query must be terminated by a \$
Created by @danogentili (@daniilgentili) using the daniil.it/MadelineProto PHP MTProto client.";
while (true) {
$updates = $MadelineProto->API->get_updates(['offset' => $offset, 'limit' => 50, 'timeout' => 0]); // Just like in the bot API, you can specify an offset, a limit and a timeout
foreach ($updates as $update) {
$offset = $update['update_id'] + 1; // Just like in the bot API, the offset must be set to the last update_id
switch ($update['update']['_']) {
case 'updateNewMessage':
var_dump($update);
if (isset($update['update']['message']['out']) && $update['update']['message']['out']) {
continue;
}
try {
if (preg_match('|/start|', $update['update']['message']['message'])){
$MadelineProto->messages->sendMessage(['peer' => $update['update']['message']['from_id'], 'message' => $start, 'reply_to_msg_id' => $update['update']['message']['id']]);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
}
break;
case 'updateNewChannelMessage':
if (isset($update['update']['message']['out']) && $update['update']['message']['out']) {
continue;
}
try {
if (preg_match('|/start|', $update['update']['message']['message'])){
$MadelineProto->messages->sendMessage(['peer' => $update['update']['message']['to_id'], 'message' => $start, 'reply_to_msg_id' => $update['update']['message']['id']]);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\Exception $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
}
break;
case 'updateBotInlineQuery':
try {
$sswitch = ['_' => 'inlineBotSwitchPM', 'text' => 'FAQ', 'start_param' => 'lel'];
if ($update['update']['query'] === '') {
$MadelineProto->messages->setInlineBotResults(['query_id' => $update['update']['query_id'], 'results' => [], 'cache_time' => 0, 'switch_pm' => $sswitch]);
} else {
$toset = ['query_id' => $update['update']['query_id'], 'results' => [], 'cache_time' => 0, 'private' => true];
if (preg_match('|\$\s*$|', $update['update']['query'])) {
$exploded = explode('|', preg_replace('/\$\s*$/', '', $update['update']['query']));
array_walk($exploded, function (&$value, $key) { $value = preg_replace(['/^\s+/', '/\s+$/'], '', $value); });
$query = array_shift($exploded);
foreach ($exploded as $current => $botq) {
$bot = preg_replace('|:.*|', '', $botq);
if ($bot === '' || $uMadelineProto->get_info($bot)['bot_api_id'] === $MadelineProto->API->datacenter->authorization['user']['id']) {
$toset['switch_pm'] = $sswitch;
break;
}
$select = preg_replace('|'.$bot.':|', '', $botq);
$results = $uMadelineProto->messages->getInlineBotResults(['bot' => $bot, 'peer' => $update['update']['user_id'], 'query' => $query, 'offset' => $offset]);
if (isset($results['switch_pm'])) {
$toset['switch_pm'] = $results['switch_pm'];
break;
}
$toset['gallery'] = $results['gallery'];
$toset['results'] = [];
if (is_numeric($select)) {
$toset['results'][0] = $results['results'][$select-1];
} else if ($select === '') {
$toset['results'] = $results['results'];
} else {
foreach ($results['results'] as $result) {
if (isset($result['send_message']['message']) && preg_match('|'.$select.'|', $result['send_message']['message'])) {
$toset['results'][0] = $result;
}
}
}
if (!isset($toset['results'][0])) $toset['results'] = $results['results'];
if (count($exploded) - 1 === $current || !isset($toset['results'][0]['send_message']['message'])) break;
$query = $toset['results'][0]['send_message']['message'];
}
}
if (empty($toset['results'])) {
$toset['switch_pm'] = $sswitch;
} else {
array_walk($toset['results'], 'translate');
}
$MadelineProto->messages->setInlineBotResults($toset);
}
} catch (\danog\MadelineProto\RPCErrorException $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
try {
$MadelineProto->messages->sendMessage(['peer' => $update['update']['user_id'], 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) { ; }
try {
$toset['switch_pm'] = $sswitch;
$MadelineProto->messages->setInlineBotResults($toset);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) { ; }
} catch (\danog\MadelineProto\Exception $e) {
$MadelineProto->messages->sendMessage(['peer' => '@danogentili', 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
try {
$MadelineProto->messages->sendMessage(['peer' => $update['update']['user_id'], 'message' => $e->getCode().': '.$e->getMessage().PHP_EOL.$e->getTraceAsString()]);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) { ; }
try {
$toset['switch_pm'] = $sswitch;
$MadelineProto->messages->setInlineBotResults($toset);
} catch (\danog\MadelineProto\RPCErrorException $e) {
} catch (\danog\MadelineProto\Exception $e) { ; }
}
}
}
\danog\MadelineProto\Serialization::serialize('bot.madeline', $MadelineProto);
\danog\MadelineProto\Serialization::serialize('pwr.madeline', $uMadelineProto);
}

View File

@ -103,8 +103,6 @@ class Connection
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
fclose($this->sock);
break;
case 'http':
case 'https':
fclose($this->sock);
@ -128,16 +126,6 @@ class Connection
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout);
}
/**
* Function to get hex crc32.
*
* @param $data Data to encode.
*/
public function newcrc32($data)
{
return hexdec(hash('crc32b', $data));
}
public function write($what, $length = null)
{
if ($length !== null) {
@ -179,6 +167,9 @@ class Connection
throw new Exception("Connection: couldn't connect to socket.");
}
$packet = stream_get_contents($this->sock, $length);
if ($packet === false) {
throw new NothingInTheSocketException('Nothing in the socket!');
}
if (strlen($packet) != $length) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
}
@ -197,12 +188,9 @@ class Connection
switch ($this->protocol) {
case 'tcp_full':
$packet_length_data = $this->read(4);
if (strlen($packet_length_data) < 4) {
throw new Exception('Nothing in the socket!');
}
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
$packet = $this->read($packet_length - 4);
if ($this->newcrc32($packet_length_data.substr($packet, 0, -4)) != \danog\PHP\Struct::unpack('<I', substr($packet, -4))[0]) {
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!');
}
$this->in_seq_no++;
@ -211,22 +199,13 @@ class Connection
throw new Exception('Incoming seq_no mismatch');
}
$payload = $this->fopen_and_write('php://memory', 'rw+b', substr($packet, 4, $packet_length - 12));
break;
return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate':
$packet_length_data = $this->read(4);
if (strlen($packet_length_data) < 4) {
throw new Exception('Nothing in the socket!');
}
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
$packet = $this->read($packet_length);
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
break;
return $this->read($packet_length);
case 'tcp_abridged':
$packet_length_data = $this->read(1);
if (strlen($packet_length_data) < 1) {
throw new Exception('Nothing in the socket!');
}
$packet_length = ord($packet_length_data);
if ($packet_length < 127) {
$packet_length <<= 2;
@ -234,9 +213,7 @@ class Connection
$packet_length_data = $this->read(3);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data.pack('x'))[0] << 2;
}
$packet = $this->read($packet_length);
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
break;
return $this->read($packet_length);
case 'http':
case 'https':
$headers = [];
@ -261,19 +238,17 @@ class Connection
}
$headers[] = $current_header;
}
$payload = $this->fopen_and_write('php://memory', 'rw+b', $this->read($length));
$read = $this->read($length);
if ($headers[0] !== 'HTTP/1.1 200 OK') {
throw new Exception($headers[0]);
}
if ($close) {
$this->close_and_reopen();
}
break;
return $read;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
}
return $payload;
}
public function send_message($message)
@ -282,7 +257,7 @@ class Connection
case 'tcp_full':
$this->out_seq_no++;
$step1 = \danog\PHP\Struct::pack('<II', (strlen($message) + 12), $this->out_seq_no).$message;
$step2 = $step1.\danog\PHP\Struct::pack('<I', $this->newcrc32($step1));
$step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2);
break;
case 'tcp_intermediate':

View File

@ -26,7 +26,7 @@ class Exception extends \Exception
return true; // return true to continue through the others error handlers
}
if (\danog\MadelineProto\Logger::$constructed) {
\danog\MadelineProto\Logger::log([$errstr], \danog\MadelineProto\Logger::FATAL_ERROR);
\danog\MadelineProto\Logger::log([$errstr.' in '.basename($errfile).':'.$errline], \danog\MadelineProto\Logger::FATAL_ERROR);
}
$e = new \danog\MadelineProto\Exception($errstr, $errno);
$e->file = $errfile;

View File

@ -202,6 +202,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
'incoming' => 1000,
'outgoing' => 1000,
],
'peer' => ['full_info_cache_time' => 60],
'updates' => [
'handle_updates' => true, // Should I handle updates?
'callback' => [$this, 'get_updates_update_handler'], // A callable function that will be called every time an update is received, must accept an array (for the update) as the only parameter
@ -336,14 +337,17 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function parse_config()
{
foreach ($this->config['dc_options'] as $dc) {
$this->parse_dc_options($this->config['dc_options']);
unset($this->config['dc_options']);
\danog\MadelineProto\Logger::log(['Updated config!', $this->config], Logger::NOTICE);
}
public function parse_dc_options($dc_options) {
foreach ($dc_options as $dc) {
$test = $this->config['test_mode'] ? 'test' : 'main';
$ipv6 = ($dc['ipv6'] ? 'ipv6' : 'ipv4');
$id = $dc['id'];
$test .= (isset($this->settings['connection'][$test][$ipv6][$id]) && $this->settings['connection'][$test][$ipv6][$id]['ip_address'] != $dc['ip_address']) ? '_bk' : '';
$this->settings['connection'][$test][$ipv6][$id] = $dc;
}
unset($this->config['dc_options']);
\danog\MadelineProto\Logger::log(['Updated config!', $this->config], Logger::NOTICE);
}
}

View File

@ -213,13 +213,13 @@ trait AuthKeyHandler
* int $server_time
* ]
*/
$server_DH_inner_data = $this->deserialize($this->fopen_and_write('php://memory', 'rw+b', $answer));
$server_DH_inner_data = $this->deserialize($answer);
/*
* ***********************************************************************
* Do some checks
*/
$server_DH_inner_data_length = $this->get_length($this->fopen_and_write('php://memory', 'rw+b', $answer));
$server_DH_inner_data_length = $this->get_length(new \danog\MadelineProto\Stream($answer));
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
throw new \danog\MadelineProto\Exception('answer_hash mismatch.');
}

View File

@ -29,62 +29,17 @@ trait Crypt
public function ige_encrypt($message, $key, $iv)
{
return $this->_ige($message, $key, $iv, 'encrypt');
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_IGE);
$cipher->setKey($key);
$cipher->setIV($iv);
return $cipher->encrypt($message);
}
public function ige_decrypt($message, $key, $iv)
{
return $this->_ige($message, $key, $iv, 'decrypt');
}
/**
* Given a key, given an iv, and message
* do whatever operation asked in the operation field.
* Operation will be checked for: "decrypt" and "encrypt" strings.
* Returns the message encrypted/decrypted.
* message must be a multiple by 16 bytes (for division in 16 byte blocks)
* key must be 32 byte
* iv must be 32 byte (it's not internally used in AES 256 ECB, but it's
* needed for IGE).
*/
public function _ige($message, $key, $iv, $operation = 'decrypt')
{
if (strlen($key) != 32) {
throw new \danog\MadelineProto\Exception('key must be 32 bytes long (was '.strlen($key).' bytes)');
}
if (strlen($iv) != 32) {
throw new \danog\MadelineProto\Exception('iv must be 32 bytes long (was '.strlen($iv).' bytes)');
}
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_ECB);
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_IGE);
$cipher->setKey($key);
$cipher->paddable = false;
$blocksize = $cipher->block_size;
if ((strlen($message) % $blocksize) != 0) {
throw new \danog\MadelineProto\Exception('message must be a multiple of 16 bytes (try adding '.(16 - (strlen($message) % 16)).' bytes of padding)');
}
$ivp = substr($iv, 0, $blocksize);
$ivp2 = substr($iv, $blocksize);
$ciphered = '';
for ($i = 0; $i <= strlen($message); $i += $blocksize) {
$indata = substr($message, $i, $blocksize);
if ($operation === 'decrypt') {
$xored = $indata ^ $ivp2;
$decrypt_xored = $cipher->decrypt($xored);
$outdata = $decrypt_xored ^ $ivp;
$ivp = $indata;
$ivp2 = $outdata;
} elseif ($operation === 'encrypt') {
$xored = $indata ^ $ivp;
$encrypt_xored = $cipher->encrypt($xored);
$outdata = $encrypt_xored ^ $ivp2;
$ivp = $outdata;
$ivp2 = $indata;
} else {
throw new \danog\MadelineProto\Exception('Crypt: operation must be either \'decrypt\' or \'encrypt\'');
}
$ciphered .= $outdata;
}
return $ciphered;
$cipher->setIV($iv);
return $cipher->decrypt($message);
}
}

View File

@ -35,11 +35,11 @@ trait MessageHandler
$message = str_repeat(chr(0), 8).$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
} else {
$seq_no = $this->generate_seq_no($content_related);
$encrypted_data = \danog\PHP\Struct::pack('<q', $this->datacenter->temp_auth_key['server_salt']).$this->datacenter->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
$data2enc = \danog\PHP\Struct::pack('<q', $this->datacenter->temp_auth_key['server_salt']).$this->datacenter->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
$padding = $this->random($this->posmod(-strlen($data2enc), 16));
$message_key = substr(sha1($data2enc, true), -16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key']);
$message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($data2enc.$padding, $aes_key, $aes_iv);
$this->datacenter->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
}
$this->datacenter->outgoing_messages[$int_message_id]['response'] = -1;
@ -49,13 +49,13 @@ trait MessageHandler
}
/**
* Reading connectionet and receiving message from server. Check the CRC32.
* Reading connection and receiving message from server.
*/
public function recv_message()
{
$payload = $this->datacenter->read_message();
if (fstat($payload)['size'] === 4) {
$error = \danog\PHP\Struct::unpack('<i', stream_get_contents($payload, 4))[0];
if (strlen($payload) === 4) {
$error = \danog\PHP\Struct::unpack('<i', $payload)[0];
if ($error === -404) {
if ($this->datacenter->temp_auth_key != null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
@ -68,14 +68,14 @@ trait MessageHandler
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$auth_key_id = stream_get_contents($payload, 8);
$auth_key_id = substr($payload, 0, 8);
if ($auth_key_id === str_repeat(chr(0), 8)) {
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', stream_get_contents($payload, 12));
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', substr($payload, 8, 12));
$this->check_message_id($message_id, false);
$message_data = stream_get_contents($payload, $message_length);
$message_data = substr($payload, 20, $message_length);
} elseif ($auth_key_id === $this->datacenter->temp_auth_key['id']) {
$message_key = stream_get_contents($payload, 16);
$encrypted_data = stream_get_contents($payload);
$message_key = substr($payload, 8, 16);
$encrypted_data = substr($payload, 24);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server');
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
@ -121,7 +121,7 @@ trait MessageHandler
} else {
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
}
$deserialized = $this->deserialize($this->fopen_and_write('php://memory', 'rw+b', $message_data));
$deserialized = $this->deserialize($message_data);
$this->datacenter->incoming_messages[$message_id]['content'] = $deserialized;
$this->datacenter->incoming_messages[$message_id]['response'] = -1;
$this->datacenter->new_incoming[$message_id] = $message_id;

View File

@ -262,9 +262,18 @@ trait PeerHandler
return $res;
}
public function full_chat_last_updated($id)
{
$id = $this->get_info($id)['bot_api_id'];
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
}
public function get_full_info($id)
{
if (time() - $this->full_chat_last_updated($id) < (isset($this->settings['peer']['full_info_cache_time']) ? $this->settings['peer']['full_info_cache_time'] : 0)) {
return $this->full_chats[$id];
}
$partial = $this->get_info($id);
switch ($partial['type']) {
case 'user':
@ -283,6 +292,8 @@ trait PeerHandler
}
$partial = $this->get_info($id);
$partial['full'] = $full;
$partial['last_update'] = time();
$this->full_chats[$partial['id']] = $partial;
return $partial;
}

View File

@ -23,32 +23,22 @@ trait UpdateHandler
public $updates_key = 0;
private $getting_state = false;
public $full_chats;
public function full_chat_last_updated($id)
{
$id = $this->get_info($id)['bot_api_id'];
return isset($this->full_chats[$id]['last_update']) ? $this->full_chats[$id]['last_update'] : 0;
}
private $msg_ids = [];
public function pwr_update_handler($update)
{
if (isset($update['message']['to_id']) && time() - $this->full_chat_last_updated($update['message']['to_id']) <= 600) {
if (isset($update['message']['to_id'])) {
try {
$full_chat = $this->get_pwr_chat($update['message']['to_id']);
$full_chat['last_update'] = time();
$this->full_chats[$full_chat['id']] = $full_chat;
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\RPCErrorException $e) {
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
}
}
if (isset($update['message']['from_id']) && time() - $this->full_chat_last_updated($update['message']['from_id']) <= 600) {
if (isset($update['message']['from_id'])) {
try {
$full_chat = $this->get_pwr_chat($update['message']['from_id']);
$full_chat['last_update'] = time();
$this->full_chats[$full_chat['id']] = $full_chat;
} catch (\danog\MadelineProto\Exception $e) {
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
} catch (\danog\MadelineProto\RPCErrorException $e) {
@ -121,7 +111,15 @@ trait UpdateHandler
$this->get_channel_state($channel)['pts'] = $data['pts'];
}
}
public function get_msg_id($peer) {
$id = $this->get_info($peer)['bot_api_id'];
return isset($this->msg_ids[$id]) ? $this->msg_ids[$id] : false;
}
public function set_msg_id($peer, $msg_id) {
$id = $this->get_info($peer)['bot_api_id'];
$this->msg_ids[$id] = $msg_id;
$this->should_serialize = true;
}
public function get_channel_difference($channel)
{
if (!$this->settings['updates']['handle_updates']) {
@ -138,6 +136,7 @@ trait UpdateHandler
}
$difference = $this->method_call('updates.getChannelDifference', ['channel' => $input, 'filter' => ['_' => 'channelMessagesFilterEmpty'], 'pts' => $this->get_channel_state($channel)['pts'], 'limit' => 30]);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->get_channel_state($channel)['sync_loading'] = false;
switch ($difference['_']) {
case 'updates.channelDifferenceEmpty':
$this->set_channel_state($channel, $difference);
@ -163,7 +162,6 @@ trait UpdateHandler
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
break;
}
$this->get_channel_state($channel)['sync_loading'] = false;
}
public function set_update_state($data)
@ -207,6 +205,8 @@ trait UpdateHandler
$difference = $this->method_call('updates.getDifference', ['pts' => $this->get_update_state()['pts'], 'date' => $this->get_update_state()['date'], 'qts' => -1]);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->get_update_state()['sync_loading'] = false;
switch ($difference['_']) {
case 'updates.differenceEmpty':
$this->set_update_state($difference);
@ -227,7 +227,6 @@ trait UpdateHandler
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
break;
}
$this->get_update_state()['sync_loading'] = false;
}
public function get_updates_state()
@ -245,7 +244,6 @@ trait UpdateHandler
return;
}
\danog\MadelineProto\Logger::log(['Handling an update of type '.$update['_'].'...'], \danog\MadelineProto\Logger::VERBOSE);
//var_dump($update, $options);
$channel_id = false;
switch ($update['_']) {
@ -276,13 +274,12 @@ trait UpdateHandler
} else {
$cur_state = &$this->get_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
}
/*
if ($cur_state['sync_loading']) {
if ($cur_state['sync_loading'] && in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
\danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
// return false;
return false;
}
*/
switch ($update['_']) {
case 'updateChannelTooLong':
$this->get_channel_difference($channel_id);
@ -340,6 +337,7 @@ trait UpdateHandler
return false;
}
if ($update['pts'] > $cur_state['pts']) {
\danog\MadelineProto\Logger::log(['Applying pts. current pts: '.$cur_state['pts'].' + pts count: '.(isset($update['pts_count']) ? $update['pts_count'] : 0).' = new pts: '.$new_pts.', channel id: '.$channel_id], \danog\MadelineProto\Logger::VERBOSE);
$cur_state['pts'] = $update['pts'];
$this->should_serialize = true;
$pop_pts = true;
@ -372,7 +370,6 @@ trait UpdateHandler
$pop_seq = true;
}
}
$this->save_update($update);
if ($pop_pts) {
@ -447,11 +444,11 @@ trait UpdateHandler
}
if ($channel === false) {
foreach ($updates as $update) {
$this->handle_update($update, $options);
$this->handle_update($update, $options);
}
} else {
foreach ($updates as $update) {
$this->handle_update($update);
$this->handle_update($update);
}
}
}
@ -468,6 +465,11 @@ trait UpdateHandler
public function save_update($update)
{
if ($update['_'] === 'updateDcOptions') {
\danog\MadelineProto\Logger::log(['Got new dc options'], \danog\MadelineProto\Logger::VERBOSE);
$this->parse_dc_options($update['dc_options']);
return;
}
if (!$this->settings['updates']['handle_updates']) {
return;
}

View File

@ -0,0 +1,17 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class NothingInTheSocketException extends \Exception
{
}

View File

@ -25,12 +25,8 @@ class RSA
$key = new \phpseclib\Crypt\RSA();
\danog\MadelineProto\Logger::log(['Loading key...'], Logger::ULTRA_VERBOSE);
if (method_exists($key, 'load')) {
$key->load($rsa_key);
} else {
$key->loadKey($rsa_key);
}
$this->keydata = ['n' => $key->modulus, 'e' => $key->exponent];
$key->load($rsa_key);
$this->keydata = ['n' => \phpseclib\Common\Functions\Objects::getVar($key, 'modulus'), 'e' => \phpseclib\Common\Functions\Objects::getVar($key, 'exponent')];
\danog\MadelineProto\Logger::log(['Computing fingerprint...'], Logger::ULTRA_VERBOSE);
$this->keydata['fp'] = \danog\PHP\Struct::unpack('<q', substr(

View File

@ -42,12 +42,16 @@ class Serialization
*
* @param string $filename
*
* @return bool|API
* @return API
*
* @throws \danog\MadelineProto\Exception
*/
public static function deserialize($filename)
{
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
return file_exists($filename) ? unserialize(file_get_contents($filename)) : false;
if (file_exists($filename)) $unserialized = unserialize(file_get_contents($filename)); else throw new Exception('File does not exist');
//if ($unserialized === false) throw new Exception('An error occurred on deserialization');
return $unserialized;
}
}

View File

@ -0,0 +1,33 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
The PWRTelegram API is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
/**
* Manages connection to telegram servers.
*/
class Stream
{
public $pos = 0;
public function __construct($string)
{
$this->string = $string;
}
public function read($length)
{
$d = substr($this->string, $this->pos, $length);
$this->pos += $length;
return $d;
}
}

View File

@ -287,7 +287,7 @@ trait TL
{
$this->deserialize($bytes_io, $type);
return ftell($bytes_io);
return $bytes_io->pos;
}
/**
@ -295,49 +295,47 @@ trait TL
*/
public function deserialize($bytes_io, $type = ['type' => ''])
{
if (!(!is_string($bytes_io) && (get_resource_type($bytes_io) === 'file' || get_resource_type($bytes_io) === 'stream'))) {
if (is_string($bytes_io)) {
$bytes_io = $this->fopen_and_write('php://memory', 'rw+b', $bytes_io);
} else {
throw new Exception('An invalid bytes_io handle was provided.');
}
if (is_string($bytes_io)) {
$bytes_io = new \danog\MadelineProto\Stream($bytes_io);
} else if (!is_object($bytes_io)) {
throw new Exception('An invalid bytes_io handle was provided.');
}
//\danog\MadelineProto\Logger::log(['Deserializing '.$type['type'].' at byte '.ftell($bytes_io));
//\danog\MadelineProto\Logger::log(['Deserializing '.$type['type'].' at byte '.$bytes_io->pos);
switch ($type['type']) {
case 'Bool':
return $this->deserialize_bool(stream_get_contents($bytes_io, 4));
return $this->deserialize_bool($bytes_io->read(4));
case 'int':
return \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
return \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0];
case '#':
return \danog\PHP\Struct::unpack('<I', stream_get_contents($bytes_io, 4))[0];
return \danog\PHP\Struct::unpack('<I', $bytes_io->read(4))[0];
case 'long':
return \danog\PHP\Struct::unpack('<q', stream_get_contents($bytes_io, 8))[0];
return \danog\PHP\Struct::unpack('<q', $bytes_io->read(8))[0];
case 'double':
return \danog\PHP\Struct::unpack('<d', stream_get_contents($bytes_io, 8))[0];
return \danog\PHP\Struct::unpack('<d', $bytes_io->read(8))[0];
case 'int128':
return stream_get_contents($bytes_io, 16);
return $bytes_io->read(16);
case 'int256':
return stream_get_contents($bytes_io, 32);
return $bytes_io->read(32);
case 'int512':
return stream_get_contents($bytes_io, 32);
return $bytes_io->read(32);
case 'string':
case 'bytes':
$l = \danog\PHP\Struct::unpack('<B', stream_get_contents($bytes_io, 1))[0];
$l = \danog\PHP\Struct::unpack('<B', $bytes_io->read(1))[0];
if ($l > 254) {
throw new Exception('Length is too big');
}
if ($l === 254) {
$long_len = \danog\PHP\Struct::unpack('<I', stream_get_contents($bytes_io, 3).chr(0))[0];
$x = stream_get_contents($bytes_io, $long_len);
$long_len = \danog\PHP\Struct::unpack('<I', $bytes_io->read(3).chr(0))[0];
$x = $bytes_io->read($long_len);
$resto = $this->posmod(-$long_len, 4);
if ($resto > 0) {
stream_get_contents($bytes_io, $resto);
$bytes_io->read($resto);
}
} else {
$x = stream_get_contents($bytes_io, $l);
$x = $bytes_io->read($l);
$resto = $this->posmod(-($l + 1), 4);
if ($resto > 0) {
stream_get_contents($bytes_io, $resto);
$bytes_io->read($resto);
}
}
if (!is_string($x)) {
@ -348,14 +346,14 @@ trait TL
case 'true':
return true;
case 'Vector t':
$id = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$id = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0];
$constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) {
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
}
switch ($constructorData['predicate']) {
case 'gzip_packed':
return $this->deserialize($this->fopen_and_write('php://memory', 'rw+b', gzdecode($this->deserialize($bytes_io, ['type' => 'string']))));
return $this->deserialize(gzdecode($this->deserialize($bytes_io, ['type' => 'string'])));
case 'Vector t':
case 'vector':
break;
@ -363,7 +361,7 @@ trait TL
throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
}
case 'vector':
$count = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$count = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0];
$result = [];
for ($i = 0; $i < $count; $i++) {
$result[] = $this->deserialize($bytes_io, ['type' => $type['subtype']]);
@ -380,7 +378,7 @@ trait TL
} else {
$constructorData = $this->constructors->find_by_predicate($type['type']);
if ($constructorData === false) {
$id = \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0];
$id = \danog\PHP\Struct::unpack('<i', $bytes_io->read(4))[0];
$constructorData = $this->constructors->find_by_id($id);
if ($constructorData === false) {
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
@ -388,7 +386,7 @@ trait TL
}
}
if ($constructorData['predicate'] === 'gzip_packed') {
return $this->deserialize($this->fopen_and_write('php://memory', 'rw+b', gzdecode($this->deserialize($bytes_io, ['type' => 'string']))));
return $this->deserialize(gzdecode($this->deserialize($bytes_io, ['type' => 'string'])));
}
$x = ['_' => $constructorData['predicate']];
foreach ($constructorData['params'] as $arg) {

View File

@ -0,0 +1,112 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\Threads;
/**
* Manages packing and unpacking of messages, and the list of sent and received messages.
*/
class SocketHandler extends Threaded
{
public $payloads = [];
public function __construct(&$me) {
$this->API = $me;
}
/**
* Reading connection and receiving message from server. Check the CRC32.
*/
public function run()
{
$this->socket_handler->synchronized(function ($thread) {
if (empty($thread->payloads)) {
$thread->wait();
} else {
foreach ($thread->payloads as $payload) {
if (fstat($payload)['size'] === 4) {
$error = \danog\PHP\Struct::unpack('<i', stream_get_contents($payload, 4))[0];
if ($error === -404) {
if ($thread->API->datacenter->temp_auth_key != null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$thread->API->datacenter->temp_auth_key = null;
$thread->API->init_authorization();
$thread->API->config = $thread->API->write_client_info('help.getConfig');
$thread->API->parse_config();
continue;
//throw new \danog\MadelineProto\Exception('I had to recreate the temporary authorization key');
}
}
throw new \danog\MadelineProto\RPCErrorException($error, $error);
}
$auth_key_id = stream_get_contents($payload, 8);
if ($auth_key_id === str_repeat(chr(0), 8)) {
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', stream_get_contents($payload, 12));
$thread->API->check_message_id($message_id, false);
$message_data = stream_get_contents($payload, $message_length);
} elseif ($auth_key_id === $thread->API->datacenter->temp_auth_key['id']) {
$message_key = stream_get_contents($payload, 16);
$encrypted_data = stream_get_contents($payload);
list($aes_key, $aes_iv) = $thread->API->aes_calculate($message_key, $thread->API->datacenter->temp_auth_key['auth_key'], 'from server');
$decrypted_data = $thread->API->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
$server_salt = \danog\PHP\Struct::unpack('<q', substr($decrypted_data, 0, 8))[0];
if ($server_salt != $thread->API->datacenter->temp_auth_key['server_salt']) {
//\danog\MadelineProto\Logger::log(['WARNING: Server salt mismatch (my server salt '.$thread->API->datacenter->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').'], \danog\MadelineProto\Logger::WARNING);
}
$session_id = substr($decrypted_data, 8, 8);
if ($session_id != $thread->API->datacenter->session_id) {
throw new \danog\MadelineProto\Exception('Session id mismatch.');
}
$message_id = \danog\PHP\Struct::unpack('<Q', substr($decrypted_data, 16, 8))[0];
$thread->API->check_message_id($message_id, false);
$seq_no = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 24, 4))[0];
// Dunno how to handle any incorrect sequence numbers
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 28, 4))[0];
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\Exception('message_data_length is too big');
}
if ((strlen($decrypted_data) - 32) - $message_data_length > 15) {
throw new \danog\MadelineProto\Exception('difference between message_data_length and the length of the remaining decrypted buffer is too big');
}
if ($message_data_length < 0) {
throw new \danog\MadelineProto\Exception('message_data_length not positive');
}
if ($message_data_length % 4 != 0) {
throw new \danog\MadelineProto\Exception('message_data_length not divisible by 4');
}
$message_data = substr($decrypted_data, 32, $message_data_length);
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
throw new \danog\MadelineProto\Exception('msg_key mismatch');
}
$thread->API->datacenter->incoming_messages[$message_id]['seq_no'] = $seq_no;
} else {
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
}
$deserialized = $thread->API->deserialize($message_data);
$thread->API->datacenter->incoming_messages[$message_id]['content'] = $deserialized;
$thread->API->datacenter->incoming_messages[$message_id]['response'] = -1;
$thread->API->datacenter->new_incoming[$message_id] = $message_id;
$thread->API->handle_messages();
}
}
}, $this);
}
}

View File

@ -0,0 +1,36 @@
<?php
/*
Copyright 2016-2017 Daniil Gentili
(https://daniil.it)
This file is part of MadelineProto.
MadelineProto is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
MadelineProto is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Affero General Public License for more details.
You should have received a copy of the GNU General Public License along with MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto\Threads;
/**
* Manages packing and unpacking of messages, and the list of sent and received messages.
*/
class SocketReader extends Threaded
{
public function __construct(&$me) {
$this->API = $me;
}
/**
* Reading connection and receiving message from server. Check the CRC32.
*/
public function run()
{
try {
$payload = $this->API->datacenter->read_message();
$this->socket_handler->synchronized(function ($thread, $payload) {
$thread->payloads[] = $payload;
$thread->notify();
}, $this->API->socket_handler, $payload);
} catch (\danog\MadelineProto\NothingInTheSocketException $e) { ; };
}
}

View File

@ -40,15 +40,6 @@ trait Tools
return $resto;
}
public function fopen_and_write($filename, $mode, $data)
{
$handle = fopen($filename, $mode);
fwrite($handle, $data);
rewind($handle);
return $handle;
}
public function utf8ize($d)
{
if (is_array($d)) {