HUGE performance improvements (run composer update), started to work on threading
This commit is contained in:
parent
f7f80241f2
commit
81c1bcd208
@ -4,9 +4,9 @@ group: edge
|
|||||||
language: php
|
language: php
|
||||||
php:
|
php:
|
||||||
- '7.0'
|
- '7.0'
|
||||||
|
- '7.1'
|
||||||
- nightly
|
- nightly
|
||||||
- hhvm
|
- hhvm
|
||||||
- '5.6'
|
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
|
@ -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).
|
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.
|
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
|
### 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.
|
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.
|
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:
|
Example:
|
||||||
@ -324,9 +324,13 @@ The same operation should be done when serializing to another destination manual
|
|||||||
|
|
||||||
MadelineProto can throw three different exceptions:
|
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\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\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\TL\Exception - Thrown on TL serialization/deserialization errors
|
||||||
|
|
||||||
|
* \danog\MadelineProto\NothingInTheSocketException - Thrown if no data can be read from the TCP socket
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -5,11 +5,19 @@
|
|||||||
"license": "AGPLV3",
|
"license": "AGPLV3",
|
||||||
"homepage": "https://daniil.it/MadelineProto",
|
"homepage": "https://daniil.it/MadelineProto",
|
||||||
"keywords": ["telegram", "mtproto", "protocol", "bytes", "messenger", "client", "PHP", "video", "stickers", "audio", "files", "GB"],
|
"keywords": ["telegram", "mtproto", "protocol", "bytes", "messenger", "client", "PHP", "video", "stickers", "audio", "files", "GB"],
|
||||||
|
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/danog/phpseclib"
|
||||||
|
}
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.6.0",
|
"php": ">=5.6.0",
|
||||||
"danog/phpstruct": "^1.2",
|
"danog/phpstruct": "dev-fast",
|
||||||
"phpseclib/phpseclib": "^2.0.4",
|
"phpseclib/phpseclib": "dev-master",
|
||||||
"vlucas/phpdotenv": "^2.4"
|
"vlucas/phpdotenv": "^2.4",
|
||||||
|
"krakjoe/pthreads-polyfill": "dev-master"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpdocumentor/reflection-docblock": "^3.1"
|
"phpdocumentor/reflection-docblock": "^3.1"
|
||||||
|
@ -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).
|
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.
|
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
|
### 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.
|
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.
|
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:
|
Example:
|
||||||
@ -328,9 +328,13 @@ The same operation should be done when serializing to another destination manual
|
|||||||
|
|
||||||
MadelineProto can throw three different exceptions:
|
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\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\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\TL\Exception - Thrown on TL serialization/deserialization errors
|
||||||
|
|
||||||
|
* \danog\MadelineProto\NothingInTheSocketException - Thrown if no data can be read from the TCP socket
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
185
pipesbot.php
Executable file
185
pipesbot.php
Executable 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);
|
||||||
|
}
|
@ -103,8 +103,6 @@ class Connection
|
|||||||
case 'tcp_abridged':
|
case 'tcp_abridged':
|
||||||
case 'tcp_intermediate':
|
case 'tcp_intermediate':
|
||||||
case 'tcp_full':
|
case 'tcp_full':
|
||||||
fclose($this->sock);
|
|
||||||
break;
|
|
||||||
case 'http':
|
case 'http':
|
||||||
case 'https':
|
case 'https':
|
||||||
fclose($this->sock);
|
fclose($this->sock);
|
||||||
@ -128,16 +126,6 @@ class Connection
|
|||||||
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout);
|
$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)
|
public function write($what, $length = null)
|
||||||
{
|
{
|
||||||
if ($length !== null) {
|
if ($length !== null) {
|
||||||
@ -179,6 +167,9 @@ class Connection
|
|||||||
throw new Exception("Connection: couldn't connect to socket.");
|
throw new Exception("Connection: couldn't connect to socket.");
|
||||||
}
|
}
|
||||||
$packet = stream_get_contents($this->sock, $length);
|
$packet = stream_get_contents($this->sock, $length);
|
||||||
|
if ($packet === false) {
|
||||||
|
throw new NothingInTheSocketException('Nothing in the socket!');
|
||||||
|
}
|
||||||
if (strlen($packet) != $length) {
|
if (strlen($packet) != $length) {
|
||||||
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
|
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) {
|
switch ($this->protocol) {
|
||||||
case 'tcp_full':
|
case 'tcp_full':
|
||||||
$packet_length_data = $this->read(4);
|
$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_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
|
||||||
$packet = $this->read($packet_length - 4);
|
$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!');
|
throw new Exception('CRC32 was not correct!');
|
||||||
}
|
}
|
||||||
$this->in_seq_no++;
|
$this->in_seq_no++;
|
||||||
@ -211,22 +199,13 @@ class Connection
|
|||||||
throw new Exception('Incoming seq_no mismatch');
|
throw new Exception('Incoming seq_no mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
$payload = $this->fopen_and_write('php://memory', 'rw+b', substr($packet, 4, $packet_length - 12));
|
return substr($packet, 4, $packet_length - 12);
|
||||||
break;
|
|
||||||
case 'tcp_intermediate':
|
case 'tcp_intermediate':
|
||||||
$packet_length_data = $this->read(4);
|
$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_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
|
||||||
$packet = $this->read($packet_length);
|
return $this->read($packet_length);
|
||||||
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
|
|
||||||
break;
|
|
||||||
case 'tcp_abridged':
|
case 'tcp_abridged':
|
||||||
$packet_length_data = $this->read(1);
|
$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);
|
$packet_length = ord($packet_length_data);
|
||||||
if ($packet_length < 127) {
|
if ($packet_length < 127) {
|
||||||
$packet_length <<= 2;
|
$packet_length <<= 2;
|
||||||
@ -234,9 +213,7 @@ class Connection
|
|||||||
$packet_length_data = $this->read(3);
|
$packet_length_data = $this->read(3);
|
||||||
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data.pack('x'))[0] << 2;
|
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data.pack('x'))[0] << 2;
|
||||||
}
|
}
|
||||||
$packet = $this->read($packet_length);
|
return $this->read($packet_length);
|
||||||
$payload = $this->fopen_and_write('php://memory', 'rw+b', $packet);
|
|
||||||
break;
|
|
||||||
case 'http':
|
case 'http':
|
||||||
case 'https':
|
case 'https':
|
||||||
$headers = [];
|
$headers = [];
|
||||||
@ -261,19 +238,17 @@ class Connection
|
|||||||
}
|
}
|
||||||
$headers[] = $current_header;
|
$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') {
|
if ($headers[0] !== 'HTTP/1.1 200 OK') {
|
||||||
throw new Exception($headers[0]);
|
throw new Exception($headers[0]);
|
||||||
}
|
}
|
||||||
if ($close) {
|
if ($close) {
|
||||||
$this->close_and_reopen();
|
$this->close_and_reopen();
|
||||||
}
|
}
|
||||||
break;
|
return $read;
|
||||||
case 'udp':
|
case 'udp':
|
||||||
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
throw new Exception("Connection: This protocol wasn't implemented yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $payload;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function send_message($message)
|
public function send_message($message)
|
||||||
@ -282,7 +257,7 @@ class Connection
|
|||||||
case 'tcp_full':
|
case 'tcp_full':
|
||||||
$this->out_seq_no++;
|
$this->out_seq_no++;
|
||||||
$step1 = \danog\PHP\Struct::pack('<II', (strlen($message) + 12), $this->out_seq_no).$message;
|
$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);
|
$this->write($step2);
|
||||||
break;
|
break;
|
||||||
case 'tcp_intermediate':
|
case 'tcp_intermediate':
|
||||||
|
@ -26,7 +26,7 @@ class Exception extends \Exception
|
|||||||
return true; // return true to continue through the others error handlers
|
return true; // return true to continue through the others error handlers
|
||||||
}
|
}
|
||||||
if (\danog\MadelineProto\Logger::$constructed) {
|
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 = new \danog\MadelineProto\Exception($errstr, $errno);
|
||||||
$e->file = $errfile;
|
$e->file = $errfile;
|
||||||
|
@ -202,6 +202,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
|
|||||||
'incoming' => 1000,
|
'incoming' => 1000,
|
||||||
'outgoing' => 1000,
|
'outgoing' => 1000,
|
||||||
],
|
],
|
||||||
|
'peer' => ['full_info_cache_time' => 60],
|
||||||
'updates' => [
|
'updates' => [
|
||||||
'handle_updates' => true, // Should I handle 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
|
'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()
|
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';
|
$test = $this->config['test_mode'] ? 'test' : 'main';
|
||||||
$ipv6 = ($dc['ipv6'] ? 'ipv6' : 'ipv4');
|
$ipv6 = ($dc['ipv6'] ? 'ipv6' : 'ipv4');
|
||||||
$id = $dc['id'];
|
$id = $dc['id'];
|
||||||
$test .= (isset($this->settings['connection'][$test][$ipv6][$id]) && $this->settings['connection'][$test][$ipv6][$id]['ip_address'] != $dc['ip_address']) ? '_bk' : '';
|
$test .= (isset($this->settings['connection'][$test][$ipv6][$id]) && $this->settings['connection'][$test][$ipv6][$id]['ip_address'] != $dc['ip_address']) ? '_bk' : '';
|
||||||
$this->settings['connection'][$test][$ipv6][$id] = $dc;
|
$this->settings['connection'][$test][$ipv6][$id] = $dc;
|
||||||
}
|
}
|
||||||
unset($this->config['dc_options']);
|
|
||||||
\danog\MadelineProto\Logger::log(['Updated config!', $this->config], Logger::NOTICE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,13 +213,13 @@ trait AuthKeyHandler
|
|||||||
* int $server_time
|
* 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
|
* 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) {
|
if (sha1(substr($answer, 0, $server_DH_inner_data_length), true) != $answer_hash) {
|
||||||
throw new \danog\MadelineProto\Exception('answer_hash mismatch.');
|
throw new \danog\MadelineProto\Exception('answer_hash mismatch.');
|
||||||
}
|
}
|
||||||
|
@ -29,62 +29,17 @@ trait Crypt
|
|||||||
|
|
||||||
public function ige_encrypt($message, $key, $iv)
|
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)
|
public function ige_decrypt($message, $key, $iv)
|
||||||
{
|
{
|
||||||
return $this->_ige($message, $key, $iv, 'decrypt');
|
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_IGE);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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->setKey($key);
|
$cipher->setKey($key);
|
||||||
$cipher->paddable = false;
|
$cipher->setIV($iv);
|
||||||
$blocksize = $cipher->block_size;
|
return $cipher->decrypt($message);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,11 +35,11 @@ trait MessageHandler
|
|||||||
$message = str_repeat(chr(0), 8).$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
|
$message = str_repeat(chr(0), 8).$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
|
||||||
} else {
|
} else {
|
||||||
$seq_no = $this->generate_seq_no($content_related);
|
$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;
|
$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;
|
||||||
$message_key = substr(sha1($encrypted_data, true), -16);
|
$padding = $this->random($this->posmod(-strlen($data2enc), 16));
|
||||||
$padding = $this->random($this->posmod(-strlen($encrypted_data), 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']);
|
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]['seq_no'] = $seq_no;
|
||||||
}
|
}
|
||||||
$this->datacenter->outgoing_messages[$int_message_id]['response'] = -1;
|
$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()
|
public function recv_message()
|
||||||
{
|
{
|
||||||
$payload = $this->datacenter->read_message();
|
$payload = $this->datacenter->read_message();
|
||||||
if (fstat($payload)['size'] === 4) {
|
if (strlen($payload) === 4) {
|
||||||
$error = \danog\PHP\Struct::unpack('<i', stream_get_contents($payload, 4))[0];
|
$error = \danog\PHP\Struct::unpack('<i', $payload)[0];
|
||||||
if ($error === -404) {
|
if ($error === -404) {
|
||||||
if ($this->datacenter->temp_auth_key != null) {
|
if ($this->datacenter->temp_auth_key != null) {
|
||||||
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
|
\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);
|
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)) {
|
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);
|
$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']) {
|
} elseif ($auth_key_id === $this->datacenter->temp_auth_key['id']) {
|
||||||
$message_key = stream_get_contents($payload, 16);
|
$message_key = substr($payload, 8, 16);
|
||||||
$encrypted_data = stream_get_contents($payload);
|
$encrypted_data = substr($payload, 24);
|
||||||
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server');
|
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server');
|
||||||
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ trait MessageHandler
|
|||||||
} else {
|
} else {
|
||||||
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
|
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]['content'] = $deserialized;
|
||||||
$this->datacenter->incoming_messages[$message_id]['response'] = -1;
|
$this->datacenter->incoming_messages[$message_id]['response'] = -1;
|
||||||
$this->datacenter->new_incoming[$message_id] = $message_id;
|
$this->datacenter->new_incoming[$message_id] = $message_id;
|
||||||
|
@ -262,9 +262,18 @@ trait PeerHandler
|
|||||||
|
|
||||||
return $res;
|
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)
|
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);
|
$partial = $this->get_info($id);
|
||||||
switch ($partial['type']) {
|
switch ($partial['type']) {
|
||||||
case 'user':
|
case 'user':
|
||||||
@ -283,6 +292,8 @@ trait PeerHandler
|
|||||||
}
|
}
|
||||||
$partial = $this->get_info($id);
|
$partial = $this->get_info($id);
|
||||||
$partial['full'] = $full;
|
$partial['full'] = $full;
|
||||||
|
$partial['last_update'] = time();
|
||||||
|
$this->full_chats[$partial['id']] = $partial;
|
||||||
|
|
||||||
return $partial;
|
return $partial;
|
||||||
}
|
}
|
||||||
|
@ -23,32 +23,22 @@ trait UpdateHandler
|
|||||||
public $updates_key = 0;
|
public $updates_key = 0;
|
||||||
private $getting_state = false;
|
private $getting_state = false;
|
||||||
public $full_chats;
|
public $full_chats;
|
||||||
|
private $msg_ids = [];
|
||||||
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 pwr_update_handler($update)
|
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 {
|
try {
|
||||||
$full_chat = $this->get_pwr_chat($update['message']['to_id']);
|
$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) {
|
} catch (\danog\MadelineProto\Exception $e) {
|
||||||
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
|
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
|
||||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||||
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
|
\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 {
|
try {
|
||||||
$full_chat = $this->get_pwr_chat($update['message']['from_id']);
|
$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) {
|
} catch (\danog\MadelineProto\Exception $e) {
|
||||||
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
|
\danog\MadelineProto\Logger::log([$e->getMessage()], \danog\MadelineProto\Logger::WARNING);
|
||||||
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
} catch (\danog\MadelineProto\RPCErrorException $e) {
|
||||||
@ -121,7 +111,15 @@ trait UpdateHandler
|
|||||||
$this->get_channel_state($channel)['pts'] = $data['pts'];
|
$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)
|
public function get_channel_difference($channel)
|
||||||
{
|
{
|
||||||
if (!$this->settings['updates']['handle_updates']) {
|
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]);
|
$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);
|
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
|
||||||
|
$this->get_channel_state($channel)['sync_loading'] = false;
|
||||||
switch ($difference['_']) {
|
switch ($difference['_']) {
|
||||||
case 'updates.channelDifferenceEmpty':
|
case 'updates.channelDifferenceEmpty':
|
||||||
$this->set_channel_state($channel, $difference);
|
$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));
|
throw new \danog\MadelineProto\Exception('Unrecognized update difference received: '.var_export($difference, true));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$this->get_channel_state($channel)['sync_loading'] = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function set_update_state($data)
|
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]);
|
$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);
|
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
|
||||||
|
$this->get_update_state()['sync_loading'] = false;
|
||||||
|
|
||||||
switch ($difference['_']) {
|
switch ($difference['_']) {
|
||||||
case 'updates.differenceEmpty':
|
case 'updates.differenceEmpty':
|
||||||
$this->set_update_state($difference);
|
$this->set_update_state($difference);
|
||||||
@ -227,7 +227,6 @@ trait UpdateHandler
|
|||||||
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;
|
||||||
}
|
}
|
||||||
$this->get_update_state()['sync_loading'] = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_updates_state()
|
public function get_updates_state()
|
||||||
@ -245,7 +244,6 @@ trait UpdateHandler
|
|||||||
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);
|
||||||
//var_dump($update, $options);
|
|
||||||
|
|
||||||
$channel_id = false;
|
$channel_id = false;
|
||||||
switch ($update['_']) {
|
switch ($update['_']) {
|
||||||
@ -276,13 +274,12 @@ trait UpdateHandler
|
|||||||
} else {
|
} else {
|
||||||
$cur_state = &$this->get_channel_state($channel_id, (isset($update['pts']) ? $update['pts'] : 0) - (isset($update['pts_count']) ? $update['pts_count'] : 0));
|
$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'] && in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
|
||||||
if ($cur_state['sync_loading']) {
|
|
||||||
\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);
|
||||||
@ -340,6 +337,7 @@ trait UpdateHandler
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ($update['pts'] > $cur_state['pts']) {
|
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'];
|
$cur_state['pts'] = $update['pts'];
|
||||||
$this->should_serialize = true;
|
$this->should_serialize = true;
|
||||||
$pop_pts = true;
|
$pop_pts = true;
|
||||||
@ -372,7 +370,6 @@ trait UpdateHandler
|
|||||||
$pop_seq = true;
|
$pop_seq = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->save_update($update);
|
$this->save_update($update);
|
||||||
|
|
||||||
if ($pop_pts) {
|
if ($pop_pts) {
|
||||||
@ -447,11 +444,11 @@ trait UpdateHandler
|
|||||||
}
|
}
|
||||||
if ($channel === false) {
|
if ($channel === false) {
|
||||||
foreach ($updates as $update) {
|
foreach ($updates as $update) {
|
||||||
$this->handle_update($update, $options);
|
$this->handle_update($update, $options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
foreach ($updates as $update) {
|
foreach ($updates as $update) {
|
||||||
$this->handle_update($update);
|
$this->handle_update($update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,6 +465,11 @@ trait UpdateHandler
|
|||||||
|
|
||||||
public function save_update($update)
|
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']) {
|
if (!$this->settings['updates']['handle_updates']) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
17
src/danog/MadelineProto/NothingInTheSocketException.php
Normal file
17
src/danog/MadelineProto/NothingInTheSocketException.php
Normal 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
|
||||||
|
{
|
||||||
|
}
|
@ -25,12 +25,8 @@ class RSA
|
|||||||
$key = new \phpseclib\Crypt\RSA();
|
$key = new \phpseclib\Crypt\RSA();
|
||||||
|
|
||||||
\danog\MadelineProto\Logger::log(['Loading key...'], Logger::ULTRA_VERBOSE);
|
\danog\MadelineProto\Logger::log(['Loading key...'], Logger::ULTRA_VERBOSE);
|
||||||
if (method_exists($key, 'load')) {
|
$key->load($rsa_key);
|
||||||
$key->load($rsa_key);
|
$this->keydata = ['n' => \phpseclib\Common\Functions\Objects::getVar($key, 'modulus'), 'e' => \phpseclib\Common\Functions\Objects::getVar($key, 'exponent')];
|
||||||
} else {
|
|
||||||
$key->loadKey($rsa_key);
|
|
||||||
}
|
|
||||||
$this->keydata = ['n' => $key->modulus, 'e' => $key->exponent];
|
|
||||||
|
|
||||||
\danog\MadelineProto\Logger::log(['Computing fingerprint...'], Logger::ULTRA_VERBOSE);
|
\danog\MadelineProto\Logger::log(['Computing fingerprint...'], Logger::ULTRA_VERBOSE);
|
||||||
$this->keydata['fp'] = \danog\PHP\Struct::unpack('<q', substr(
|
$this->keydata['fp'] = \danog\PHP\Struct::unpack('<q', substr(
|
||||||
|
@ -42,12 +42,16 @@ class Serialization
|
|||||||
*
|
*
|
||||||
* @param string $filename
|
* @param string $filename
|
||||||
*
|
*
|
||||||
* @return bool|API
|
* @return API
|
||||||
|
*
|
||||||
|
* @throws \danog\MadelineProto\Exception
|
||||||
*/
|
*/
|
||||||
public static function deserialize($filename)
|
public static function deserialize($filename)
|
||||||
{
|
{
|
||||||
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
src/danog/MadelineProto/Stream.php
Normal file
33
src/danog/MadelineProto/Stream.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -287,7 +287,7 @@ trait TL
|
|||||||
{
|
{
|
||||||
$this->deserialize($bytes_io, $type);
|
$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' => ''])
|
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)) {
|
||||||
if (is_string($bytes_io)) {
|
$bytes_io = new \danog\MadelineProto\Stream($bytes_io);
|
||||||
$bytes_io = $this->fopen_and_write('php://memory', 'rw+b', $bytes_io);
|
} else if (!is_object($bytes_io)) {
|
||||||
} else {
|
throw new Exception('An invalid bytes_io handle was provided.');
|
||||||
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']) {
|
switch ($type['type']) {
|
||||||
case 'Bool':
|
case 'Bool':
|
||||||
return $this->deserialize_bool(stream_get_contents($bytes_io, 4));
|
return $this->deserialize_bool($bytes_io->read(4));
|
||||||
case 'int':
|
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 '#':
|
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':
|
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':
|
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':
|
case 'int128':
|
||||||
return stream_get_contents($bytes_io, 16);
|
return $bytes_io->read(16);
|
||||||
case 'int256':
|
case 'int256':
|
||||||
return stream_get_contents($bytes_io, 32);
|
return $bytes_io->read(32);
|
||||||
case 'int512':
|
case 'int512':
|
||||||
return stream_get_contents($bytes_io, 32);
|
return $bytes_io->read(32);
|
||||||
case 'string':
|
case 'string':
|
||||||
case 'bytes':
|
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) {
|
if ($l > 254) {
|
||||||
throw new Exception('Length is too big');
|
throw new Exception('Length is too big');
|
||||||
}
|
}
|
||||||
if ($l === 254) {
|
if ($l === 254) {
|
||||||
$long_len = \danog\PHP\Struct::unpack('<I', stream_get_contents($bytes_io, 3).chr(0))[0];
|
$long_len = \danog\PHP\Struct::unpack('<I', $bytes_io->read(3).chr(0))[0];
|
||||||
$x = stream_get_contents($bytes_io, $long_len);
|
$x = $bytes_io->read($long_len);
|
||||||
$resto = $this->posmod(-$long_len, 4);
|
$resto = $this->posmod(-$long_len, 4);
|
||||||
if ($resto > 0) {
|
if ($resto > 0) {
|
||||||
stream_get_contents($bytes_io, $resto);
|
$bytes_io->read($resto);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$x = stream_get_contents($bytes_io, $l);
|
$x = $bytes_io->read($l);
|
||||||
$resto = $this->posmod(-($l + 1), 4);
|
$resto = $this->posmod(-($l + 1), 4);
|
||||||
if ($resto > 0) {
|
if ($resto > 0) {
|
||||||
stream_get_contents($bytes_io, $resto);
|
$bytes_io->read($resto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!is_string($x)) {
|
if (!is_string($x)) {
|
||||||
@ -348,14 +346,14 @@ trait TL
|
|||||||
case 'true':
|
case 'true':
|
||||||
return true;
|
return true;
|
||||||
case 'Vector t':
|
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);
|
$constructorData = $this->constructors->find_by_id($id);
|
||||||
if ($constructorData === false) {
|
if ($constructorData === false) {
|
||||||
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
|
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
|
||||||
}
|
}
|
||||||
switch ($constructorData['predicate']) {
|
switch ($constructorData['predicate']) {
|
||||||
case 'gzip_packed':
|
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 t':
|
||||||
case 'vector':
|
case 'vector':
|
||||||
break;
|
break;
|
||||||
@ -363,7 +361,7 @@ trait TL
|
|||||||
throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
|
throw new Exception('Invalid vector constructor: '.$constructorData['predicate']);
|
||||||
}
|
}
|
||||||
case 'vector':
|
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 = [];
|
$result = [];
|
||||||
for ($i = 0; $i < $count; $i++) {
|
for ($i = 0; $i < $count; $i++) {
|
||||||
$result[] = $this->deserialize($bytes_io, ['type' => $type['subtype']]);
|
$result[] = $this->deserialize($bytes_io, ['type' => $type['subtype']]);
|
||||||
@ -380,7 +378,7 @@ trait TL
|
|||||||
} else {
|
} else {
|
||||||
$constructorData = $this->constructors->find_by_predicate($type['type']);
|
$constructorData = $this->constructors->find_by_predicate($type['type']);
|
||||||
if ($constructorData === false) {
|
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);
|
$constructorData = $this->constructors->find_by_id($id);
|
||||||
if ($constructorData === false) {
|
if ($constructorData === false) {
|
||||||
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
|
throw new Exception('Could not extract type: '.$type['type'].' with id '.$id);
|
||||||
@ -388,7 +386,7 @@ trait TL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($constructorData['predicate'] === 'gzip_packed') {
|
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']];
|
$x = ['_' => $constructorData['predicate']];
|
||||||
foreach ($constructorData['params'] as $arg) {
|
foreach ($constructorData['params'] as $arg) {
|
||||||
|
112
src/danog/MadelineProto/Threads/SocketHandler.php
Normal file
112
src/danog/MadelineProto/Threads/SocketHandler.php
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
36
src/danog/MadelineProto/Threads/SocketReader.php
Normal file
36
src/danog/MadelineProto/Threads/SocketReader.php
Normal 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) { ; };
|
||||||
|
}
|
||||||
|
}
|
@ -40,15 +40,6 @@ trait Tools
|
|||||||
return $resto;
|
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)
|
public function utf8ize($d)
|
||||||
{
|
{
|
||||||
if (is_array($d)) {
|
if (is_array($d)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user