Clickable buttons, thread safety (#119)

* I'll just leave this here

* Threading fixes

* Apply fixes from StyleCI

* Composer fixes

* Lots of threading fixes, included all RSA keys

* Apply fixes from StyleCI

* Updated phpseclib

* fixes

* Apply fixes from StyleCI

* final fixes

* git add -A

* Apply fixes from StyleCI

* bugfix

* Fixes

* Apply fixes from StyleCI

* Small fixes

* Final fixes

* Speed improvements

* speed fixes

* Apply fixes from StyleCI

* This is faster than sanic

* Apply fixes from StyleCI

* Final speed fixes

* Apply fixes from StyleCI

* Less logging

* Speed+++

* Apply fixes from StyleCI

* More fixes

* Bug74586Exception

* Apply fixes from StyleCI

* Fixes

* Lemme fix that dumb-ass bug that broke everything

* Apply fixes from StyleCI

* Updated rollbar token

* Fixes for other tcp_* protocols

* Apply fixes from StyleCI

* No need for phpstruct anymore

* Add a conflict (OH NOES pony warfare)

* Less logs, beginning of clickable buttons

* Apply fixes from StyleCI

* Bugfixes, fixed clickable buttons

* Apply fixes from StyleCI

* Better errors

* Apply fixes from StyleCI

* You can now click text buttons

* Apply fixes from StyleCI
This commit is contained in:
Daniil Gentili 2017-05-16 15:12:04 +02:00 committed by GitHub
parent f6208e76cb
commit 915a0cd180
66 changed files with 1101 additions and 818 deletions

1
.gitignore vendored
View File

@ -94,3 +94,4 @@ fuzzer.php
tests/500mb tests/500mb
*.save *.save
*.save.1 *.save.1
*.save.*

44
a.php
View File

@ -1,12 +1,38 @@
<?php <?php
$payload = base64_decode('NAAAAAAAAAAAAAAAAAAAAAAAAAC06N5YFAAAAHiXRmAwx9DuB28gQszy/RHjhN2RdLkYdQ=='); $service_port = getservbyname('www', 'tcp');
$socket = fsockopen('tcp://149.154.167.91:443'); // DC 4 $address = gethostbyname('www.google.com');
$socket = fsockopen('tcp://149.154.167.50:443'); // DC 2 var_dump(unpack('q', pack('l', 200).chr(0).chr(0).chr(0).chr(0)));
stream_set_timeout($socket, 5); class a extends Volatile
echo 'Wrote '.fwrite($socket, $payload).' bytes'.PHP_EOL; {
if (strlen(fread($socket, 100))) { public $a = [];
echo 'Read 100 bytes from socket'.PHP_EOL;
} else { public function run()
echo 'No data could be read from socket'.PHP_EOL; {
$this->a[1] = new b();
$this->a[1]->a['a'] = [];
var_dump($this);
}
} }
class b extends \Volatile
{
public $a = [];
}
class main extends Threaded
{
public function __construct()
{
$this->a = new a();
var_dump($this->a);
$this->a->run();
// One of the OH NOES (b) is printed here
}
public function run()
{
// $this->a;
}
}
$a = new main();
$pool = new Pool(1);
//$pool->submit($a); // One of the OH NOES (a) is printed here

4
ab.php Normal file
View File

@ -0,0 +1,4 @@
<?php
$packet = file_get_contents('t');
var_dump(strrev(hash('crc32b', substr($packet, 0, -4), true)) !== substr($packet, -4));

View File

@ -5,12 +5,14 @@
"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"],
"conflict": {
"krakjoe/pthreads-polyfill": "*"
},
"require": { "require": {
"php": ">=5.6.0", "php": ">=5.6.0",
"danog/phpstruct": "dev-fast",
"danog/primemodule": "dev-master", "danog/primemodule": "dev-master",
"phpseclib/phpseclib": "dev-master#12dc23d9", "danog/magicalserializer": "dev-master",
"phpseclib/phpseclib": "dev-master#200c2a9",
"vlucas/phpdotenv": "^2.4", "vlucas/phpdotenv": "^2.4",
"erusev/parsedown": "^1.6", "erusev/parsedown": "^1.6",
"rollbar/rollbar": "dev-master", "rollbar/rollbar": "dev-master",
@ -29,6 +31,10 @@
"autoload": { "autoload": {
"psr-0": { "psr-0": {
"danog\\MadelineProto\\": "src/" "danog\\MadelineProto\\": "src/"
} },
"files": [
"src/Socket.php",
"src/Volatile.php"
]
} }
} }

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -7,6 +7,26 @@ description: constructors and methods of type KeyboardButton
Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $KeyboardButton->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
### Possible values (constructors): ### Possible values (constructors):
[keyboardButton](../constructors/keyboardButton.md) [keyboardButton](../constructors/keyboardButton.md)

View File

@ -10,8 +10,6 @@ You should have received a copy of the GNU General Public License along with Mad
If not, see <http://www.gnu.org/licenses/>. If not, see <http://www.gnu.org/licenses/>.
*/ */
namespace danog\MadelineProto;
if (!extension_loaded('pthreads')) { if (!extension_loaded('pthreads')) {
class Socket class Socket
{ {

136
src/Volatile.php Normal file
View File

@ -0,0 +1,136 @@
<?php
if (!extension_loaded('pthreads')) {
class Volatile implements ArrayAccess, Countable, IteratorAggregate
{
const NOTHING = (0);
const STARTED = (1 << 0);
const RUNNING = (1 << 1);
const JOINED = (1 << 2);
const ERROR = (1 << 3);
public function offsetSet($offset, $value)
{
$this->__set($offset, $value);
}
public function offsetGet($offset)
{
return $this->{$offset};
}
public function offsetUnset($offset)
{
$this->__unset($offset);
}
public function offsetExists($offset)
{
return isset($this->{$offset});
}
public function count()
{
return count(get_object_vars($this));
}
public function getIterator()
{
return new ArrayIterator(get_object_vars($this));
}
public function __set($offset, $value)
{
if ($offset === null) {
$offset = count(get_object_vars($this));
}
if (!$this instanceof self) {
if (isset($this->{$offset}) &&
$this->{$offset} instanceof Threaded) {
throw new \RuntimeException();
}
}
return $this->{$offset} = $value;
}
public function __unset($offset)
{
if (!$this instanceof self) {
if (isset($this->{$offset}) && $this->{$offset} instanceof Threaded) {
throw new \RuntimeException();
}
}
unset($this->{$offset});
}
public function wait($timeout = 0)
{
return true;
}
public function notify()
{
return true;
}
public function synchronized(Closure $closure, ...$args)
{
return $closure(...$args);
}
public function isRunning()
{
return $this->state & THREAD::RUNNING;
}
public function isTerminated()
{
return $this->state & THREAD::ERROR;
}
public static function extend($class)
{
return true;
}
public function addRef()
{
}
public function delRef()
{
}
public function getRefCount()
{
}
public function lock()
{
return true;
}
public function unlock()
{
return true;
}
public function isWaiting()
{
return false;
}
public function run()
{
}
public function isGarbage()
{
return true;
}
protected $state;
}
}

View File

@ -16,11 +16,12 @@ class API extends APIFactory
{ {
use \danog\MadelineProto\Wrappers\Login; use \danog\MadelineProto\Wrappers\Login;
use \danog\MadelineProto\Wrappers\SettingsManager; use \danog\MadelineProto\Wrappers\SettingsManager;
use \danog\Serializable;
public $API; public $API;
public $namespace = ''; public $namespace = '';
public function __construct($params = []) public function ___construct($params = [])
{ {
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']); set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
$this->API = new MTProto($params); $this->API = new MTProto($params);
@ -46,6 +47,9 @@ class API extends APIFactory
public function __destruct() public function __destruct()
{ {
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
restore_error_handler(); restore_error_handler();
} }

View File

@ -12,92 +12,9 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto; namespace danog\MadelineProto;
class APIFactory class APIFactory extends \Volatile
{ {
/** use Tools;
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var phone
*/
public $phone;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var payments
*/
public $payments;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var bots
*/
public $bots;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var help
*/
public $help;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var upload
*/
public $upload;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var photos
*/
public $photos;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var updates
*/
public $updates;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var messages
*/
public $messages;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var contacts
*/
public $contacts;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var users
*/
public $users;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var channels
*/
public $channels;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var account
*/
public $account;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var auth
*/
public $auth;
/**
* @internal this is a internal property generated by build_docs.php, don't change manually
*
* @var contest
*/
public $contest;
public $namespace; public $namespace;
public $API; public $API;
@ -112,6 +29,6 @@ class APIFactory
{ {
$this->API->get_config([], ['datacenter' => $this->API->datacenter->curdc]); $this->API->get_config([], ['datacenter' => $this->API->datacenter->curdc]);
return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && is_array($arguments[0])) ? $arguments[0] : [], (isset($arguments[1]) && is_array($arguments[1])) ? $arguments[1] : ['datacenter' => $this->API->datacenter->curdc]); return method_exists($this->API, $this->namespace.$name) ? $this->API->{$this->namespace.$name}(...$arguments) : $this->API->method_call($this->namespace.$name, (isset($arguments[0]) && $this->is_array($arguments[0])) ? $arguments[0] : [], (isset($arguments[1]) && $this->is_array($arguments[1])) ? $arguments[1] : ['datacenter' => $this->API->datacenter->curdc]);
} }
} }

View File

@ -17,6 +17,7 @@ use phpDocumentor\Reflection\DocBlockFactory;
class AnnotationsBuilder class AnnotationsBuilder
{ {
use \danog\MadelineProto\TL\TL; use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($settings) public function __construct($settings)
{ {

View File

@ -12,6 +12,6 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto; namespace danog\MadelineProto;
class RSASerializable extends VolatileSerializer class Bug74586Exception extends \Exception
{ {
} }

View File

@ -0,0 +1,48 @@
<?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 Button extends \Volatile implements \JsonSerializable
{
use \danog\Serializable;
private $info = [];
public function __construct($API, $message, $button)
{
foreach ($button as $key => $value) {
$this->{$key} = $value;
}
$this->info['peer'] = $message['to_id'];
$this->info['id'] = $message['id'];
$this->info['API'] = $API;
}
public function click($donotwait = false)
{
switch ($this->_) {
default: return false;
case 'keyboardButtonUrl': return $this->url;
case 'keyboardButton': return $this->info['API']->method_call('messages.sendMessage', ['peer' => $this->info['peer'], 'message' => $this->text, 'reply_to_msg_id' => $this->info['id']], ['datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonCallback': return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'data' => $this->data], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
case 'keyboardButtonGame': return $this->info['API']->method_call('messages.getBotCallbackAnswer', ['peer' => $this->info['peer'], 'msg_id' => $this->info['id'], 'game' => true], ['noResponse' => $donotwait, 'datacenter' => $this->info['API']->datacenter->curdc]);
}
}
public function jsonSerialize()
{
$res = get_object_vars($this);
unset($res['info']);
return $res;
}
}

View File

@ -15,8 +15,9 @@ namespace danog\MadelineProto;
/** /**
* Manages connection to telegram servers. * Manages connection to telegram servers.
*/ */
class Connection class Connection extends \Volatile
{ {
use \danog\Serializable;
use \danog\MadelineProto\Tools; use \danog\MadelineProto\Tools;
public $sock = null; public $sock = null;
@ -24,21 +25,22 @@ class Connection
public $ip = null; public $ip = null;
public $port = null; public $port = null;
public $timeout = null; public $timeout = null;
// public $parsed = [];
public $time_delta = 0; public $time_delta = 0;
public $temp_auth_key; public $temp_auth_key;
public $auth_key; public $auth_key;
public $session_id; public $session_id;
public $session_out_seq_no = 0; public $session_out_seq_no = 0;
public $session_in_seq_no = 0; public $session_in_seq_no = 0;
public $ipv6 = false;
public $incoming_messages = []; public $incoming_messages = [];
public $outgoing_messages = []; public $outgoing_messages = [];
public $new_incoming = []; public $new_incoming = [];
public $new_outgoing = []; public $new_outgoing = [];
public function __construct($ip, $port, $protocol, $timeout) public function ___construct($ip, $port, $protocol, $timeout, $ipv6)
{ {
// Can use: // Can use:
/* /*
- tcp_full - tcp_full
@ -50,42 +52,63 @@ class Connection
*/ */
$this->protocol = $protocol; $this->protocol = $protocol;
$this->timeout = $timeout; $this->timeout = $timeout;
$this->ipv6 = $ipv6;
$this->ip = $ip; $this->ip = $ip;
$this->port = $port; $this->port = $port;
switch ($this->protocol) { switch ($this->protocol) {
case 'tcp_abridged': case 'tcp_abridged':
$this->sock = fsockopen('tcp://'.$ip.':'.$port); $this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
stream_set_timeout($this->sock, $timeout); if (!\danog\MadelineProto\Logger::$has_thread) {
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) { $this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket."); throw new Exception("Connection: couldn't connect to socket.");
} }
$this->write(chr(239)); $this->write(chr(239));
break; break;
case 'tcp_intermediate': case 'tcp_intermediate':
$this->sock = fsockopen('tcp://'.$ip.':'.$port); $this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
stream_set_timeout($this->sock, $timeout); $this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) { $this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket."); throw new Exception("Connection: couldn't connect to socket.");
} }
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
$this->write(str_repeat(chr(238), 4)); $this->write(str_repeat(chr(238), 4));
break; break;
case 'tcp_full': case 'tcp_full':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
stream_set_timeout($this->sock, $timeout); $this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) { if (!$this->sock->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket."); throw new Exception("Connection: couldn't connect to socket.");
} }
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
$this->out_seq_no = -1; $this->out_seq_no = -1;
$this->in_seq_no = -1; $this->in_seq_no = -1;
break; break;
case 'http': case 'http':
case 'https': case 'https':
$this->parsed = parse_url($ip); $this->parsed = parse_url($ip);
$this->sock = fsockopen(($this->protocol === 'https' ? 'tls' : 'tcp').'://'.$this->parsed['host'].':'.$port); $this->sock = new \Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname($this->protocol === 'https' ? 'tls' : 'tcp'));
stream_set_timeout($this->sock, $timeout); if (!$this->sock->connect($this->parsed['host'], $port)) {
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) {
throw new Exception("Connection: couldn't connect to socket."); throw new Exception("Connection: couldn't connect to socket.");
} }
if (!\danog\MadelineProto\Logger::$has_thread) {
$this->sock->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
}
$this->sock->setBlocking(true);
break; break;
case 'udp': case 'udp':
throw new Exception("Connection: This protocol isn't implemented yet."); throw new Exception("Connection: This protocol isn't implemented yet.");
@ -97,6 +120,9 @@ class Connection
public function __destruct() public function __destruct()
{ {
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
switch ($this->protocol) { switch ($this->protocol) {
case 'tcp_abridged': case 'tcp_abridged':
case 'tcp_intermediate': case 'tcp_intermediate':
@ -104,7 +130,7 @@ class Connection
case 'http': case 'http':
case 'https': case 'https':
try { try {
fclose($this->sock); unset($this->sock);
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
} }
break; break;
@ -119,12 +145,35 @@ class Connection
public function close_and_reopen() public function close_and_reopen()
{ {
$this->__destruct(); $this->__destruct();
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout); $this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function __sleep()
{
$t = get_object_vars($this);
if (isset($t['sock'])) {
unset($t['sock']);
}
$keys = array_keys((array) $t);
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
return $keys;
} }
public function __wakeup() public function __wakeup()
{ {
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout); if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
$keys = array_keys((array) get_object_vars($this));
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
//$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
} }
public function write($what, $length = null) public function write($what, $length = null)
@ -138,11 +187,10 @@ class Connection
case 'tcp_full': case 'tcp_full':
case 'http': case 'http':
case 'https': case 'https':
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) { $wrote = 0;
throw new Exception("Connection: couldn't connect to socket."); $len = strlen($what);
} if (($wrote += $this->sock->write($what)) !== $len) {
if (($wrote = fwrite($this->sock, $what)) !== strlen($what)) { while (($wrote += $this->sock->write(substr($what, $wrote))) !== $len);
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was written (should've written ".strlen($what).', wrote '.$wrote.')!');
} }
return $wrote; return $wrote;
@ -164,15 +212,16 @@ class Connection
case 'tcp_full': case 'tcp_full':
case 'http': case 'http':
case 'https': case 'https':
if (!(get_resource_type($this->sock) === 'file' || get_resource_type($this->sock) === 'stream')) { $packet = '';
throw new Exception("Connection: couldn't connect to socket."); while (strlen($packet) < $length) {
$packet .= $this->sock->read($length - strlen($packet));
if ($packet === false || strlen($packet) === 0) {
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!');
}
} }
$packet = stream_get_contents($this->sock, $length); if (strlen($packet) !== $length) {
if ($packet === false || strlen($packet) === 0) { $this->close_and_reopen();
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!'); throw new Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
}
if (strlen($packet) != $length) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
} }
return $packet; return $packet;
@ -189,13 +238,14 @@ 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);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0]; $packet_length = unpack('V', $packet_length_data)[1];
$packet = $this->read($packet_length - 4); $packet = $this->read($packet_length - 4);
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) { if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!'); throw new Exception('CRC32 was not correct!');
} }
$this->in_seq_no++; $this->in_seq_no++;
$in_seq_no = \danog\PHP\Struct::unpack('<I', substr($packet, 0, 4))[0]; $in_seq_no = unpack('V', substr($packet, 0, 4))[1];
if ($in_seq_no != $this->in_seq_no) { if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch'); throw new Exception('Incoming seq_no mismatch');
} }
@ -203,7 +253,7 @@ class Connection
return substr($packet, 4, $packet_length - 12); return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate': case 'tcp_intermediate':
$packet_length_data = $this->read(4); $packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0]; $packet_length = unpack('V', $packet_length_data)[1];
return $this->read($packet_length); return $this->read($packet_length);
case 'tcp_abridged': case 'tcp_abridged':
@ -213,7 +263,7 @@ class Connection
$packet_length <<= 2; $packet_length <<= 2;
} else { } else {
$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 = unpack('V', $packet_length_data."\0")[1] << 2;
} }
return $this->read($packet_length); return $this->read($packet_length);
@ -234,7 +284,7 @@ class Connection
throw new Exception('No data in the socket!'); throw new Exception('No data in the socket!');
} }
if (preg_match('|^Content-Length: |i', $current_header)) { if (preg_match('|^Content-Length: |i', $current_header)) {
$length = preg_replace('|Content-Length: |i', '', $current_header); $length = (int) preg_replace('|Content-Length: |i', '', $current_header);
} }
if (preg_match('|^Connection: close|i', $current_header)) { if (preg_match('|^Connection: close|i', $current_header)) {
$close = true; $close = true;
@ -260,19 +310,19 @@ class Connection
switch ($this->protocol) { switch ($this->protocol) {
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 = pack('VV', (strlen($message) + 12), $this->out_seq_no).$message;
$step2 = $step1.strrev(hash('crc32b', $step1, true)); $step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2); $this->write($step2);
break; break;
case 'tcp_intermediate': case 'tcp_intermediate':
$this->write(\danog\PHP\Struct::pack('<I', strlen($message)).$message); $this->write(pack('V', strlen($message)).$message);
break; break;
case 'tcp_abridged': case 'tcp_abridged':
$len = strlen($message) / 4; $len = strlen($message) / 4;
if ($len < 127) { if ($len < 127) {
$step1 = chr($len).$message; $step1 = chr($len).$message;
} else { } else {
$step1 = chr(127).substr(\danog\PHP\Struct::pack('<I', $len), 0, 3).$message; $step1 = chr(127).substr(pack('V', $len), 0, 3).$message;
} }
$this->write($step1); $this->write($step1);
break; break;

View File

@ -1,17 +0,0 @@
<?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 ConnectionSerializable extends VolatileSerializer
{
}

View File

@ -15,9 +15,10 @@ namespace danog\MadelineProto;
/** /**
* Manages datacenters. * Manages datacenters.
*/ */
class DataCenter class DataCenter extends \Volatile
{ {
use \danog\MadelineProto\Tools; use \danog\MadelineProto\Tools;
use \danog\Serializable;
public $sockets = []; public $sockets = [];
public $curdc = 0; public $curdc = 0;
@ -29,9 +30,8 @@ class DataCenter
return ['sockets', 'curdc', 'dclist', 'settings']; return ['sockets', 'curdc', 'dclist', 'settings'];
} }
public function __construct($dclist, $settings) public function ___construct($dclist, $settings)
{ {
//if ($this->unserialized($dclist)) return true;
$this->dclist = $dclist; $this->dclist = $dclist;
$this->settings = $settings; $this->settings = $settings;
foreach ($this->sockets as $socket) { foreach ($this->sockets as $socket) {
@ -63,7 +63,7 @@ class DataCenter
$address = $this->settings[$dc_config_number]['ipv6'] ? '['.$address.']' : $address; $address = $this->settings[$dc_config_number]['ipv6'] ? '['.$address.']' : $address;
$port = $this->dclist[$test][$ipv6][$dc_number]['port']; $port = $this->dclist[$test][$ipv6][$dc_number]['port'];
if ($this->settings[$dc_config_number]['protocol'] === 'https') { if ($this->settings[$dc_config_number]['protocol'] === 'https') {
$subdomain = $this->dclist['ssl_subdomains'][$dc_config_number]; $subdomain = $this->dclist['ssl_subdomains'][$dc_number];
$path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1'; $path = $this->settings[$dc_config_number]['test_mode'] ? 'apiw_test1' : 'apiw1';
$address = $this->settings[$dc_config_number]['protocol'].'://'.$subdomain.'.web.telegram.org/'.$path; $address = $this->settings[$dc_config_number]['protocol'].'://'.$subdomain.'.web.telegram.org/'.$path;
} }
@ -74,17 +74,17 @@ class DataCenter
} }
\danog\MadelineProto\Logger::log(['Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.', '.$this->settings[$dc_config_number]['protocol'].')...'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Connecting to DC '.$dc_number.' ('.$test.' server, '.$ipv6.', '.$this->settings[$dc_config_number]['protocol'].')...'], \danog\MadelineProto\Logger::VERBOSE);
$this->sockets[$dc_number] = new Connection($address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout']); $this->sockets[$dc_number] = new Connection($address, $port, $this->settings[$dc_config_number]['protocol'], $this->settings[$dc_config_number]['timeout'], $this->settings[$dc_config_number]['ipv6']);
return true; return true;
} }
public function get_dcs() public function get_dcs($all = true)
{ {
$test = $this->settings['all']['test_mode'] ? 'test' : 'main'; $test = $this->settings['all']['test_mode'] ? 'test' : 'main';
$ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4'; $ipv6 = $this->settings['all']['ipv6'] ? 'ipv6' : 'ipv4';
return array_keys($this->dclist[$test][$ipv6]); return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
} }
/* /*

View File

@ -1,17 +0,0 @@
<?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 DataCenterSerializable extends VolatileSerializer
{
}

View File

@ -15,6 +15,7 @@ namespace danog\MadelineProto;
class DocsBuilder class DocsBuilder
{ {
use \danog\MadelineProto\TL\TL; use \danog\MadelineProto\TL\TL;
use Tools;
public function __construct($settings) public function __construct($settings)
{ {
@ -37,7 +38,7 @@ class DocsBuilder
public function mk_docs() public function mk_docs()
{ {
$types = []; $types = [];
// $any = '*'; $any = '*';
\danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Generating documentation index...'], \danog\MadelineProto\Logger::NOTICE);
file_put_contents($this->index, '--- file_put_contents($this->index, '---
@ -306,7 +307,7 @@ description: List of methods
'.implode('', $methods)); '.implode('', $methods));
foreach (glob('constructors/*') as $unlink) { foreach (glob('constructors/'.$any) as $unlink) {
unlink($unlink); unlink($unlink);
} }
@ -588,6 +589,29 @@ $'.$type.' = -147286699; // Numeric chat id returned by request_secret_chat, can
``` ```
';
}
if (in_array($type, ['KeyboardButton'])) {
$header .= 'Clicking these buttons:
To click these buttons simply run the `click` method:
```
$result = $'.$type.'->click();
```
`$result` can be one of the following:
* A string - If the button is a keyboardButtonUrl
* [Updates](Updates.md) - If the button is a keyboardButton, the message will be sent to the chat, in reply to the message with the keyboard
* [messages_BotCallbackAnswer](messages_BotCallbackAnswer.md) - If the button is a keyboardButtonCallback or a keyboardButtonGame the button will be pressed and the result will be returned
* `false` - If the button is an unsupported button, like keyboardButtonRequestPhone, keyboardButtonRequestGeoLocation, keyboardButtonSwitchInlinekeyboardButtonBuy; you will have to parse data from these buttons manually
'; ';
} }
$constructors = '### Possible values (constructors): $constructors = '### Possible values (constructors):

View File

@ -29,7 +29,7 @@ class Exception extends \Exception
if (in_array($message, ['Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden'])) { if (in_array($message, ['Re-executing query...', 'I had to recreate the temporary authorization key', 'This peer is not present in the internal peer database', "Couldn't get response", 'Chat forbidden'])) {
return; return;
} }
if (strpos($message, 'fwrite') !== false || strpos($message, 'Received request to switch to DC ') !== false || strpos($message, 'Re-executing query...') !== false || strpos($message, "Couldn't find peer by provided") !== false) { if (strpos($message, 'socket_write') !== false || strpos($message, 'socket_read') !== false || strpos($message, 'Received request to switch to DC ') !== false || strpos($message, 'Re-executing query...') !== false || strpos($message, "Couldn't find peer by provided") !== false || strpos($message, 'id.pwrtelegram.xyz') !== false) {
return; return;
} }
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0)); \Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $this, debug_backtrace(0));

View File

@ -17,11 +17,16 @@ namespace danog\MadelineProto;
class Logger class Logger
{ {
public static $storage = [];
public static $mode = null; public static $mode = null;
public static $optional = null; public static $optional = null;
public static $constructed = false; public static $constructed = false;
public static $prefix = ''; public static $prefix = '';
public static $level = 3; public static $level = 3;
public static $has_thread = false;
public static $BIG_ENDIAN = false;
public static $bigint = true;
const ULTRA_VERBOSE = 5; const ULTRA_VERBOSE = 5;
const VERBOSE = 4; const VERBOSE = 4;
const NOTICE = 3; const NOTICE = 3;
@ -29,6 +34,13 @@ class Logger
const ERROR = 1; const ERROR = 1;
const FATAL_ERROR = 0; const FATAL_ERROR = 0;
public static function class_exists()
{
self::$has_thread = class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread');
self::$BIG_ENDIAN = (pack('L', 1) === pack('N', 1));
self::$bigint = PHP_INT_SIZE < 8;
}
/* /*
* Constructor function * Constructor function
* Accepts various logger modes: * Accepts various logger modes:
@ -47,6 +59,7 @@ class Logger
self::$constructed = true; self::$constructed = true;
self::$prefix = $prefix === '' ? '' : ', '.$prefix; self::$prefix = $prefix === '' ? '' : ', '.$prefix;
self::$level = $level; self::$level = $level;
self::class_exists();
} }
public static function log($params, $level = self::NOTICE) public static function log($params, $level = self::NOTICE)
@ -58,12 +71,12 @@ class Logger
return false; return false;
} }
$prefix = self::$prefix; $prefix = self::$prefix;
if (class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread') && is_object(\Thread::getCurrentThread())) { if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
$prefix .= ' (t)'; $prefix .= ' (t)';
} }
foreach (is_array($params) ? $params : [$params] as $param) { foreach (is_array($params) ? $params : [$params] as $param) {
if (!is_string($param)) { if (!is_string($param)) {
$param = var_export($param, true); $param = json_encode($param, JSON_PRETTY_PRINT);
} }
$param = str_pad(basename(debug_backtrace()[0]['file'], '.php').$prefix.': ', 16 + strlen($prefix))."\t".$param; $param = str_pad(basename(debug_backtrace()[0]['file'], '.php').$prefix.': ', 16 + strlen($prefix))."\t".$param;
switch (self::$mode) { switch (self::$mode) {

View File

@ -14,11 +14,12 @@ namespace danog\MadelineProto;
class Lua class Lua
{ {
use \danog\Serializable;
public $MadelineProto; public $MadelineProto;
private $Lua; protected $Lua;
private $script; protected $script;
public function __construct($script, $MadelineProto) public function ___construct($script, $MadelineProto)
{ {
if (!file_exists($script)) { if (!file_exists($script)) {
throw new Exception('Provided script does not exist'); throw new Exception('Provided script does not exist');

View File

@ -15,8 +15,9 @@ namespace danog\MadelineProto;
/** /**
* Manages all of the mtproto stuff. * Manages all of the mtproto stuff.
*/ */
class MTProto class MTProto extends \Volatile
{ {
use \danog\Serializable;
use \danog\MadelineProto\MTProtoTools\AckHandler; use \danog\MadelineProto\MTProtoTools\AckHandler;
use \danog\MadelineProto\MTProtoTools\AuthKeyHandler; use \danog\MadelineProto\MTProtoTools\AuthKeyHandler;
use \danog\MadelineProto\MTProtoTools\CallHandler; use \danog\MadelineProto\MTProtoTools\CallHandler;
@ -51,11 +52,12 @@ class MTProto
public $bigint = false; public $bigint = false;
public $run_workers = false; public $run_workers = false;
public $threads = false; public $threads = false;
public $rsa_keys = [];
public function __construct($settings = []) public function ___construct($settings = [])
{ {
//if ($this->unserialized($settings)) return true; //if ($this->unserialized($settings)) return true;
$this->bigint = PHP_INT_SIZE < 8; \danog\MadelineProto\Logger::class_exists();
// Parse settings // Parse settings
$this->parse_settings($settings); $this->parse_settings($settings);
@ -68,8 +70,10 @@ class MTProto
} }
// Load rsa key // Load rsa key
\danog\MadelineProto\Logger::log(['Loading RSA key...'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Loading RSA key...'], Logger::ULTRA_VERBOSE);
$key = new RSA($this->settings['authorization']['rsa_key']); foreach ($this->settings['authorization']['rsa_keys'] as $key) {
$this->rsa_keys[$key->fp] = $key; $key = new RSA($key);
$this->rsa_keys[$key->fp] = $key;
}
// Istantiate TL class // Istantiate TL class
\danog\MadelineProto\Logger::log(['Translating tl schemas...'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Translating tl schemas...'], Logger::ULTRA_VERBOSE);
@ -89,8 +93,6 @@ class MTProto
$this->twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328'); $this->twoe2047 = new \phpseclib\Math\BigInteger('16158503035655503650357438344334975980222051334857742016065172713762327569433945446598600705761456731844358980460949009747059779575245460547544076193224141560315438683650498045875098875194826053398028819192033784138396109321309878080919047169238085235290822926018152521443787945770532904303776199561965192760957166694834171210342487393282284747428088017663161029038902829665513096354230157075129296432088558362971801859230928678799175576150822952201848806616643615613562842355410104862578550863465661734839271290328348967522998634176499319107762583194718667771801067716614802322659239302476074096777926805529798115328');
$this->twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656'); $this->twoe2048 = new \phpseclib\Math\BigInteger('32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656');
$this->setup_threads();
$this->connect_to_all_dcs(); $this->connect_to_all_dcs();
$this->datacenter->curdc = 2; $this->datacenter->curdc = 2;
@ -112,23 +114,30 @@ class MTProto
public function __sleep() public function __sleep()
{ {
$t = get_object_vars($this); $t = get_object_vars($this);
if (isset($t['reader_pool'])) {
unset($t['reader_pool']);
}
if (isset($t['readers'])) { if (isset($t['readers'])) {
unset($t['readers']); unset($t['readers']);
} }
return array_keys($t); $keys = array_keys((array) $t);
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
return $keys;
} }
public function __wakeup() public function __wakeup()
{ {
set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']); set_error_handler(['\danog\MadelineProto\Exception', 'ExceptionErrorHandler']);
$this->setup_logger(); $this->setup_logger();
if (class_exists('\Thread') && method_exists('\Thread', 'getCurrentThread') && is_object(\Thread::getCurrentThread())) { if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return; return;
} }
$keys = array_keys((array) get_object_vars($this));
if (count($keys) !== count(array_unique($keys))) {
throw new Bug74586Exception();
}
/* /*
if (method_exists($this->datacenter, 'wakeup')) $this->datacenter = $this->datacenter->wakeup(); if (method_exists($this->datacenter, 'wakeup')) $this->datacenter = $this->datacenter->wakeup();
foreach ($this->rsa_keys as $key => $elem) { foreach ($this->rsa_keys as $key => $elem) {
@ -139,7 +148,6 @@ class MTProto
} }
*/ */
$this->getting_state = false; $this->getting_state = false;
$this->bigint = PHP_INT_SIZE < 8;
$this->reset_session(); $this->reset_session();
if (!isset($this->v) || $this->v !== $this->getV()) { if (!isset($this->v) || $this->v !== $this->getV()) {
\danog\MadelineProto\Logger::log(['Serialization is out of date, reconstructing object!'], Logger::WARNING); \danog\MadelineProto\Logger::log(['Serialization is out of date, reconstructing object!'], Logger::WARNING);
@ -148,6 +156,9 @@ class MTProto
$settings['updates']['callback'] = 'get_updates_update_handler'; $settings['updates']['callback'] = 'get_updates_update_handler';
} }
unset($settings['tl_schema']); unset($settings['tl_schema']);
if (isset($settings['authorization']['rsa_key'])) {
unset($settings['authorization']['rsa_key']);
}
$this->reset_session(true, true); $this->reset_session(true, true);
$this->__construct($settings); $this->__construct($settings);
} }
@ -161,12 +172,15 @@ class MTProto
public function __destruct() public function __destruct()
{ {
if (isset($this->reader_pool)) { if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
return;
}
if (isset(Logger::$storage[spl_object_hash($this)])) {
$this->run_workers = false; $this->run_workers = false;
while ($number = $this->reader_pool->collect()) { while ($number = Logger::$storage[spl_object_hash($this)]->collect()) {
\danog\MadelineProto\Logger::log(['Shutting down reader pool, '.$number.' jobs left'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Shutting down reader pool, '.$number.' jobs left'], \danog\MadelineProto\Logger::NOTICE);
} }
$this->reader_pool->shutdown(); Logger::$storage[spl_object_hash($this)]->shutdown();
} }
} }
@ -180,28 +194,33 @@ class MTProto
public function start_threads() public function start_threads()
{ {
if ($this->threads) { if ($this->threads && !is_object(\Thread::getCurrentThread())) {
$dcs = $this->datacenter->get_dcs(); $dcs = $this->datacenter->get_dcs(false);
if (!isset($this->reader_pool)) { if (!isset(Logger::$storage[spl_object_hash($this)])) {
$this->reader_pool = new \Pool(count($dcs)); Logger::$storage[spl_object_hash($this)] = new \Pool(count($dcs));
} }
if (!isset($this->readers)) { if (!isset($this->readers)) {
$this->readers = []; $this->readers = [];
} }
foreach ($dcs as $dc) { foreach ($dcs as $dc) {
if (!isset($this->readers[$dc])) { if (!isset($this->readers[$dc])) {
Logger::log(['Socket reader on DC '.$dc.': CREATING'], Logger::WARNING);
$this->readers[$dc] = new \danog\MadelineProto\Threads\SocketReader($this, $dc); $this->readers[$dc] = new \danog\MadelineProto\Threads\SocketReader($this, $dc);
} }
if (!$this->readers[$dc]->isRunning()) { if (!$this->readers[$dc]->isRunning()) {
Logger::log(['Socket reader on DC '.$dc.': SUBMITTING'], Logger::WARNING);
$this->readers[$dc]->garbage = false; $this->readers[$dc]->garbage = false;
$this->reader_pool->submit($this->readers[$dc]); Logger::$storage[spl_object_hash($this)]->submit($this->readers[$dc]);
Logger::log(['Socket reader on DC '.$dc.': RESTARTED'], Logger::WARNING); Logger::log(['Socket reader on DC '.$dc.': WAITING'], Logger::WARNING);
while (!$this->readers[$dc]->ready); while (!$this->readers[$dc]->ready);
Logger::log(['Socket reader on DC '.$dc.': READY'], Logger::WARNING);
} else { } else {
Logger::log(['Socket reader on DC '.$dc.': WORKING'], Logger::NOTICE); Logger::log(['Socket reader on DC '.$dc.': WORKING'], Logger::ULTRA_VERBOSE);
} }
} }
} }
return true;
} }
public function parse_settings($settings) public function parse_settings($settings)
@ -232,14 +251,12 @@ class MTProto
$default_settings = [ $default_settings = [
'authorization' => [ // Authorization settings 'authorization' => [ // Authorization settings
'default_temp_auth_key_expires_in' => 31557600, // validity of temporary keys and the binding of the temporary and permanent keys 'default_temp_auth_key_expires_in' => 31557600, // validity of temporary keys and the binding of the temporary and permanent keys
'rsa_key' => '-----BEGIN RSA PUBLIC KEY----- 'rsa_keys' => [
MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6\nlyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS\nan9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw\nEfzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+\n8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n\nSlv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB\n-----END RSA PUBLIC KEY-----",
lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAxq7aeLAqJR20tkQQMfRn+ocfrtMlJsQ2Uksfs7Xcoo77jAid0bRt\nksiVmT2HEIJUlRxfABoPBV8wY9zRTUMaMA654pUX41mhyVN+XoerGxFvrs9dF1Ru\nvCHbI02dM2ppPvyytvvMoefRoL5BTcpAihFgm5xCaakgsJ/tH5oVl74CdhQw8J5L\nxI/K++KJBUyZ26Uba1632cOiq05JBUW0Z2vWIOk4BLysk7+U9z+SxynKiZR3/xdi\nXvFKk01R3BHV+GUKM2RYazpS/P8v7eyKhAbKxOdRcFpHLlVwfjyM1VlDQrEZxsMp\nNTLYXb6Sce1Uov0YtNx5wEowlREH1WOTlwIDAQAB\n-----END RSA PUBLIC KEY-----",
an9tqw3bfUV/nqgbhGX81v/+7RFAEd+RwFnK7a+XYl9sluzHRyVVaTTveB2GazTw "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAsQZnSWVZNfClk29RcDTJQ76n8zZaiTGuUsi8sUhW8AS4PSbPKDm+\nDyJgdHDWdIF3HBzl7DHeFrILuqTs0vfS7Pa2NW8nUBwiaYQmPtwEa4n7bTmBVGsB\n1700/tz8wQWOLUlL2nMv+BPlDhxq4kmJCyJfgrIrHlX8sGPcPA4Y6Rwo0MSqYn3s\ng1Pu5gOKlaT9HKmE6wn5Sut6IiBjWozrRQ6n5h2RXNtO7O2qCDqjgB2vBxhV7B+z\nhRbLbCmW0tYMDsvPpX5M8fsO05svN+lKtCAuz1leFns8piZpptpSCFn7bWxiA9/f\nx5x17D7pfah3Sy2pA+NDXyzSlGcKdaUmwQIDAQAB\n-----END RSA PUBLIC KEY-----",
Efzk2DWgkBluml8OREmvfraX3bkHZJTKX4EQSjBbbdJ2ZXIsRrYOXfaA+xayEGB+ "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAwqjFW0pi4reKGbkc9pK83Eunwj/k0G8ZTioMMPbZmW99GivMibwa\nxDM9RDWabEMyUtGoQC2ZcDeLWRK3W8jMP6dnEKAlvLkDLfC4fXYHzFO5KHEqF06i\nqAqBdmI1iBGdQv/OQCBcbXIWCGDY2AsiqLhlGQfPOI7/vvKc188rTriocgUtoTUc\n/n/sIUzkgwTqRyvWYynWARWzQg0I9olLBBC2q5RQJJlnYXZwyTL3y9tdb7zOHkks\nWV9IMQmZmyZh/N7sMbGWQpt4NMchGpPGeJ2e5gHBjDnlIf2p1yZOYeUYrdbwcS0t\nUiggS4UeE8TzIuXFQxw7fzEIlmhIaq3FnwIDAQAB\n-----END RSA PUBLIC KEY-----",
8hdlLmAjbCVfaigxX0CDqWeR1yFL9kwd9P0NsZRPsmoqVwMbMu7mStFai6aIhc3n ], // RSA public keys
Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
-----END RSA PUBLIC KEY-----', // RSA public key
], ],
'connection' => [ // List of datacenters/subdomains where to connect 'connection' => [ // List of datacenters/subdomains where to connect
'ssl_subdomains' => [ // Subdomains of web.telegram.org for https protocol 'ssl_subdomains' => [ // Subdomains of web.telegram.org for https protocol
@ -310,7 +327,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
'telegram' => __DIR__.'/TL_telegram_v66.tl', // telegram TL scheme 'telegram' => __DIR__.'/TL_telegram_v66.tl', // telegram TL scheme
'secret' => __DIR__.'/TL_secret.tl', // secret chats TL scheme 'secret' => __DIR__.'/TL_secret.tl', // secret chats TL scheme
'calls' => __DIR__.'/TL_calls.tl', // calls TL scheme 'calls' => __DIR__.'/TL_calls.tl', // calls TL scheme
'td' => __DIR__.'/TL_td.tl', // telegram-cli TL scheme //'td' => __DIR__.'/TL_td.tl', // telegram-cli TL scheme
'botAPI' => __DIR__.'/TL_botAPI.tl', // bot API TL scheme for file ids 'botAPI' => __DIR__.'/TL_botAPI.tl', // bot API TL scheme for file ids
], ],
], ],
@ -326,6 +343,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
'logger_param' => '/tmp/MadelineProto.log', 'logger_param' => '/tmp/MadelineProto.log',
'logger' => 3, // overwrite previous setting and echo logs 'logger' => 3, // overwrite previous setting and echo logs
'logger_level' => Logger::VERBOSE, // Logging level, available logging levels are: ULTRA_VERBOSE, VERBOSE, NOTICE, WARNING, ERROR, FATAL_ERROR. Can be provided as last parameter to the logging function. 'logger_level' => Logger::VERBOSE, // Logging level, available logging levels are: ULTRA_VERBOSE, VERBOSE, NOTICE, WARNING, ERROR, FATAL_ERROR. Can be provided as last parameter to the logging function.
'rollbar_token' => 'f9fff6689aea4905b58eec73f66c791d',
//'rollbar_token' => 'f9fff6689aea4905b58eec73f66c791d' // You can provide a token for the rollbar log management system //'rollbar_token' => 'f9fff6689aea4905b58eec73f66c791d' // You can provide a token for the rollbar log management system
], ],
'max_tries' => [ 'max_tries' => [
@ -384,7 +402,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function setup_logger() public function setup_logger()
{ {
\Rollbar\Rollbar::init(['environment' => 'production', 'root' => __DIR__, 'access_token' => (isset($this->settings['logger']['rollbar_token']) && !in_array($this->settings['logger']['rollbar_token'], ['f9fff6689aea4905b58eec73f66c791d'])) ? $this->settings['logger']['rollbar_token'] : '300afd7ccef346ea84d0c185ae831718'], false, false); \Rollbar\Rollbar::init(['environment' => 'production', 'root' => __DIR__, 'access_token' => (isset($this->settings['logger']['rollbar_token']) && !in_array($this->settings['logger']['rollbar_token'], ['f9fff6689aea4905b58eec73f66c791d', '300afd7ccef346ea84d0c185ae831718'])) ? $this->settings['logger']['rollbar_token'] : '11a8c2fe4c474328b40a28193f8d63f5'], false, false);
\danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param'], isset($this->authorization['user']) ? (isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id']) : '', isset($this->settings['logger']['logger_level']) ? $this->settings['logger']['logger_level'] : Logger::VERBOSE); \danog\MadelineProto\Logger::constructor($this->settings['logger']['logger'], $this->settings['logger']['logger_param'], isset($this->authorization['user']) ? (isset($this->authorization['user']['username']) ? $this->authorization['user']['username'] : $this->authorization['user']['id']) : '', isset($this->settings['logger']['logger_level']) ? $this->settings['logger']['logger_level'] : Logger::VERBOSE);
} }
@ -416,13 +434,14 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
$this->datacenter->dc_connect($new_dc); $this->datacenter->dc_connect($new_dc);
} }
} }
$this->setup_threads();
$this->init_authorization(); $this->init_authorization();
if ($old !== $this->datacenter->get_dcs()) { if ($old !== $this->datacenter->get_dcs()) {
$this->connect_to_all_dcs(); $this->connect_to_all_dcs();
} }
} }
private $initing_authorization = false; protected $initing_authorization = false;
// Creates authorization keys // Creates authorization keys
public function init_authorization() public function init_authorization()
@ -466,7 +485,8 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
if ($int_dc != $new_dc) { if ($int_dc != $new_dc) {
continue; continue;
} }
if (preg_match('|media|', $new_dc)) { \danog\MadelineProto\Logger::log([$int_dc, $new_dc]);
if (preg_match('|_|', $new_dc)) {
continue; continue;
} }
\danog\MadelineProto\Logger::log(['Copying authorization from dc '.$authorized_dc.' to dc '.$new_dc.'...'], Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Copying authorization from dc '.$authorized_dc.' to dc '.$new_dc.'...'], Logger::VERBOSE);
@ -538,7 +558,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
public function getV() public function getV()
{ {
return 24; return 29;
} }
public function get_self() public function get_self()

View File

@ -1,17 +0,0 @@
<?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 MTProtoSerializable extends VolatileSerializer
{
}

View File

@ -37,7 +37,7 @@ trait AckHandler
\danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(["WARNING: Couldn't find message id ".$message_id.' in the array of incomgoing messages. Maybe try to increase its size?'], \danog\MadelineProto\Logger::WARNING);
//throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?'); //throw new \danog\MadelineProto\Exception("Couldn't find message id ".$message_id.' in the array of incoming message ids. Maybe try to increase its size?');
} }
if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === str_repeat(chr(0), 8) || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) { if ($this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === null || $this->datacenter->sockets[$datacenter]->temp_auth_key['id'] === "\0\0\0\0\0\0\0\0" || (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack']) && $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['ack'])) {
return; return;
} }

View File

@ -79,7 +79,7 @@ trait AuthKeyHandler
*/ */
foreach ($this->rsa_keys as $fp => $curkey) { foreach ($this->rsa_keys as $fp => $curkey) {
if (in_array($fp, $ResPQ['server_public_key_fingerprints'])) { if ($this->in_array($fp, $ResPQ['server_public_key_fingerprints'])) {
$key = $curkey; $key = $curkey;
} }
} }
@ -546,7 +546,7 @@ trait AuthKeyHandler
$message_id = $this->generate_message_id($datacenter); $message_id = $this->generate_message_id($datacenter);
$seq_no = 0; $seq_no = 0;
$encrypted_data = $this->random(16).$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data; $encrypted_data = $this->random(16).$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16); $message_key = substr(sha1($encrypted_data, true), -16);
$padding = $this->random($this->posmod(-strlen($encrypted_data), 16)); $padding = $this->random($this->posmod(-strlen($encrypted_data), 16));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']); list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key']);

View File

@ -34,7 +34,7 @@ trait CallHandler
} }
$args = $this->botAPI_to_MTProto($args); $args = $this->botAPI_to_MTProto($args);
if (isset($args['ping_id']) && is_int($args['ping_id'])) { if (isset($args['ping_id']) && is_int($args['ping_id'])) {
$args['ping_id'] = \danog\PHP\Struct::pack('<q', $args['ping_id']); $args['ping_id'] = $this->pack_signed_long($args['ping_id']);
} }
if (isset($args['chat_id']) && !isset($args['peer']) && $method !== 'messages.discardEncryption' && (is_object($args['chat_id']) || $args['chat_id'] < 0)) { if (isset($args['chat_id']) && !isset($args['peer']) && $method !== 'messages.discardEncryption' && (is_object($args['chat_id']) || $args['chat_id'] < 0)) {
$res = $this->get_info($args['chat_id']); $res = $this->get_info($args['chat_id']);
@ -54,12 +54,12 @@ trait CallHandler
try { try {
\danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Calling method (try number '.$count.' for '.$method.')...'], \danog\MadelineProto\Logger::VERBOSE);
$int_message_id = $this->send_message($serialized, $content_related, $aargs); $message_id = $this->send_message($serialized, $content_related, $aargs);
if ($method === 'http_wait' || (isset($aargs['noResponse']) && $aargs['noResponse'])) { if ($method === 'http_wait' || (isset($aargs['noResponse']) && $aargs['noResponse'])) {
return true; return true;
} }
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['content'] = ['method' => $method, 'args' => $args]; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['content'] = ['method' => $method, 'args' => $args];
$this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id] = ['msg_id' => $int_message_id, 'method' => $method, 'type' => $type]; $this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id] = ['msg_id' => $message_id, 'method' => $method, 'type' => $type];
$res_count = 0; $res_count = 0;
$server_answer = null; $server_answer = null;
$update_count = 0; $update_count = 0;
@ -68,8 +68,8 @@ trait CallHandler
try { try {
\danog\MadelineProto\Logger::log(['Getting response (try number '.$res_count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Getting response (try number '.$res_count.' for '.$method.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
//sleep(2); //sleep(2);
//$this->start_threads(); $this->start_threads();
if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping if (!isset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']) || !isset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'])) { // Checks if I have received the response to the called method, if not continue looping
if ($only_updates) { if ($only_updates) {
if ($update_count > 50) { if ($update_count > 50) {
$update_count = 0; $update_count = 0;
@ -79,13 +79,18 @@ trait CallHandler
} }
} }
} else { } else {
$server_answer = $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content']; //var_dump(base64_encode($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']), $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]);
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']]['content'] = []; $server_answer = (array) $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'];
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']]['content'] = [];
break; break;
} }
if (!$this->threads && !$this->run_workers) { if (!$this->threads && !$this->run_workers) {
$this->recv_message($aargs['datacenter']); // This method receives data from the socket, and parses stuff $this->recv_message($aargs['datacenter']); // This method receives data from the socket, and parses stuff
$only_updates = $this->handle_messages($aargs['datacenter']); // This method receives data from the socket, and parses stuff $only_updates = $this->handle_messages($aargs['datacenter']); // This method receives data from the socket, and parses stuff
} else {
$res_count--;
//var_dump($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
sleep(1);
} }
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
if ($e->getMessage() === 'I had to recreate the temporary authorization key') { if ($e->getMessage() === 'I had to recreate the temporary authorization key') {
@ -122,7 +127,7 @@ trait CallHandler
case 16: case 16:
case 17: case 17:
\danog\MadelineProto\Logger::log(['Received bad_msg_notification: '.$this->bad_msg_error_codes[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['Received bad_msg_notification: '.$this->bad_msg_error_codes[$server_answer['error_code']]], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$aargs['datacenter']]->timedelta = (int) ((new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['response']), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString()); $this->datacenter->sockets[$aargs['datacenter']]->timedelta = (int) ((new \phpseclib\Math\BigInteger(strrev($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response']), 256))->bitwise_rightShift(32)->subtract(new \phpseclib\Math\BigInteger(time()))->toString());
\danog\MadelineProto\Logger::log(['Set time delta to '.$this->datacenter->sockets[$aargs['datacenter']]->timedelta], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['Set time delta to '.$this->datacenter->sockets[$aargs['datacenter']]->timedelta], \danog\MadelineProto\Logger::WARNING);
$this->reset_session(); $this->reset_session();
$this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null; $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
@ -142,7 +147,7 @@ trait CallHandler
} catch (\danog\MadelineProto\Exception $e) { } catch (\danog\MadelineProto\Exception $e) {
$last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine(); $last_error = $e->getMessage().' in '.basename($e->getFile(), '.php').' on line '.$e->getLine();
\danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['An error occurred while calling method '.$method.': '.$last_error.'. Recreating connection and retrying to call method...'], \danog\MadelineProto\Logger::WARNING);
if (in_array($this->datacenter->sockets[$aargs['datacenter']]->protocol, ['http', 'https']) && $method !== 'http_wait') { if ($this->in_array($this->datacenter->sockets[$aargs['datacenter']]->protocol, ['http', 'https']) && $method !== 'http_wait') {
//$this->method_call('http_wait', ['max_wait' => $this->datacenter->sockets[$aargs['datacenter']]->timeout, 'wait_after' => 0, 'max_delay' => 0], ['datacenter' => $aargs['datacenter']]); //$this->method_call('http_wait', ['max_wait' => $this->datacenter->sockets[$aargs['datacenter']]->timeout, 'wait_after' => 0, 'max_delay' => 0], ['datacenter' => $aargs['datacenter']]);
} else { } else {
$this->datacenter->sockets[$aargs['datacenter']]->close_and_reopen(); $this->datacenter->sockets[$aargs['datacenter']]->close_and_reopen();
@ -150,14 +155,14 @@ trait CallHandler
//sleep(1); // To avoid flooding //sleep(1); // To avoid flooding
continue; continue;
} finally { } finally {
if (isset($aargs['heavy']) && $aargs['heavy'] && isset($int_message_id)) { if (isset($aargs['heavy']) && $aargs['heavy'] && isset($message_id)) {
//$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['args'] = []; //$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['args'] = [];
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id] = []; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id] = [];
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id]); unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
} }
if (isset($int_message_id) && $method === 'req_pq') { if (isset($message_id) && $method === 'req_pq') {
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]); unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]);
unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$int_message_id]); unset($this->datacenter->sockets[$aargs['datacenter']]->new_outgoing[$message_id]);
} }
if ($canunset) { if ($canunset) {
$this->getting_state = false; $this->getting_state = false;
@ -166,9 +171,9 @@ trait CallHandler
} }
if ($server_answer === null) { if ($server_answer === null) {
if ($last_recv === $this->last_recv && $this->datacenter->sockets[$args['datacenter']]->temp_auth_key !== null) { if ($last_recv === $this->last_recv && $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
$this->datacenter->sockets[$args['datacenter']]->temp_auth_key = null; $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key = null;
$this->init_authorization(); $this->init_authorization();
return $this->method_call($method, $args, $aargs); return $this->method_call($method, $args, $aargs);
@ -176,7 +181,7 @@ trait CallHandler
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').'); throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
} }
\danog\MadelineProto\Logger::log(['Got response for method '.$method.' @ try '.$count.' (response try '.$res_count.')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Got response for method '.$method.' @ try '.$count.' (response try '.$res_count.')'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id] = []; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id] = [];
if (isset($message_chunks) && count($message_chunks)) { if (isset($message_chunks) && count($message_chunks)) {
$server_answer = [$server_answer]; $server_answer = [$server_answer];
foreach ($message_chunks as $message) { foreach ($message_chunks as $message) {
@ -187,7 +192,9 @@ trait CallHandler
return $server_answer; return $server_answer;
} }
if ($method === 'req_pq') {
throw new \danog\MadelineProto\RPCErrorException('RPC_CALL_FAIL');
}
throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').'); throw new \danog\MadelineProto\Exception('An error occurred while calling method '.$method.' ('.$last_error.').');
} }
@ -202,9 +209,9 @@ trait CallHandler
for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) { for ($count = 1; $count <= $this->settings['max_tries']['query']; $count++) {
try { try {
\danog\MadelineProto\Logger::log([$object === 'msgs_ack' ? 'ack '.$args['msg_ids'][0] : 'Sending object (try number '.$count.' for '.$object.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log([$object === 'msgs_ack' ? 'ack '.$args['msg_ids'][0] : 'Sending object (try number '.$count.' for '.$object.')...'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$int_message_id = $this->send_message($this->serialize_object(['type' => $object], $args), $this->content_related($object), $aargs); $message_id = $this->send_message($this->serialize_object(['type' => $object], $args), $this->content_related($object), $aargs);
if ($object !== 'msgs_ack') { if ($object !== 'msgs_ack') {
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$int_message_id]['content'] = ['method' => $object, 'args' => $args]; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['content'] = ['method' => $object, 'args' => $args];
} }
} catch (Exception $e) { } catch (Exception $e) {
\danog\MadelineProto\Logger::log(['An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['An error occurred while calling object '.$object.': '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine().'. Recreating connection and retrying to call object...'], \danog\MadelineProto\Logger::WARNING);
@ -212,7 +219,7 @@ trait CallHandler
continue; continue;
} }
return $int_message_id; return $message_id;
} }
throw new \danog\MadelineProto\Exception('An error occurred while sending object '.$object.'.'); throw new \danog\MadelineProto\Exception('An error occurred while sending object '.$object.'.');
} }

View File

@ -39,7 +39,7 @@ trait Crypt
public function ctr_encrypt($message, $key, $iv, $length) public function ctr_encrypt($message, $key, $iv, $length)
{ {
$cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_CTR); $cipher = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_CTR);
$iv .= \danog\PHP\Struct::pack('<i', $length); $iv .= $this->pack_signed_int($length);
$cipher->setKey($key); $cipher->setKey($key);
$cipher->setIV($iv); $cipher->setIV($iv);

View File

@ -27,7 +27,7 @@ trait Files
} }
$datacenter = is_null($datacenter) ? $this->datacenter->curdc : $datacenter; $datacenter = is_null($datacenter) ? $this->datacenter->curdc : $datacenter;
$file_size = filesize($file); $file_size = filesize($file);
if ($file_size > 1500 * 1024 * 1024) { if ($file_size > 1610612736) {
throw new \danog\MadelineProto\Exception('Given file is too big!'); throw new \danog\MadelineProto\Exception('Given file is too big!');
} }
if ($cb === null) { if ($cb === null) {
@ -47,7 +47,7 @@ trait Files
$key = $this->random(32); $key = $this->random(32);
$iv = $this->random(32); $iv = $this->random(32);
$digest = hash('md5', $key.$iv, true); $digest = hash('md5', $key.$iv, true);
$fingerprint = \danog\PHP\Struct::unpack('<i', substr($digest, 0, 4) ^ substr($digest, 4, 4))[0]; $fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
$ige = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_IGE); $ige = new \phpseclib\Crypt\AES(\phpseclib\Crypt\AES::MODE_IGE);
$ige->setIV($iv); $ige->setIV($iv);
$ige->setKey($key); $ige->setKey($key);
@ -61,7 +61,7 @@ trait Files
$bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0))); $bytes = $ige->encrypt(str_pad($bytes, $part_size, chr(0)));
} }
hash_update($ctx, $bytes); hash_update($ctx, $bytes);
if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => $datacenter])) { if (!$this->method_call($method, ['file_id' => $file_id, 'file_part' => $part_num++, 'file_total_parts' => $part_total_num, 'bytes' => $bytes], ['heavy' => true, 'datacenter' => &$datacenter])) {
throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num); throw new \danog\MadelineProto\Exception('An error occurred while uploading file part '.$part_num);
} }
$cb(ftell($f) * 100 / $file_size); $cb(ftell($f) * 100 / $file_size);
@ -254,7 +254,7 @@ trait Files
$datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc; $datacenter = isset($message_media['InputFileLocation']['dc_id']) ? $message_media['InputFileLocation']['dc_id'] : $this->datacenter->curdc;
if (isset($message_media['key'])) { if (isset($message_media['key'])) {
$digest = hash('md5', $message_media['key'].$message_media['iv'], true); $digest = hash('md5', $message_media['key'].$message_media['iv'], true);
$fingerprint = \danog\PHP\Struct::unpack('<i', substr($digest, 0, 4) ^ substr($digest, 4, 4))[0]; $fingerprint = $this->unpack_signed_int(substr($digest, 0, 4) ^ substr($digest, 4, 4));
if ($fingerprint !== $message_media['key_fingerprint']) { if ($fingerprint !== $message_media['key_fingerprint']) {
throw new \danog\MadelineProto\Exception('Fingerprint mismatch!'); throw new \danog\MadelineProto\Exception('Fingerprint mismatch!');
} }
@ -270,7 +270,7 @@ trait Files
$offset -= $start_at; $offset -= $start_at;
} }
try { try {
$res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]); $res = $cdn ? $this->method_call('upload.getCdnFile', ['file_token' => $message_media['file_token'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => $datacenter]) : $this->method_call('upload.getFile', ['location' => $message_media['InputFileLocation'], 'offset' => $offset, 'limit' => $part_size], ['heavy' => true, 'datacenter' => &$datacenter]);
} catch (\danog\MadelineProto\RPCErrorException $e) { } catch (\danog\MadelineProto\RPCErrorException $e) {
if ($e->rpc === 'OFFSET_INVALID') { if ($e->rpc === 'OFFSET_INVALID') {
\Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $e->rpc, ['info' => $message_media, 'offset' => $offset]); \Rollbar\Rollbar::log(\Rollbar\Payload\Level::error(), $e->rpc, ['info' => $message_media, 'offset' => $offset]);

View File

@ -21,7 +21,7 @@ trait MessageHandler
* Forming the message frame and sending message to server * Forming the message frame and sending message to server
* :param message: byte string to send. * :param message: byte string to send.
*/ */
private $last_recv = 0; protected $last_recv = 0;
public function send_message($message_data, $content_related, $aargs = []) public function send_message($message_data, $content_related, $aargs = [])
{ {
@ -33,22 +33,23 @@ trait MessageHandler
if (!is_string($message_id)) { if (!is_string($message_id)) {
throw new \danog\MadelineProto\Exception("Specified message id isn't a string"); throw new \danog\MadelineProto\Exception("Specified message id isn't a string");
} }
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.$message_id] = [];
if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key'] === null || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'] === null) { if ($this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key'] === null || $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'] === null) {
$message = str_repeat(chr(0), 8).$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data; $message = "\0\0\0\0\0\0\0\0".$message_id.$this->pack_unsigned_int(strlen($message_data)).$message_data;
} else { } else {
$seq_no = $this->generate_out_seq_no($aargs['datacenter'], $content_related); $seq_no = $this->generate_out_seq_no($aargs['datacenter'], $content_related);
$data2enc = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'].$this->datacenter->sockets[$aargs['datacenter']]->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data; $data2enc = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['server_salt'].$this->datacenter->sockets[$aargs['datacenter']]->session_id.$message_id.pack('VV', $seq_no, strlen($message_data)).$message_data;
$padding = $this->random($this->posmod(-strlen($data2enc), 16)); $padding = $this->random($this->posmod(-strlen($data2enc), 16));
$message_key = substr(sha1($data2enc, true), -16); $message_key = substr(sha1($data2enc, true), -16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key']); list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['auth_key']);
$message = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['id'].$message_key.$this->ige_encrypt($data2enc.$padding, $aes_key, $aes_iv); $message = $this->datacenter->sockets[$aargs['datacenter']]->temp_auth_key['id'].$message_key.$this->ige_encrypt($data2enc.$padding, $aes_key, $aes_iv);
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['seq_no'] = $seq_no; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.$message_id]['seq_no'] = $seq_no;
} }
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[$message_id]['response'] = -1; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.$message_id]['response'] = -1;
$this->datacenter->sockets[$aargs['datacenter']]->send_message($message); $this->datacenter->sockets[$aargs['datacenter']]->send_message($message);
return $message_id; return 'a'.$message_id;
} }
/** /**
@ -58,7 +59,7 @@ trait MessageHandler
{ {
$payload = $this->datacenter->sockets[$datacenter]->read_message(); $payload = $this->datacenter->sockets[$datacenter]->read_message();
if (strlen($payload) === 4) { if (strlen($payload) === 4) {
$error = \danog\PHP\Struct::unpack('<i', $payload)[0]; $error = $this->unpack_signed_int($payload);
if ($error === -404) { if ($error === -404) {
if ($this->datacenter->sockets[$datacenter]->temp_auth_key !== null) { if ($this->datacenter->sockets[$datacenter]->temp_auth_key !== null) {
\danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING); \danog\MadelineProto\Logger::log(['WARNING: Resetting auth key...'], \danog\MadelineProto\Logger::WARNING);
@ -70,11 +71,12 @@ trait MessageHandler
throw new \danog\MadelineProto\RPCErrorException($error, $error); throw new \danog\MadelineProto\RPCErrorException($error, $error);
} }
$auth_key_id = substr($payload, 0, 8); $auth_key_id = substr($payload, 0, 8);
if ($auth_key_id === str_repeat(chr(0), 8)) { if ($auth_key_id === "\0\0\0\0\0\0\0\0") {
$message_id = substr($payload, 8, 8); $message_id = 'a'.substr($payload, 8, 8);
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]); $this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
$message_length = \danog\PHP\Struct::unpack('<I', substr($payload, 16, 4))[0]; $message_length = unpack('V', substr($payload, 16, 4))[1];
$message_data = substr($payload, 20, $message_length); $message_data = substr($payload, 20, $message_length);
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = [];
} elseif ($auth_key_id === $this->datacenter->sockets[$datacenter]->temp_auth_key['id']) { } elseif ($auth_key_id === $this->datacenter->sockets[$datacenter]->temp_auth_key['id']) {
$message_key = substr($payload, 8, 16); $message_key = substr($payload, 8, 16);
$encrypted_data = substr($payload, 24); $encrypted_data = substr($payload, 24);
@ -91,13 +93,13 @@ trait MessageHandler
throw new \danog\MadelineProto\Exception('Session id mismatch.'); throw new \danog\MadelineProto\Exception('Session id mismatch.');
} }
$message_id = substr($decrypted_data, 16, 8); $message_id = 'a'.substr($decrypted_data, 16, 8);
$this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]); $this->check_message_id($message_id, ['outgoing' => false, 'datacenter' => $datacenter, 'container' => false]);
$seq_no = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 24, 4))[0]; $seq_no = unpack('V', substr($decrypted_data, 24, 4))[1];
// Dunno how to handle any incorrect sequence numbers // Dunno how to handle any incorrect sequence numbers
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 28, 4))[0]; $message_data_length = unpack('V', substr($decrypted_data, 28, 4))[1];
if ($message_data_length > strlen($decrypted_data)) { if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException('message_data_length is too big'); throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
@ -119,7 +121,7 @@ trait MessageHandler
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) { if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
throw new \danog\MadelineProto\SecurityException('msg_key mismatch'); throw new \danog\MadelineProto\SecurityException('msg_key mismatch');
} }
$this->datacenter->sockets[$datacenter]->incoming_messages[$message_id]['seq_no'] = $seq_no; $this->datacenter->sockets[$datacenter]->incoming_messages[$message_id] = ['seq_no' => $seq_no];
} else { } else {
throw new \danog\MadelineProto\SecurityException('Got unknown auth_key id'); throw new \danog\MadelineProto\SecurityException('Got unknown auth_key id');
} }

View File

@ -20,7 +20,7 @@ trait MsgIdHandler
public function check_message_id($new_message_id, $aargs) public function check_message_id($new_message_id, $aargs)
{ {
if (!is_object($new_message_id)) { if (!is_object($new_message_id)) {
$new_message_id = new \phpseclib\Math\BigInteger(strrev($new_message_id), 256); $new_message_id = new \phpseclib\Math\BigInteger(strrev(substr($new_message_id, 1)), 256);
} }
$min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32); $min_message_id = (new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$aargs['datacenter']]->time_delta - 300))->bitwise_leftShift(32);
if ($min_message_id->compare($new_message_id) > 0) { if ($min_message_id->compare($new_message_id) > 0) {
@ -34,7 +34,6 @@ trait MsgIdHandler
if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) { if (!$new_message_id->divide($this->four)[1]->equals($this->zero)) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4.'); throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is not divisible by 4.');
} }
$keys = array_keys($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
$key = $this->get_max_id($aargs['datacenter'], false); $key = $this->get_max_id($aargs['datacenter'], false);
if ($new_message_id->compare($key) <= 0) { if ($new_message_id->compare($key) <= 0) {
throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$key.').', 1); throw new \danog\MadelineProto\Exception('Given message id ('.$new_message_id.') is lower than or equal than the current limit ('.$key.').', 1);
@ -43,7 +42,7 @@ trait MsgIdHandler
reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages); reset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages);
unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[key($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages)]); unset($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[key($this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages)]);
} }
$this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages[strrev($new_message_id->toBytes())] = []; $this->datacenter->sockets[$aargs['datacenter']]->outgoing_messages['a'.strrev($new_message_id->toBytes())] = [];
} else { } else {
if (!$new_message_id->divide($this->four)[1]->equals($this->one) && !$new_message_id->divide($this->four)[1]->equals($this->three)) { if (!$new_message_id->divide($this->four)[1]->equals($this->one) && !$new_message_id->divide($this->four)[1]->equals($this->three)) {
throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3'); throw new \danog\MadelineProto\Exception('message id mod 4 != 1 or 3');
@ -63,7 +62,7 @@ trait MsgIdHandler
reset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages); reset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages);
unset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[key($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages)]); unset($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[key($this->datacenter->sockets[$aargs['datacenter']]->incoming_messages)]);
} }
$this->datacenter->sockets[$aargs['datacenter']]->incoming_messages[strrev($new_message_id->toBytes())] = []; $this->datacenter->sockets[$aargs['datacenter']]->incoming_messages['a'.strrev($new_message_id->toBytes())] = [];
} }
} }
@ -81,12 +80,12 @@ trait MsgIdHandler
public function get_max_id($datacenter, $incoming) public function get_max_id($datacenter, $incoming)
{ {
$keys = array_keys($this->datacenter->sockets[$datacenter]->{$incoming ? 'incoming_messages' : 'outgoing_messages'}); $keys = array_keys((array) $this->datacenter->sockets[$datacenter]->{$incoming ? 'incoming_messages' : 'outgoing_messages'});
if (empty($keys)) { if (empty($keys)) {
return $this->zero; return $this->zero;
} }
array_walk($keys, function (&$value, $key) { array_walk($keys, function (&$value, $key) {
$value = is_int($value) ? new \phpseclib\Math\BigInteger($value) : new \phpseclib\Math\BigInteger(strrev($value), 256); $value = new \phpseclib\Math\BigInteger(strrev(substr($value, 1)), 256);
}); });
return \phpseclib\Math\BigInteger::max(...$keys); return \phpseclib\Math\BigInteger::max(...$keys);

View File

@ -200,7 +200,7 @@ trait PeerHandler
if (is_numeric($id)) { if (is_numeric($id)) {
if (is_string($id)) { if (is_string($id)) {
$id = $this->bigint ? ((float) $id) : ((int) $id); $id = \danog\MadelineProto\Logger::$bigint ? ((float) $id) : ((int) $id);
} }
if (isset($this->chats[$id])) { if (isset($this->chats[$id])) {
return $this->gen_all($this->chats[$id]); return $this->gen_all($this->chats[$id]);
@ -212,15 +212,15 @@ trait PeerHandler
} }
} }
if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true) { if (!isset($this->settings['pwr']['requests']) || $this->settings['pwr']['requests'] === true) {
$dbres = json_decode(file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id='.$id, false, stream_context_create(['http'=> [ $dbres = json_decode(@file_get_contents('https://id.pwrtelegram.xyz/db/getusername?id='.$id, false, stream_context_create(['http'=> [
'timeout' => 2, 'timeout' => 2,
], ],
])), true); ])), true);
if ($dbres['ok']) { if (isset($dbres['ok']) && $dbres['ok']) {
return $this->get_info('@'.$dbres['result']); return $this->get_info('@'.$dbres['result']);
} }
} }
throw new \danog\MadelineProto\Exception("Couldn't find peer by provided chat id ".$id); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
$id = str_replace('@', '', $id); $id = str_replace('@', '', $id);
foreach ($this->chats as $chat) { foreach ($this->chats as $chat) {
@ -233,7 +233,7 @@ trait PeerHandler
return $this->get_info($id, false); return $this->get_info($id, false);
} }
throw new \danog\MadelineProto\Exception("Couldn't find peer by provided username ".$id); throw new \danog\MadelineProto\Exception('This peer is not present in the internal peer database');
} }
public function gen_all($constructor) public function gen_all($constructor)

View File

@ -17,8 +17,8 @@ namespace danog\MadelineProto\MTProtoTools;
*/ */
trait ResponseHandler trait ResponseHandler
{ {
private $pending_updates = []; protected $pending_updates = [];
private $bad_msg_error_codes = [ protected $bad_msg_error_codes = [
16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)', 16 => 'msg_id too low (most likely, client time is wrong; it would be worthwhile to synchronize it using msg_id notifications and re-send the original message with the “correct” msg_id or wrap it in a container with a new msg_id if the original message had waited too long on the client to be transmitted)',
17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)', 17 => 'msg_id too high (similar to the previous case, the client time has to be synchronized, and the message re-sent with the correct msg_id)',
18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)', 18 => 'incorrect two lower order msg_id bits (the server expects client message msg_id to be divisible by 4)',
@ -31,7 +31,7 @@ trait ResponseHandler
48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)', 48 => 'incorrect server salt (in this case, the bad_server_salt response is received with the correct salt, and the message is to be re-sent with it)',
64 => 'invalid container.', 64 => 'invalid container.',
]; ];
private $msgs_info_flags = [ protected $msgs_info_flags = [
1 => 'nothing is known about the message (msg_id too low, the other party may have forgotten it)', 1 => 'nothing is known about the message (msg_id too low, the other party may have forgotten it)',
2 => 'message not received (msg_id falls within the range of stored identifiers; however, the other party has certainly not received a message like that)', 2 => 'message not received (msg_id falls within the range of stored identifiers; however, the other party has certainly not received a message like that)',
3 => 'message not received (msg_id too high; however, the other party has certainly not received it yet)', 3 => 'message not received (msg_id too high; however, the other party has certainly not received it yet)',
@ -48,8 +48,8 @@ trait ResponseHandler
$info = ''; $info = '';
foreach ($msg_ids as $msg_id) { foreach ($msg_ids as $msg_id) {
$cur_info = 0; $cur_info = 0;
if (!in_array($msg_id, $this->datacenter->sockets[$datacenter]->incoming_messages)) { if (!isset($this->datacenter->sockets[$datacenter]->incoming_messages[$msg_id])) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256); $msg_id = new \phpseclib\Math\BigInteger(strrev(substr($msg_id, 1)), 256);
if ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) { if ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta + 30))->bitwise_leftShift(32)->compare($msg_id) < 0) {
$cur_info |= 3; $cur_info |= 3;
} elseif ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) { } elseif ((new \phpseclib\Math\BigInteger(time() + $this->datacenter->sockets[$datacenter]->time_delta - 300))->bitwise_leftShift(32)->compare($msg_id) > 0) {
@ -68,13 +68,31 @@ trait ResponseHandler
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id; $this->datacenter->sockets[$datacenter]->outgoing_messages[$this->object_call('msgs_state_info', ['req_msg_id' => $req_msg_id, 'info' => $info], ['datacenter' => $datacenter])]['response'] = $req_msg_id;
} }
public $stop = false;
public function handle_messages($datacenter) public function handle_messages($datacenter)
{ {
if ($this->stop) {
return;
}
$only_updates = true; $only_updates = true;
foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) { foreach ($this->datacenter->sockets[$datacenter]->new_incoming as $current_msg_id) {
$unset = false; $unset = false;
\danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Received '.$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
if (\danog\MadelineProto\Logger::$has_thread && is_object(\Thread::getCurrentThread())) {
if (!$this->synchronized(function ($zis, $datacenter, $current_msg_id) {
if (isset($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'])) {
return false;
}
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['handling'] = true;
return true;
}, $this, $datacenter, $current_msg_id) || $this->stop) {
\danog\MadelineProto\Logger::log([base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].' is already being handled'], \danog\MadelineProto\Logger::VERBOSE);
continue;
}
\danog\MadelineProto\Logger::log(['Handling '.base64_encode($current_msg_id).$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_'].'.'], \danog\MadelineProto\Logger::VERBOSE);
}
switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) { switch ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['_']) {
case 'msgs_ack': case 'msgs_ack':
$this->check_in_seq_no($datacenter, $current_msg_id); $this->check_in_seq_no($datacenter, $current_msg_id);
@ -88,10 +106,17 @@ trait ResponseHandler
case 'rpc_result': case 'rpc_result':
$this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response $this->ack_incoming_message_id($current_msg_id, $datacenter); // Acknowledge that I received the server's response
$this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request $this->ack_outgoing_message_id($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id'], $datacenter); // Acknowledge that the server received my request
$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]); unset($this->datacenter->sockets[$datacenter]->new_outgoing[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]);
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result']; $this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response'] = $current_msg_id;
//var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
$content = (array) $this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['result'];
$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content'] = $content;
///var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
///var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]);
///var_dump($this->datacenter->sockets[$datacenter]->incoming_messages[$this->datacenter->sockets[$datacenter]->outgoing_messages[$this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['req_msg_id']]['response']]['content']);
///$this->stop = true;
//var_dump(base64_encode($current_msg_id), $this->datacenter->sockets[$datacenter]->incoming_messages);
$this->check_in_seq_no($datacenter, $current_msg_id); $this->check_in_seq_no($datacenter, $current_msg_id);
$only_updates = false; $only_updates = false;
break; break;
@ -200,13 +225,13 @@ trait ResponseHandler
unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]); unset($this->datacenter->sockets[$datacenter]->new_incoming[$current_msg_id]);
foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) { foreach ($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['msg_ids'] as $key => $msg_id) {
$msg_id = new \phpseclib\Math\BigInteger(strrev($msg_id), 256); $msg_id = new \phpseclib\Math\BigInteger(strrev(substr($msg_id, 1)), 256);
$status = 'Status for message id '.$msg_id.': '; $status = 'Status for message id '.$msg_id.': ';
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) === 1) { if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & 4) !== 0) {
$this->ack_outgoing_message_id($msg_id, $datacenter); $this->ack_outgoing_message_id($msg_id, $datacenter);
} }
foreach ($this->msgs_info_flags as $flag => $description) { foreach ($this->msgs_info_flags as $flag => $description) {
if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & $flag) === 1) { if (($this->datacenter->sockets[$datacenter]->incoming_messages[$current_msg_id]['content']['info'][$key] & $flag) !== 0) {
$status .= $description; $status .= $description;
} }
} }
@ -319,41 +344,43 @@ trait ResponseHandler
return $only_updates; return $only_updates;
} }
public function handle_messages_threaded()
{
}
public function handle_rpc_error($server_answer, &$datacenter) public function handle_rpc_error($server_answer, &$datacenter)
{ {
switch ($server_answer['error_code']) { switch ($server_answer['error_code']) {
case 303: case 303:
$this->datacenter->curdc = $datacenter = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']); $this->datacenter->curdc = $datacenter = (int) preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc); throw new \danog\MadelineProto\Exception('Received request to switch to DC '.$this->datacenter->curdc);
case 401: case 401:
switch ($server_answer['error_message']) { switch ($server_answer['error_message']) {
case 'USER_DEACTIVATED': case 'USER_DEACTIVATED':
case 'SESSION_REVOKED': case 'SESSION_REVOKED':
case 'SESSION_EXPIRED': case 'SESSION_EXPIRED':
$this->datacenter->sockets[$datacenter]->temp_auth_key = null; $this->datacenter->sockets[$datacenter]->temp_auth_key = null;
$this->datacenter->sockets[$datacenter]->auth_key = null; $this->datacenter->sockets[$datacenter]->auth_key = null;
$this->authorized = false; $this->authorized = false;
$this->authorization = null; $this->authorization = null;
$this->init_authorization(); // idk $this->init_authorization(); // idk
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']); throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
case 'AUTH_KEY_UNREGISTERED': case 'AUTH_KEY_UNREGISTERED':
case 'AUTH_KEY_INVALID': case 'AUTH_KEY_INVALID':
$this->datacenter->sockets[$datacenter]->temp_auth_key = null; $this->datacenter->sockets[$datacenter]->temp_auth_key = null;
$this->init_authorization(); // idk $this->init_authorization(); // idk
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']); throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
} }
case 420:
case 420: $seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']);
$seconds = preg_replace('/[^0-9]+/', '', $server_answer['error_message']); if (is_numeric($seconds) && isset($this->settings['flood_timeout']['wait_if_lt']) && $seconds < $this->settings['flood_timeout']['wait_if_lt']) {
if (is_numeric($seconds) && isset($this->settings['flood_timeout']['wait_if_lt']) && $seconds < $this->settings['flood_timeout']['wait_if_lt']) { \danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE);
\danog\MadelineProto\Logger::log(['Flood, waiting '.$seconds.' seconds...'], \danog\MadelineProto\Logger::NOTICE); sleep($seconds);
sleep($seconds); throw new \danog\MadelineProto\Exception('Re-executing query...');
throw new \danog\MadelineProto\Exception('Re-executing query...'); }
} default:
default: throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']);
throw new \danog\MadelineProto\RPCErrorException($server_answer['error_message'], $server_answer['error_code']); }
break;
}
} }
public function handle_pending_updates() public function handle_pending_updates()

View File

@ -44,7 +44,7 @@ trait SeqNoHandler
public function content_related($method) public function content_related($method)
{ {
return isset($method['_']) ? !in_array( return isset($method['_']) ? !$this->in_array(
$method['_'], $method['_'],
[ [
'rpc_result', 'rpc_result',

View File

@ -22,9 +22,9 @@ trait UpdateHandler
public $channels_state = []; public $channels_state = [];
public $updates = []; public $updates = [];
public $updates_key = 0; public $updates_key = 0;
private $getting_state = false; protected $getting_state = false;
public $full_chats; public $full_chats;
private $msg_ids = []; protected $msg_ids = [];
public function pwr_update_handler($update) public function pwr_update_handler($update)
{ {
@ -80,7 +80,7 @@ trait UpdateHandler
return []; return [];
} }
if ($params['offset'] < 0) { if ($params['offset'] < 0) {
$params['offset'] = array_reverse(array_keys($this->updates))[abs($params['offset']) - 1]; $params['offset'] = array_reverse(array_keys((array) $this->updates))[abs($params['offset']) - 1];
} }
$updates = []; $updates = [];
ksort($this->updates); ksort($this->updates);
@ -158,7 +158,6 @@ trait UpdateHandler
} }
throw $e; throw $e;
} }
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->get_channel_state($channel)['sync_loading'] = false; $this->get_channel_state($channel)['sync_loading'] = false;
switch ($difference['_']) { switch ($difference['_']) {
case 'updates.channelDifferenceEmpty': case 'updates.channelDifferenceEmpty':
@ -175,6 +174,7 @@ trait UpdateHandler
} }
break; break;
case 'updates.channelDifferenceTooLong': case 'updates.channelDifferenceTooLong':
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE);
$this->set_channel_state($channel, $difference); $this->set_channel_state($channel, $difference);
$this->handle_update_messages($difference['messages'], $channel); $this->handle_update_messages($difference['messages'], $channel);
unset($difference); unset($difference);
@ -239,7 +239,7 @@ trait UpdateHandler
$this->get_update_state()['pending_seq_updates'] = []; $this->get_update_state()['pending_seq_updates'] = [];
} }
$difference = $this->method_call('updates.getDifference', ['pts' => $this->get_update_state()['pts'], 'date' => $this->get_update_state()['date'], 'qts' => $this->get_update_state()['qts']], ['datacenter' => $this->datacenter->curdc]); $difference = $this->method_call('updates.getDifference', ['pts' => $this->get_update_state()['pts'], 'date' => $this->get_update_state()['date'], 'qts' => $this->get_update_state()['qts']], ['datacenter' => $this->datacenter->curdc]);
\danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::VERBOSE); \danog\MadelineProto\Logger::log(['Got '.$difference['_']], \danog\MadelineProto\Logger::ULTRA_VERBOSE);
$this->get_update_state()['sync_loading'] = false; $this->get_update_state()['sync_loading'] = false;
switch ($difference['_']) { switch ($difference['_']) {
@ -312,7 +312,7 @@ 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'] && $this->in_array($update['_'], ['updateNewMessage', 'updateEditMessage', 'updateNewChannelMessage', 'updateEditChannelMessage'])) {
\danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Sync loading, not handling update'], \danog\MadelineProto\Logger::NOTICE);
return false; return false;
@ -561,7 +561,7 @@ trait UpdateHandler
if ($update['_'] === 'updateEncryption') { if ($update['_'] === 'updateEncryption') {
switch ($update['chat']['_']) { switch ($update['chat']['_']) {
case 'encryptedChatRequested': case 'encryptedChatRequested':
if ($this->settings['secret_chats']['accept_chats'] === false || ($this->is_array($this->settings['secret_chats']['accept_chats']) && !in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats']))) { if ($this->settings['secret_chats']['accept_chats'] === false || ($this->is_array($this->settings['secret_chats']['accept_chats']) && !$this->in_array($update['chat']['admin_id'], $this->settings['secret_chats']['accept_chats']))) {
return; return;
} }
\danog\MadelineProto\Logger::log(['Accepting secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Accepting secret chat '.$update['chat']['id']], \danog\MadelineProto\Logger::NOTICE);

View File

@ -1,305 +0,0 @@
<?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 NewConnection extends SerializableVolatile
{
use \danog\MadelineProto\Tools;
public $sock = null;
public $protocol = null;
public $ip = null;
public $port = null;
public $timeout = null;
public $time_delta = 0;
public $temp_auth_key;
public $auth_key;
public $session_id;
public $session_out_seq_no = 0;
public $session_in_seq_no = 0;
public $ipv6 = false;
public $incoming_messages = [];
public $outgoing_messages = [];
public $new_incoming = [];
public $new_outgoing = [];
public function __construct($ip, $port = null, $protocol = null, $timeout = null, $ipv6 = null)
{
if ($this->unserialized($ip)) {
return true;
}
// Can use:
/*
- tcp_full
- tcp_abridged
- tcp_intermediate
- http
- https
- udp
*/
$this->protocol = $protocol;
$this->timeout = $timeout;
$this->ipv6 = $ipv6;
$this->ip = $ip;
$this->port = $port;
$this->sock = new \Volatile();
switch ($this->protocol) {
case 'tcp_abridged':
$this->sock['pony'] = new \Volatile();
$this->sock['pony']['socket'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(chr(239));
break;
case 'tcp_intermediate':
$this->sock['pony'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(str_repeat(chr(238), 4));
break;
case 'tcp_full':
$this->sock['pony'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname('tcp'));
//$this->sock["pony"]->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
//$this->sock["pony"]->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($ip, $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->out_seq_no = -1;
$this->in_seq_no = -1;
break;
case 'http':
case 'https':
$this->parsed = parse_url($ip);
$this->sock['pony'] = new Socket($ipv6 ? \AF_INET6 : \AF_INET, \SOCK_STREAM, getprotobyname($this->protocol === 'https' ? 'tls' : 'tcp'));
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_RCVTIMEO, $timeout);
$this->sock['pony']->setOption(\SOL_SOCKET, \SO_SNDTIMEO, $timeout);
if (!$this->sock['pony']->connect($this->parsed['host'], $port)) {
throw new Exception("Connection: couldn't connect to socket.");
}
break;
case 'udp':
throw new Exception("Connection: This protocol isn't implemented yet.");
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function __destruct()
{
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
try {
unset($this->sock['pony']);
} catch (\danog\MadelineProto\Exception $e) {
}
break;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function close_and_reopen()
{
$this->__destruct();
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function __sleep()
{
$t = get_object_vars($this);
if (isset($t['sock'])) {
unset($t['sock']);
}
return array_keys($t);
}
public function __wakeup()
{
$this->__construct($this->ip, $this->port, $this->protocol, $this->timeout, $this->ipv6);
}
public function write($what, $length = null)
{
if ($length !== null) {
$what = substr($what, 0, $length);
}
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
if (($wrote = $this->sock['pony']->write($what)) !== strlen($what)) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was written (should've written ".strlen($what).', wrote '.$wrote.')!');
}
return $wrote;
break;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
break;
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function read($length)
{
switch ($this->protocol) {
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
case 'http':
case 'https':
$packet = $this->sock['pony']->read($length);
if ($packet === false || strlen($packet) === 0) {
throw new \danog\MadelineProto\NothingInTheSocketException('Nothing in the socket!');
}
if (strlen($packet) != $length) {
throw new \danog\MadelineProto\Exception("WARNING: Wrong length was read (should've read ".($length).', read '.strlen($packet).')!');
}
return $packet;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
default:
throw new Exception('Connection: invalid protocol specified.');
break;
}
}
public function read_message()
{
switch ($this->protocol) {
case 'tcp_full':
$packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
$packet = $this->read($packet_length - 4);
if (strrev(hash('crc32b', $packet_length_data.substr($packet, 0, -4), true)) !== substr($packet, -4)) {
throw new Exception('CRC32 was not correct!');
}
$this->in_seq_no++;
$in_seq_no = \danog\PHP\Struct::unpack('<I', substr($packet, 0, 4))[0];
if ($in_seq_no != $this->in_seq_no) {
throw new Exception('Incoming seq_no mismatch');
}
return substr($packet, 4, $packet_length - 12);
case 'tcp_intermediate':
$packet_length_data = $this->read(4);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data)[0];
return $this->read($packet_length);
case 'tcp_abridged':
$packet_length_data = $this->read(1);
$packet_length = ord($packet_length_data);
if ($packet_length < 127) {
$packet_length <<= 2;
} else {
$packet_length_data = $this->read(3);
$packet_length = \danog\PHP\Struct::unpack('<I', $packet_length_data.pack('x'))[0] << 2;
}
return $this->read($packet_length);
case 'http':
case 'https':
$headers = [];
$close = false;
while (true) {
$current_header = '';
while (($curchar = $this->read(1)) !== "\n") {
$current_header .= $curchar;
}
$current_header = rtrim($current_header);
if ($current_header === '') {
break;
}
if ($current_header === false) {
throw new Exception('No data in the socket!');
}
if (preg_match('|^Content-Length: |i', $current_header)) {
$length = preg_replace('|Content-Length: |i', '', $current_header);
}
if (preg_match('|^Connection: close|i', $current_header)) {
$close = true;
}
$headers[] = $current_header;
}
$read = $this->read($length);
if ($headers[0] !== 'HTTP/1.1 200 OK') {
throw new Exception($headers[0]);
}
if ($close) {
$this->close_and_reopen();
}
return $read;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
}
}
public function send_message($message)
{
switch ($this->protocol) {
case 'tcp_full':
$this->out_seq_no++;
$step1 = \danog\PHP\Struct::pack('<II', (strlen($message) + 12), $this->out_seq_no).$message;
$step2 = $step1.strrev(hash('crc32b', $step1, true));
$this->write($step2);
break;
case 'tcp_intermediate':
$this->write(\danog\PHP\Struct::pack('<I', strlen($message)).$message);
break;
case 'tcp_abridged':
$len = strlen($message) / 4;
if ($len < 127) {
$step1 = chr($len).$message;
} else {
$step1 = chr(127).substr(\danog\PHP\Struct::pack('<I', $len), 0, 3).$message;
}
$this->write($step1);
break;
case 'http':
case 'https':
$this->write('POST '.$this->parsed['path']." HTTP/1.1\r\nHost: ".$this->parsed['host']."\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: keep-alive\r\nContent-Length: ".strlen($message)."\r\n\r\n".$message);
break;
case 'udp':
throw new Exception("Connection: This protocol wasn't implemented yet.");
default:
break;
}
}
}

View File

@ -12,16 +12,17 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto; namespace danog\MadelineProto;
class RSA class RSA extends \Volatile
{ {
use \danog\MadelineProto\TL\TL; use \danog\MadelineProto\TL\TL;
use \danog\MadelineProto\Tools; use \danog\MadelineProto\Tools;
use \danog\Serializable;
public $e; public $e;
public $n; public $n;
public $fp; public $fp;
public function __construct($rsa_key) public function ___construct($rsa_key)
{ {
//if ($this->unserialized($rsa_key)) return true; //if ($this->unserialized($rsa_key)) return true;
\danog\MadelineProto\Logger::log(['Istantiating \phpseclib\Crypt\RSA...'], Logger::ULTRA_VERBOSE); \danog\MadelineProto\Logger::log(['Istantiating \phpseclib\Crypt\RSA...'], Logger::ULTRA_VERBOSE);

View File

@ -19,8 +19,8 @@ namespace danog\MadelineProto\SecretChats;
*/ */
trait AuthKeyHandler trait AuthKeyHandler
{ {
private $temp_requested_secret_chats = []; protected $temp_requested_secret_chats = [];
private $secret_chats = []; protected $secret_chats = [];
public function accept_secret_chat($params) public function accept_secret_chat($params)
{ {
@ -95,7 +95,7 @@ trait AuthKeyHandler
$this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]); $this->method_call('messages.sendEncryptedService', ['peer' => $chat, 'message' => ['_' => 'decryptedMessageService', 'action' => ['_' => 'decryptedMessageActionNotifyLayer', 'layer' => $this->encrypted_layer]]], ['datacenter' => $this->datacenter->curdc]);
} }
private $temp_rekeyed_secret_chats = []; protected $temp_rekeyed_secret_chats = [];
public function rekey($chat) public function rekey($chat)
{ {

View File

@ -34,7 +34,7 @@ trait MessageHandler
} }
$this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message; $this->secret_chats[$chat_id]['outgoing'][$this->secret_chats[$chat_id]['out_seq_no']] = $message;
$message = $this->serialize_object(['type' => $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $this->secret_chats[$chat_id]['layer']); $message = $this->serialize_object(['type' => $this->secret_chats[$chat_id]['layer'] === 8 ? 'DecryptedMessage' : 'DecryptedMessageLayer'], $message, $this->secret_chats[$chat_id]['layer']);
$message = \danog\PHP\Struct::pack('<I', strlen($message)).$message; $message = $this->pack_unsigned_int(strlen($message)).$message;
$message_key = substr(sha1($message, true), -16); $message_key = substr(sha1($message, true), -16);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server'); list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$chat_id]['key']['auth_key'], 'to server');
@ -70,7 +70,7 @@ trait MessageHandler
$encrypted_data = substr($message['message']['bytes'], 24); $encrypted_data = substr($message['message']['bytes'], 24);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$message['message']['chat_id']][$old ? 'old_key' : 'key']['auth_key'], 'to server'); list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->secret_chats[$message['message']['chat_id']][$old ? 'old_key' : 'key']['auth_key'], 'to server');
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv); $decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 0, 4))[0]; $message_data_length = unpack('V', substr($decrypted_data, 0, 4))[1];
if ($message_data_length > strlen($decrypted_data)) { if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\SecurityException('message_data_length is too big'); throw new \danog\MadelineProto\SecurityException('message_data_length is too big');
} }

View File

@ -1,32 +0,0 @@
<?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;
abstract class SerializableVolatile
{
public function unserialized($data)
{
if (!isset($data['_']) || $data['_'] !== 'pony') {
return false;
}
unset($data['_']);
foreach ($data as $key => $data) {
$this->{$key} = $data;
}
if (method_exists($this, '__wakeup')) {
$this->__wakeup();
}
return true;
}
}

View File

@ -31,7 +31,7 @@ class Serialization
if ($instance->API->should_serialize || !(file_exists($filename) && !empty(file_get_contents($filename))) || $force) { if ($instance->API->should_serialize || !(file_exists($filename) && !empty(file_get_contents($filename))) || $force) {
$instance->API->should_serialize = false; $instance->API->should_serialize = false;
return file_put_contents($filename, serialize($instance), LOCK_EX); return file_put_contents($filename, \danog\MadelineProto\Logger::$has_thread ? \danog\Serialization::serialize($instance) : serialize($instance), LOCK_EX);
} }
return false; return false;
@ -54,16 +54,16 @@ class Serialization
$file = fopen($filename, 'r+'); $file = fopen($filename, 'r+');
flock($file, LOCK_SH); flock($file, LOCK_SH);
$unserialized = stream_get_contents($file); $unserialized = stream_get_contents($file);
/*
foreach (['MTProto', 'DataCenter', 'Connection', 'RSA'] as $class) {
$oclass = "danog\\MadelineProto\\".$class;
$nclass = $oclass.'Serializable';
$unserialized = str_replace('O:'.strlen($oclass).':"'.$oclass.'":', 'O:'.strlen($nclass).':"'.$nclass.'":', $unserialized);
}
*/
flock($file, LOCK_UN); flock($file, LOCK_UN);
fclose($file); fclose($file);
$unserialized = unserialize($unserialized); foreach (['RSA', 'TL\TLMethod', 'TL\TLConstructor', 'MTProto', 'API', 'DataCenter', 'Connection'] as $class) {
class_exists('\danog\MadelineProto\\'.$class);
}
try {
$unserialized = \danog\MadelineProto\Logger::$has_thread ? \danog\Serialization::unserialize($unserialized) : unserialize($unserialized);
} catch (Bug74586Exception $e) {
$unserialized = \danog\Serialization::unserialize($unserialized);
}
} else { } else {
throw new Exception('File does not exist'); throw new Exception('File does not exist');
} }

View File

@ -407,7 +407,7 @@ trait Extension
public function get_extension_from_mime($mime) public function get_extension_from_mime($mime)
{ {
foreach ($this->all_mimes as $key => $value) { foreach ($this->all_mimes as $key => $value) {
if (array_search($mime, $value) !== false) { if (array_search($mime, (array) $value) !== false) {
return '.'.$key; return '.'.$key;
} }
} }

View File

@ -14,7 +14,7 @@ namespace danog\MadelineProto\TL\Conversion;
trait TD trait TD
{ {
private $td_params_conversion = [ protected $td_params_conversion = [
'updateNewMessage' => [ 'updateNewMessage' => [
'_' => 'updateNewMessage', '_' => 'updateNewMessage',
'disable_notification' => ['message', 'silent'], 'disable_notification' => ['message', 'silent'],
@ -51,10 +51,10 @@ trait TD
], ],
]; ];
private $reverse = [ protected $reverse = [
'sendMessage'=> 'messages.sendMessage', 'sendMessage'=> 'messages.sendMessage',
]; ];
private $ignore = ['updateMessageID']; protected $ignore = ['updateMessageID'];
public function tdcli_to_td(&$params, $key = null) public function tdcli_to_td(&$params, $key = null)
{ {
@ -126,7 +126,7 @@ trait TD
return $params; return $params;
} }
$newparams = ['_' => $params['_']]; $newparams = ['_' => $params['_']];
if (in_array($params['_'], $this->ignore)) { if ($this->in_array($params['_'], $this->ignore)) {
return $params; return $params;
} }
foreach ($this->td_params_conversion[$params['_']] as $td => $mtproto) { foreach ($this->td_params_conversion[$params['_']] as $td => $mtproto) {

View File

@ -132,7 +132,7 @@ trait TL
$dparams = []; $dparams = [];
} }
$TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name; $TL_dict[$type][$key][$type === 'constructors' ? 'predicate' : 'method'] = $name;
$TL_dict[$type][$key]['id'] = \danog\PHP\Struct::unpack('<i', pack('V', hexdec($id)))[0]; $TL_dict[$type][$key]['id'] = strrev(hex2bin($id));
$TL_dict[$type][$key]['params'] = []; $TL_dict[$type][$key]['params'] = [];
$TL_dict[$type][$key]['type'] = preg_replace(['/.+\s/', '/;/'], '', $line); $TL_dict[$type][$key]['type'] = preg_replace(['/.+\s/', '/;/'], '', $line);
if ($layer !== null) { if ($layer !== null) {
@ -150,6 +150,13 @@ trait TL
} }
$key++; $key++;
} }
} else {
foreach ($TL_dict['constructors'] as $key => $value) {
$TL_dict['constructors'][$key]['id'] = $this->pack_signed_int($TL_dict['constructors'][$key]['id']);
}
foreach ($TL_dict['methods'] as $key => $value) {
$TL_dict['methods'][$key]['id'] = $this->pack_signed_int($TL_dict['methods'][$key]['id']);
}
} }
if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) { if (empty($TL_dict) || empty($TL_dict['constructors']) || !isset($TL_dict['methods'])) {
throw new Exception('Invalid source file was provided: '.$file); throw new Exception('Invalid source file was provided: '.$file);
@ -210,12 +217,11 @@ trait TL
public function serialize_bool($bool) public function serialize_bool($bool)
{ {
return \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('bool'.($bool ? 'True' : 'False'))['id']); return $this->constructors->find_by_predicate($bool ? 'boolTrue' : 'boolFalse')['id'];
} }
public function deserialize_bool($data) public function deserialize_bool($id)
{ {
$id = \danog\PHP\Struct::unpack('<i', $data)[0];
$tl_elem = $this->constructors->find_by_id($id); $tl_elem = $this->constructors->find_by_id($id);
if ($tl_elem === false) { if ($tl_elem === false) {
throw new Exception('Could not extract boolean'); throw new Exception('Could not extract boolean');
@ -232,13 +238,13 @@ trait TL
throw new Exception('given value ('.$object.") isn't numeric"); throw new Exception('given value ('.$object.") isn't numeric");
} }
return \danog\PHP\Struct::pack('<i', $object); return $this->pack_signed_int($object);
case '#': case '#':
if (!is_numeric($object)) { if (!is_numeric($object)) {
throw new Exception('given value ('.$object.") isn't numeric"); throw new Exception('given value ('.$object.") isn't numeric");
} }
return pack('V', $object); return $this->pack_unsigned_int($object);
case 'long': case 'long':
if (is_object($object)) { if (is_object($object)) {
return str_pad(strrev($object->toBytes()), 8, chr(0)); return str_pad(strrev($object->toBytes()), 8, chr(0));
@ -247,11 +253,14 @@ trait TL
if (is_string($object) && strlen($object) === 8) { if (is_string($object) && strlen($object) === 8) {
return $object; return $object;
} }
if (is_string($object) && strlen($object) === 9 && $object[0] === 'a') {
return substr($object, 1);
}
if (!is_numeric($object)) { if (!is_numeric($object)) {
throw new Exception('given value ('.$object.") isn't numeric"); throw new Exception('given value ('.$object.") isn't numeric");
} }
return \danog\PHP\Struct::pack('<q', $object); return $this->pack_signed_long($object);
case 'int128': case 'int128':
if (strlen($object) !== 16) { if (strlen($object) !== 16) {
throw new Exception('Given value is not 16 bytes long'); throw new Exception('Given value is not 16 bytes long');
@ -271,7 +280,7 @@ trait TL
return (string) $object; return (string) $object;
case 'double': case 'double':
return \danog\PHP\Struct::pack('<d', $object); return $this->pack_double($object);
case 'string': case 'string':
$object = pack('C*', ...unpack('C*', $object)); $object = pack('C*', ...unpack('C*', $object));
case 'bytes': case 'bytes':
@ -283,7 +292,7 @@ trait TL
$concat .= pack('@'.$this->posmod((-$l - 1), 4)); $concat .= pack('@'.$this->posmod((-$l - 1), 4));
} else { } else {
$concat .= chr(254); $concat .= chr(254);
$concat .= substr(\danog\PHP\Struct::pack('<i', $l), 0, 3); $concat .= substr($this->pack_signed_int($l), 0, 3);
$concat .= $object; $concat .= $object;
$concat .= pack('@'.$this->posmod(-$l, 4)); $concat .= pack('@'.$this->posmod(-$l, 4));
} }
@ -299,8 +308,8 @@ trait TL
if (!$this->is_array($object)) { if (!$this->is_array($object)) {
throw new Exception("You didn't provide a valid array"); throw new Exception("You didn't provide a valid array");
} }
$concat = \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('vector')['id']); $concat = $this->constructors->find_by_predicate('vector')['id'];
$concat .= \danog\PHP\Struct::pack('<i', count($object)); $concat .= $this->pack_unsigned_int(count($object));
foreach ($object as $current_object) { foreach ($object as $current_object) {
$concat .= $this->serialize_object(['type' => $type['subtype']], $current_object); $concat .= $this->serialize_object(['type' => $type['subtype']], $current_object);
} }
@ -346,7 +355,7 @@ trait TL
$constructorData = $this->constructors->find_by_predicate('inputMessageEntityMentionName'); $constructorData = $this->constructors->find_by_predicate('inputMessageEntityMentionName');
} }
if (!$bare) { if (!$bare) {
$concat .= \danog\PHP\Struct::pack('<i', $constructorData['id']); $concat .= $constructorData['id'];
} }
return $concat.$this->serialize_params($constructorData, $object, $layer); return $concat.$this->serialize_params($constructorData, $object, $layer);
@ -359,7 +368,7 @@ trait TL
throw new Exception('Could not find method: '.$method); throw new Exception('Could not find method: '.$method);
} }
return \danog\PHP\Struct::pack('<i', $tl['id']).$this->serialize_params($tl, $arguments); return $tl['id'].$this->serialize_params($tl, $arguments);
} }
public function serialize_params($tl, $arguments, $layer = -1) public function serialize_params($tl, $arguments, $layer = -1)
@ -413,8 +422,8 @@ trait TL
continue 2; continue 2;
case 'Vector t': case 'Vector t':
if (isset($arguments['id'])) { if (isset($arguments['id'])) {
$serialized .= \danog\PHP\Struct::pack('<i', $this->constructors->find_by_predicate('vector')['id']); $serialized .= $this->constructors->find_by_predicate('vector')['id'];
$serialized .= \danog\PHP\Struct::pack('<i', count($arguments['id'])); $serialized .= $this->pack_unsigned_int(count($arguments['id']));
$serialized .= $this->random(8 * count($arguments['id'])); $serialized .= $this->random(8 * count($arguments['id']));
continue 2; continue 2;
} }
@ -464,17 +473,22 @@ trait TL
} elseif (!is_resource($bytes_io)) { } elseif (!is_resource($bytes_io)) {
throw new Exception('An invalid bytes_io handle was provided.'); throw new Exception('An invalid bytes_io handle was provided.');
} }
switch ($type['type']) { switch ($type['type']) {
case 'Bool': case 'Bool':
return $this->deserialize_bool(stream_get_contents($bytes_io, 4)); return $this->deserialize_bool(stream_get_contents($bytes_io, 4));
case 'int': case 'int':
return \danog\PHP\Struct::unpack('<i', stream_get_contents($bytes_io, 4))[0]; return $this->unpack_signed_int(stream_get_contents($bytes_io, 4));
case '#': case '#':
return unpack('V', stream_get_contents($bytes_io, 4))[1]; return unpack('V', stream_get_contents($bytes_io, 4))[1];
case 'long': case 'long':
return $this->bigint || isset($type['strlong']) ? stream_get_contents($bytes_io, 8) : \danog\PHP\Struct::unpack('<q', stream_get_contents($bytes_io, 8))[0]; if (isset($type['idstrlong'])) {
return 'a'.stream_get_contents($bytes_io, 8);
}
return \danog\MadelineProto\Logger::$bigint || isset($type['strlong']) ? stream_get_contents($bytes_io, 8) : $this->unpack_signed_long(stream_get_contents($bytes_io, 8));
case 'double': case 'double':
return \danog\PHP\Struct::unpack('<d', stream_get_contents($bytes_io, 8))[0]; return $this->unpack_double(stream_get_contents($bytes_io, 8));
case 'int128': case 'int128':
return stream_get_contents($bytes_io, 16); return stream_get_contents($bytes_io, 16);
case 'int256': case 'int256':
@ -509,7 +523,7 @@ 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 = stream_get_contents($bytes_io, 4);
$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);
@ -524,7 +538,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 = unpack('V', stream_get_contents($bytes_io, 4))[1];
$result = []; $result = [];
$type['type'] = $type['subtype']; $type['type'] = $type['subtype'];
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
@ -542,7 +556,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 = stream_get_contents($bytes_io, 4);
$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);
@ -585,7 +599,10 @@ trait TL
break; break;
} }
} }
if ($this->in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id', 'key_fingerprint', 'server_salt', 'new_server_salt', 'server_public_key_fingerprints', 'ping_id', 'exchange_id'])) { if ($this->in_array($arg['name'], ['msg_ids', 'msg_id', 'bad_msg_id', 'req_msg_id', 'answer_msg_id', 'first_msg_id'])) {
$arg['idstrlong'] = true;
}
if ($this->in_array($arg['name'], ['key_fingerprint', 'server_salt', 'new_server_salt', 'server_public_key_fingerprints', 'ping_id', 'exchange_id'])) {
$arg['strlong'] = true; $arg['strlong'] = true;
} }
@ -612,6 +629,14 @@ trait TL
return json_decode($x['data'], true); return json_decode($x['data'], true);
} }
if ($x['_'] === 'message' && isset($x['reply_markup']['rows'])) {
foreach ($x['reply_markup']['rows'] as $key => $row) {
foreach ($row['buttons'] as $bkey => $button) {
$x['reply_markup']['rows'][$key]['buttons'][$bkey] = new \danog\MadelineProto\Button($this, $x, $button);
}
}
}
return $x; return $x;
} }
} }

View File

@ -12,8 +12,12 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
class TLConstructor extends TLParams class TLConstructor extends \Volatile
{ {
use \danog\Serializable;
use \danog\MadelineProto\Tools;
use TLParams;
public $id = []; public $id = [];
public $predicate = []; public $predicate = [];
public $type = []; public $type = [];
@ -23,7 +27,7 @@ class TLConstructor extends TLParams
public function add($json_dict, $scheme_type) public function add($json_dict, $scheme_type)
{ {
$this->id[$this->key] = (int) $json_dict['id']; $this->id[$this->key] = $json_dict['id'];
$this->predicate[$this->key] = (string) ((($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message') ? 'MT' : '').$json_dict['predicate']); $this->predicate[$this->key] = (string) ((($scheme_type === 'mtproto' && $json_dict['predicate'] === 'message') ? 'MT' : '').$json_dict['predicate']);
$this->type[$this->key] = (($scheme_type === 'mtproto' && $json_dict['type'] === 'Message') ? 'MT' : '').$json_dict['type']; $this->type[$this->key] = (($scheme_type === 'mtproto' && $json_dict['type'] === 'Message') ? 'MT' : '').$json_dict['type'];
$this->params[$this->key] = $json_dict['params']; $this->params[$this->key] = $json_dict['params'];
@ -36,13 +40,13 @@ class TLConstructor extends TLParams
public function find_by_type($type) public function find_by_type($type)
{ {
$key = array_search($type, $this->type); $key = array_search($type, (array) $this->type);
return ($key === false) ? false : [ return ($key === false) ? false : [
'id' => $this->id[$key], 'id' => $this->id[$key],
'predicate' => $this->predicate[$key], 'predicate' => $this->predicate[$key],
'type' => $this->type[$key], 'type' => $this->type[$key],
'params' => $this->params[$key], 'params' => $this->array_cast_recursive($this->params[$key]),
]; ];
} }
@ -50,7 +54,7 @@ class TLConstructor extends TLParams
{ {
if ($layer !== -1) { if ($layer !== -1) {
$newlayer = -1; $newlayer = -1;
$keys = array_keys($this->predicate, $predicate); $keys = array_keys((array) $this->predicate, $predicate);
foreach ($keys as $k) { foreach ($keys as $k) {
if ($this->layer[$k] <= $layer && $this->layer[$k] > $newlayer) { if ($this->layer[$k] <= $layer && $this->layer[$k] > $newlayer) {
$key = $k; $key = $k;
@ -61,26 +65,26 @@ class TLConstructor extends TLParams
} }
} }
} else { } else {
$key = array_search($predicate, $this->predicate); $key = array_search($predicate, (array) $this->predicate);
} }
return ($key === false) ? false : [ return ($key === false) ? false : [
'id' => $this->id[$key], 'id' => $this->id[$key],
'predicate' => $this->predicate[$key], 'predicate' => $this->predicate[$key],
'type' => $this->type[$key], 'type' => $this->type[$key],
'params' => $this->params[$key], 'params' => $this->array_cast_recursive($this->params[$key]),
]; ];
} }
public function find_by_id($id) public function find_by_id($id)
{ {
$key = array_search($id, $this->id); $key = array_search($id, (array) $this->id);
return ($key === false) ? false : [ return ($key === false) ? false : [
'id' => $this->id[$key], 'id' => $this->id[$key],
'predicate' => $this->predicate[$key], 'predicate' => $this->predicate[$key],
'type' => $this->type[$key], 'type' => $this->type[$key],
'params' => $this->params[$key], 'params' => $this->array_cast_recursive($this->params[$key]),
]; ];
} }
} }

View File

@ -12,8 +12,11 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
class TLMethod extends TLParams class TLMethod extends \Volatile
{ {
use \danog\Serializable;
use \danog\MadelineProto\Tools;
use TLParams;
public $id = []; public $id = [];
public $method = []; public $method = [];
public $type = []; public $type = [];
@ -23,7 +26,7 @@ class TLMethod extends TLParams
public function add($json_dict) public function add($json_dict)
{ {
$this->id[$this->key] = (int) $json_dict['id']; $this->id[$this->key] = $json_dict['id'];
$this->method[$this->key] = $json_dict['method']; $this->method[$this->key] = $json_dict['method'];
$this->type[$this->key] = $json_dict['type']; $this->type[$this->key] = $json_dict['type'];
$this->params[$this->key] = $json_dict['params']; $this->params[$this->key] = $json_dict['params'];
@ -38,25 +41,25 @@ class TLMethod extends TLParams
public function find_by_method($method) public function find_by_method($method)
{ {
$key = array_search($method, $this->method); $key = array_search($method, (array) $this->method);
return ($key === false) ? false : [ return ($key === false) ? false : [
'id' => $this->id[$key], 'id' => $this->id[$key],
'method' => $this->method[$key], 'method' => $this->method[$key],
'type' => $this->type[$key], 'type' => $this->type[$key],
'params' => $this->params[$key], 'params' => $this->array_cast_recursive($this->params[$key]),
]; ];
} }
public function find_by_id($id) public function find_by_id($id)
{ {
$key = array_search($id, $this->id); $key = array_search($id, (array) $this->id);
return ($key === false) ? false : [ return ($key === false) ? false : [
'id' => $this->id[$key], 'id' => $this->id[$key],
'method' => $this->method[$key], 'method' => $this->method[$key],
'type' => $this->type[$key], 'type' => $this->type[$key],
'params' => $this->params[$key], 'params' => $this->array_cast_recursive($this->params[$key]),
]; ];
} }
} }

View File

@ -12,7 +12,7 @@ If not, see <http://www.gnu.org/licenses/>.
namespace danog\MadelineProto\TL; namespace danog\MadelineProto\TL;
class TLParams trait TLParams
{ {
public function parse_params($key, $mtproto = false) public function parse_params($key, $mtproto = false)
{ {

View File

@ -28,18 +28,12 @@ class SocketHandler extends \Threaded implements \Collectable
*/ */
public function run() public function run()
{ {
require_once __DIR__.'/../SecurityException.php'; require __DIR__.'/../../../../vendor/autoload.php';
require_once __DIR__.'/../RPCErrorException.php'; $this->API->handle_messages($this->current);
require_once __DIR__.'/../ResponseException.php';
require_once __DIR__.'/../TL/Conversion/Exception.php';
require_once __DIR__.'/../TL/Exception.php';
require_once __DIR__.'/../NothingInTheSocketException.php';
require_once __DIR__.'/../Exception.php';
$this->API->handle_messages($current);
$this->setGarbage(); $this->setGarbage();
} }
private $garbage = false; protected $garbage = false;
public function setGarbage():void public function setGarbage():void
{ {

View File

@ -21,11 +21,8 @@ class SocketReader extends \Threaded implements \Collectable
public function __construct($me, $current) public function __construct($me, $current)
{ {
return;
$this->API = $me; $this->API = $me;
$this->current = $current; $this->current = $current;
var_dump('OK');
} }
public function __sleep() public function __sleep()
@ -43,27 +40,23 @@ class SocketReader extends \Threaded implements \Collectable
*/ */
public function run() public function run()
{ {
var_dump('BLOCK'); require __DIR__.'/../../../../vendor/autoload.php';
while (true);
require_once __DIR__.'/../SecurityException.php';
require_once __DIR__.'/../RPCErrorException.php';
require_once __DIR__.'/../ResponseException.php';
require_once __DIR__.'/../TL/Conversion/Exception.php';
require_once __DIR__.'/../TL/Exception.php';
require_once __DIR__.'/../NothingInTheSocketException.php';
require_once __DIR__.'/../Exception.php';
$handler_pool = new \Pool($this->API->settings['threading']['handler_workers']); $handler_pool = new \Pool($this->API->settings['threading']['handler_workers']);
$this->ready = true; $this->ready = true;
//while ($this->API->run_workers) { while ($this->API->run_workers) {
//try { try {
//var_dump('READING'); $this->API->datacenter->sockets[$this->current]->reading = true;
// $this->API->recv_message($this->current); $this->API->recv_message($this->current);
// $handler_pool->submit(new SocketHandler($this->API, $this->current)); var_dump('NOW HANDLE');
//} catch (\danog\MadelineProto\NothingInTheSocketException $e) { \danog\MadelineProto\Logger::log(['Nothing in the socket for dc '.$this->current], \danog\MadelineProto\Logger::VERBOSE); } $handler_pool->submit(new SocketHandler($this->API, $this->current));
//} $this->API->datacenter->sockets[$this->current]->reading = false;
} catch (\danog\MadelineProto\NothingInTheSocketException $e) {
\danog\MadelineProto\Logger::log(['Nothing in the socket for dc '.$this->current], \danog\MadelineProto\Logger::VERBOSE);
}
}
while ($number = $handler_pool->collect()) { while ($number = $handler_pool->collect()) {
\danog\MadelineProto\Logger::log(['Shutting down handler pool for dc '.$this->current.', '.$number.' jobs left'], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log(['Shutting down handler pool for dc '.$this->current.', '.$number.' jobs left'], \danog\MadelineProto\Logger::NOTICE);
} }

View File

@ -19,11 +19,7 @@ trait Tools
{ {
public function random($length) public function random($length)
{ {
if ($length === 0) { return $length === 0 ? '' : \phpseclib\Crypt\Random::string($length);
return '';
}
return \phpseclib\Crypt\Random::string($length);
} }
/** /**
@ -33,18 +29,15 @@ trait Tools
public function posmod($a, $b) public function posmod($a, $b)
{ {
$resto = $a % $b; $resto = $a % $b;
if ($resto < 0) {
$resto += abs($b);
}
return $resto; return $resto < 0 ? ($resto + abs($b)) : $resto;
} }
public function utf8ize($d) public function utf8ize($d)
{ {
if (is_array($d)) { if ($this->is_array($d)) {
foreach ($d as $k => $v) { foreach ($d as $k => $v) {
if ($k === 'bytes') { if ($k === 'bytes' || $this->is_array($v)) {
$d[$k] = $this->utf8ize($v); $d[$k] = $this->utf8ize($v);
} }
} }
@ -62,11 +55,14 @@ trait Tools
public function __call($method, $params) public function __call($method, $params)
{ {
return $method(...$this->array_cast_recursive($params)); return \danog\MadelineProto\Logger::$has_thread ? $method(...$this->array_cast_recursive($params)) : $method(...$params);
} }
public function array_cast_recursive($array) public function array_cast_recursive($array)
{ {
if (!\danog\MadelineProto\Logger::$has_thread) {
return $array;
}
if ($this->is_array($array)) { if ($this->is_array($array)) {
if (!is_array($array)) { if (!is_array($array)) {
$array = (array) $array; $array = (array) $array;
@ -78,4 +74,79 @@ trait Tools
return $array; return $array;
} }
public function unpack_signed_int($value)
{
if (strlen($value) !== 4) {
throw new TL\Exception('Length is not equal to 4');
}
return unpack('l', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function unpack_signed_long($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception('Length is not equal to 8');
}
return unpack('q', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
public function pack_signed_int($value)
{
if ($value > 2147483647) {
throw new TL\Exception('Provided value '.$value.' is bigger than 2147483647');
}
if ($value < -2147483648) {
throw new TL\Exception('Provided value '.$value.' is smaller than -2147483648');
}
$res = pack('l', $value);
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function pack_signed_long($value)
{
if ($value > 9223372036854775807) {
throw new TL\Exception('Provided value '.$value.' is bigger than 9223372036854775807');
}
if ($value < -9223372036854775808) {
throw new TL\Exception('Provided value '.$value.' is smaller than -9223372036854775808');
}
$res = \danog\MadelineProto\Logger::$bigint ? ($this->pack_signed_int($value)."\0\0\0\0") : (\danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev(pack('q', $value)) : pack('q', $value));
return $res;
}
public function pack_unsigned_int($value)
{
if ($value > 4294967295) {
throw new TL\Exception('Provided value '.$value.' is bigger than 4294967296');
}
if ($value < 0) {
throw new TL\Exception('Provided value '.$value.' is smaller than 0');
}
return pack('V', $value);
}
public function pack_signed_double($value)
{
$res = pack('d', $value);
if (strlen($res) !== 8) {
throw new TL\Exception('Could not properly encode double');
}
return \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($res) : $res;
}
public function unpack_double($value)
{
if (strlen($value) !== 8) {
throw new TL\Exception('Length is not equal to 8');
}
return unpack('d', \danog\MadelineProto\Logger::$BIG_ENDIAN ? strrev($value) : $value)[1];
}
} }

View File

@ -20,12 +20,12 @@ namespace danog\MadelineProto\VoIP;
*/ */
trait AuthKeyHandler trait AuthKeyHandler
{ {
private $calls = []; protected $calls = [];
public $REQUESTED = 0; public $REQUESTED = 0;
public $ACCEPTED = 1; public $ACCEPTED = 1;
public $CONFIRMED = 2; public $CONFIRMED = 2;
public $READY = 3; public $READY = 3;
private $emojis = ['😉', '😍', '😛', '😭', '😱', '😡', '😎', '😴', '😵', '😈', '😬', '😇', '😏', '👮', '👷', '💂', '👶', '👨', '👩', '👴', '👵', '😻', '😽', '🙀', '👺', '🙈', '🙉', '🙊', '💀', '👽', '💩', '🔥', '💥', '💤', '👂', '👀', '👃', '👅', '👄', '👍', '👎', '👌', '👊', '✌', '✋', '👐', '👆', '👇', '👉', '👈', '🙏', '👏', '💪', '🚶', '🏃', '💃', '👫', '👪', '👬', '👭', '💅', '🎩', '👑', '👒', '👟', '👞', '👠', '👕', '👗', '👖', '👙', '👜', '👓', '🎀', '💄', '💛', '💙', '💜', '💚', '💍', '💎', '🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐮', '🐗', '🐴', '🐑', '🐘', '🐼', '🐧', '🐥', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐟', '🐬', '🐋', '🐐', '🐊', '🐫', '🍀', '🌹', '🌻', '🍁', '🌾', '🍄', '🌵', '🌴', '🌳', '🌞', '🌚', '🌙', '🌎', '🌋', '⚡', '☔', '❄', '⛄', '🌀', '🌈', '🌊', '🎓', '🎆', '🎃', '👻', '🎅', '🎄', '🎁', '🎈', '🔮', '🎥', '📷', '💿', '💻', '☎', '📡', '📺', '📻', '🔉', '🔔', '⏳', '⏰', '⌚', '🔒', '🔑', '🔎', '💡', '🔦', '🔌', '🔋', '🚿', '🚽', '🔧', '🔨', '🚪', '🚬', '💣', '🔫', '🔪', '💊', '💉', '💰', '💵', '💳', '✉', '📫', '📦', '📅', '📁', '✂', '📌', '📎', '✒', '✏', '📐', '📚', '🔬', '🔭', '🎨', '🎬', '🎤', '🎧', '🎵', '🎹', '🎻', '🎺', '🎸', '👾', '🎮', '🃏', '🎲', '🎯', '🏈', '🏀', '⚽', '⚾', '🎾', '🎱', '🏉', '🎳', '🏁', '🏇', '🏆', '🏊', '🏄', '☕', '🍼', '🍺', '🍷', '🍴', '🍕', '🍔', '🍟', '🍗', '🍱', '🍚', '🍜', '🍡', '🍳', '🍞', '🍩', '🍦', '🎂', '🍰', '🍪', '🍫', '🍭', '🍯', '🍎', '🍏', '🍊', '🍋', '🍒', '🍇', '🍉', '🍓', '🍑', '🍌', '🍐', '🍍', '🍆', '🍅', '🌽', '🏡', '🏥', '🏦', '⛪', '🏰', '⛺', '🏭', '🗻', '🗽', '🎠', '🎡', '⛲', '🎢', '🚢', '🚤', '⚓', '🚀', '✈', '🚁', '🚂', '🚋', '🚎', '🚌', '🚙', '🚗', '🚕', '🚛', '🚨', '🚔', '🚒', '🚑', '🚲', '🚠', '🚜', '🚦', '⚠', '🚧', '⛽', '🎰', '🗿', '🎪', '🎭', '🇯🇵', '🇰🇷', '🇩🇪', '🇨🇳', '🇺🇸', '🇫🇷', '🇪🇸', '🇮🇹', '🇷🇺', '🇬🇧', '1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '0⃣', '🔟', '❗', '❓', '♥', '♦', '💯', '🔗', '🔱', '🔴', '🔵', '🔶', '🔷']; protected $emojis = ['😉', '😍', '😛', '😭', '😱', '😡', '😎', '😴', '😵', '😈', '😬', '😇', '😏', '👮', '👷', '💂', '👶', '👨', '👩', '👴', '👵', '😻', '😽', '🙀', '👺', '🙈', '🙉', '🙊', '💀', '👽', '💩', '🔥', '💥', '💤', '👂', '👀', '👃', '👅', '👄', '👍', '👎', '👌', '👊', '✌', '✋', '👐', '👆', '👇', '👉', '👈', '🙏', '👏', '💪', '🚶', '🏃', '💃', '👫', '👪', '👬', '👭', '💅', '🎩', '👑', '👒', '👟', '👞', '👠', '👕', '👗', '👖', '👙', '👜', '👓', '🎀', '💄', '💛', '💙', '💜', '💚', '💍', '💎', '🐶', '🐺', '🐱', '🐭', '🐹', '🐰', '🐸', '🐯', '🐨', '🐻', '🐷', '🐮', '🐗', '🐴', '🐑', '🐘', '🐼', '🐧', '🐥', '🐔', '🐍', '🐢', '🐛', '🐝', '🐜', '🐞', '🐌', '🐙', '🐚', '🐟', '🐬', '🐋', '🐐', '🐊', '🐫', '🍀', '🌹', '🌻', '🍁', '🌾', '🍄', '🌵', '🌴', '🌳', '🌞', '🌚', '🌙', '🌎', '🌋', '⚡', '☔', '❄', '⛄', '🌀', '🌈', '🌊', '🎓', '🎆', '🎃', '👻', '🎅', '🎄', '🎁', '🎈', '🔮', '🎥', '📷', '💿', '💻', '☎', '📡', '📺', '📻', '🔉', '🔔', '⏳', '⏰', '⌚', '🔒', '🔑', '🔎', '💡', '🔦', '🔌', '🔋', '🚿', '🚽', '🔧', '🔨', '🚪', '🚬', '💣', '🔫', '🔪', '💊', '💉', '💰', '💵', '💳', '✉', '📫', '📦', '📅', '📁', '✂', '📌', '📎', '✒', '✏', '📐', '📚', '🔬', '🔭', '🎨', '🎬', '🎤', '🎧', '🎵', '🎹', '🎻', '🎺', '🎸', '👾', '🎮', '🃏', '🎲', '🎯', '🏈', '🏀', '⚽', '⚾', '🎾', '🎱', '🏉', '🎳', '🏁', '🏇', '🏆', '🏊', '🏄', '☕', '🍼', '🍺', '🍷', '🍴', '🍕', '🍔', '🍟', '🍗', '🍱', '🍚', '🍜', '🍡', '🍳', '🍞', '🍩', '🍦', '🎂', '🍰', '🍪', '🍫', '🍭', '🍯', '🍎', '🍏', '🍊', '🍋', '🍒', '🍇', '🍉', '🍓', '🍑', '🍌', '🍐', '🍍', '🍆', '🍅', '🌽', '🏡', '🏥', '🏦', '⛪', '🏰', '⛺', '🏭', '🗻', '🗽', '🎠', '🎡', '⛲', '🎢', '🚢', '🚤', '⚓', '🚀', '✈', '🚁', '🚂', '🚋', '🚎', '🚌', '🚙', '🚗', '🚕', '🚛', '🚨', '🚔', '🚒', '🚑', '🚲', '🚠', '🚜', '🚦', '⚠', '🚧', '⛽', '🎰', '🗿', '🎪', '🎭', '🇯🇵', '🇰🇷', '🇩🇪', '🇨🇳', '🇺🇸', '🇫🇷', '🇪🇸', '🇮🇹', '🇷🇺', '🇬🇧', '1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣', '0⃣', '🔟', '❗', '❓', '♥', '♦', '💯', '🔗', '🔱', '🔴', '🔵', '🔶', '🔷'];
public function request_call($user) public function request_call($user)
{ {
@ -55,7 +55,7 @@ trait AuthKeyHandler
if ($this->settings['calls']['accept_calls'] === false) { if ($this->settings['calls']['accept_calls'] === false) {
return false; return false;
} }
if (is_array($this->settings['calls']['accept_calls']) && !in_array($this->settings['calls']['accept_calls'])) { if ($this->is_array($this->settings['calls']['accept_calls']) && !$this->in_array($this->settings['calls']['accept_calls'])) {
return false; return false;
} }
if ($params['protocol']['udp_p2p'] && !$this->settings['calls']['allow_p2p']) { if ($params['protocol']['udp_p2p'] && !$this->settings['calls']['allow_p2p']) {

View File

@ -1,25 +0,0 @@
<?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 the MadelineProto.
If not, see <http://www.gnu.org/licenses/>.
*/
namespace danog\MadelineProto;
class VolatileSerializer
{
public function wakeup()
{
$elements = $this;
$elements['_'] = 'pony';
$class = '\\'.str_replace('Serializable', '', get_class($this));
return new $class($elements);
}
}

View File

@ -41,6 +41,7 @@ if ($MadelineProto === false) {
'phone_number' => getenv('MTPROTO_NUMBER'), 'phone_number' => getenv('MTPROTO_NUMBER'),
] ]
); );
\danog\MadelineProto\Logger::log([$checkedPhone], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([$checkedPhone], \danog\MadelineProto\Logger::NOTICE);
$sentCode = $MadelineProto->phone_login(getenv('MTPROTO_NUMBER')); $sentCode = $MadelineProto->phone_login(getenv('MTPROTO_NUMBER'));
\danog\MadelineProto\Logger::log([$sentCode], \danog\MadelineProto\Logger::NOTICE); \danog\MadelineProto\Logger::log([$sentCode], \danog\MadelineProto\Logger::NOTICE);