1
0
mirror of https://github.com/danog/MadelineProto.git synced 2024-11-26 21:14:43 +01:00

refactoring

This commit is contained in:
danogentili 2016-08-09 23:32:34 +02:00
parent 34922d8c1f
commit 9d4c53795b
4 changed files with 111 additions and 70 deletions

View File

@ -1,6 +1,7 @@
# MadelineProto
[![StyleCI](https://styleci.io/repos/61838413/shield)](https://styleci.io/repos/61838413)
[![Build Status](https://travis-ci.org/danog/MadelineProto.svg?branch=master)](https://travis-ci.org/danog/MadelineProto)
[![Build Status](https://travis-ci.org/danog/MadelineProto.svg?branch=master)](https://travis-ci.org/danog/MadelineProto)
Licensed under AGPLv3.
PHP implementation of MTProto, based on [telepy](https://github.com/griganton/telepy_old).

View File

@ -20,12 +20,37 @@ class Connection
public $sock = null;
public $protocol = null;
public function __construct($ip, $port, $protocol = 'tcp')
public function __construct($ip, $port, $protocol = 'tcp_full')
{
switch ($protocol) {
case 'tcp':
// Can use:
/*
- tcp_full
- tcp_abridged
- tcp_intermediate
- http
- https
- udp
*/
$this->protocol = $protocol;
switch ($this->protocol) {
case 'tcp_abridged':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
stream_set_timeout($this->sock, 5);
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(Tools::string2bin('\xef'));
break;
case 'tcp_intermediate':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
stream_set_timeout($this->sock, 5);
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
$this->write(Tools::string2bin('\xeeeeeeee'));
break;
case 'tcp_full':
$this->sock = fsockopen('tcp://'.$ip.':'.$port);
$this->protocol = 'tcp';
stream_set_timeout($this->sock, 5);
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
@ -40,7 +65,9 @@ class Connection
public function __destruct()
{
switch ($this->protocol) {
case 'tcp':
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
fclose($this->sock);
break;
default:
@ -55,11 +82,12 @@ class Connection
$what = substr($what, 0, $length);
}
switch ($this->protocol) {
case 'tcp':
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
return fwrite($this->sock, $what);
break;
default:
@ -71,7 +99,9 @@ class Connection
public function read($length)
{
switch ($this->protocol) {
case 'tcp':
case 'tcp_abridged':
case 'tcp_intermediate':
case 'tcp_full':
if (!(get_resource_type($this->sock) == 'file' || get_resource_type($this->sock) == 'stream')) {
throw new Exception("Connection: couldn't connect to socket.");
}
@ -83,8 +113,4 @@ class Connection
break;
}
}
public function read_message()
{
}
}

View File

@ -28,7 +28,7 @@ class Session extends Tools
'server_salt' => null,
'ip_address' => '149.154.167.50',
'port' => '443',
'protocol' => 'tcp',
'protocol' => 'tcp_full',
'api_id' => 25628,
'api_hash' => '1fe17cda7d355166cdaa71f04122873c',
'tl_schema' => [
@ -105,23 +105,28 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
*/
public function send_message($message_data)
{
$message_id = $this->struct->pack('<Q', (int) ((time() + $this->timedelta) * pow(2, 30)) * 4);
if (($this->auth_key == null) || ($this->server_salt == null)) {
$message = Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.$this->struct->pack('<I', strlen($message_data)).$message_data;
} else {
$encrypted_data =
$this->server_salt.$this->session_id.$message_id.$this->struct->pack('<II', $this->number, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = \phpseclib\Crypt\Random::string(posmod(-strlen($encrypted_data), 16));
$this->log->log(strlen($encrypted_data.$padding));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key);
$message = $this->auth_key_id.$message_key.crypt::ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
switch ($this->settings["protocol"]) {
case 'tcp_full':
$message_id = $this->struct->pack('<Q', (int) ((time() + $this->timedelta) * pow(2, 30)) * 4);
if (($this->auth_key == null) || ($this->server_salt == null)) {
$message = Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.$this->struct->pack('<I', strlen($message_data)).$message_data;
} else {
$encrypted_data =
$this->server_salt.$this->session_id.$message_id.$this->struct->pack('<II', $this->number, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = \phpseclib\Crypt\Random::string(posmod(-strlen($encrypted_data), 16));
$this->log->log(strlen($encrypted_data.$padding));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key);
$message = $this->auth_key_id.$message_key.crypt::ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
}
$step1 = $this->struct->pack('<II', (strlen($message) + 12), $this->number).$message;
$step2 = $step1.$this->struct->pack('<I', $this->newcrc32($step1));
$this->sock->write($step2);
$this->number += 1;
break;
default:
break;
}
$step1 = $this->struct->pack('<II', (strlen($message) + 12), $this->number).$message;
$step2 = $step1.$this->struct->pack('<I', $this->newcrc32($step1));
$this->sock->write($step2);
$this->number += 1;
}
/**
@ -129,42 +134,47 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
*/
public function recv_message()
{
$packet_length_data = $this->sock->read(4);
if (strlen($packet_length_data) < 4) {
throw new Exception('Nothing in the socket!');
}
$packet_length = $this->struct->unpack('<I', $packet_length_data)[0];
$packet = $this->sock->read($packet_length - 4);
if (!($this->newcrc32($packet_length_data.substr($packet, 0, -4)) == $this->struct->unpack('<I', substr($packet, -4))[0])) {
throw new Exception('CRC32 was not correct!');
}
$x = $this->struct->unpack('<I', substr($packet, 0, 4));
$payload = substr($packet, 4, strlen($packet) - 8);
if (strlen($payload) == 4) {
throw new Exception('Server response error: '.$this->struct->unpack('<I', $payload)[0]);
}
$auth_key_id = substr($payload, 0, 8);
if ($auth_key_id == Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
list($message_id, $message_length) = $this->struct->unpack('<8sI', substr($packet, 12, 12));
$data = substr($packet, 24, (24 + $message_length) - 24);
} elseif ($auth_key_id == $this->auth_key_id) {
$message_key = substr($packet, 12, 28 - 12);
$encrypted_data = substr($packet, 28, -4 - 28);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, 'from server');
$decrypted_data = crypt::ige_decrypt($encrypted_data, $aes_key, $aes_iv);
if (substr($decrypted_data, 0, 8) != $this->server_salt) {
throw new Exception('Server salt does not match.');
}
if (substr($decrypted_data, 8, 8) != $this->session_id) {
throw new Exception('Session id does not match.');
}
$message_id = substr($decrypted_data, 16, 24 - 16);
$seq_no = $this->struct->unpack('<I', substr($decrypted_data, 24, 28 - 24)) [0];
$message_data_length = $this->struct->unpack('<I', substr($decrypted_data, 28, 32 - 28)) [0];
$data = substr($decrypted_data, 32, (32 + $message_data_length) - 32);
} else {
DebugFunctions::hex_dump($packet);
throw new Exception('Got unknown auth_key id');
switch ($this->settings["protocol"]) {
case 'tcp_full':
$packet_length_data = $this->sock->read(4);
if (strlen($packet_length_data) < 4) {
throw new Exception('Nothing in the socket!');
}
$packet_length = $this->struct->unpack('<I', $packet_length_data)[0];
$packet = $this->sock->read($packet_length - 4);
if (!($this->newcrc32($packet_length_data.substr($packet, 0, -4)) == $this->struct->unpack('<I', substr($packet, -4))[0])) {
throw new Exception('CRC32 was not correct!');
}
$x = $this->struct->unpack('<I', substr($packet, 0, 4));
$payload = substr($packet, 4, strlen($packet) - 8);
if (strlen($payload) == 4) {
throw new Exception('Server response error: '.$this->struct->unpack('<I', $payload)[0]);
}
$auth_key_id = substr($payload, 0, 8);
if ($auth_key_id == Tools::string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
list($message_id, $message_length) = $this->struct->unpack('<8sI', substr($packet, 12, 12));
$data = substr($packet, 24, $message_length);
} elseif ($auth_key_id == $this->auth_key_id) {
$message_key = substr($packet, 12, 28 - 12);
$encrypted_data = substr($packet, 28, -4 - 28);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, 'from server');
$decrypted_data = crypt::ige_decrypt($encrypted_data, $aes_key, $aes_iv);
if (substr($decrypted_data, 0, 8) != $this->server_salt) {
throw new Exception('Server salt does not match.');
}
if (substr($decrypted_data, 8, 8) != $this->session_id) {
throw new Exception('Session id does not match.');
}
$message_id = substr($decrypted_data, 16, 24 - 16);
$seq_no = $this->struct->unpack('<I', substr($decrypted_data, 24, 28 - 24)) [0];
$message_data_length = $this->struct->unpack('<I', substr($decrypted_data, 28, 32 - 28)) [0];
$data = substr($decrypted_data, 32, (32 + $message_data_length) - 32);
} else {
throw new Exception('Got unknown auth_key id');
}
break;
default:
break;
}
return $data;
@ -178,7 +188,7 @@ Slv8kg9qv1m6XHVQY3PnEw+QQtqSIXklHwIDAQAB
$this->send_message($this->tl->serialize_method($method, $kwargs));
$server_answer = $this->recv_message();
} catch (Exception $e) {
$this->log->log(PHP_EOL.'An error occurred while calling method '.$method.': '.$e->getMessage().PHP_EOL.'Stack trace:'.$e->getTraceAsString().PHP_EOL.'Retrying to call method...'.PHP_EOL);
$this->log->log(PHP_EOL.'An error occurred while calling method '.$method.': '.$e->getMessage().' on line '.$e->getLine().' of file '.$e->getFile().PHP_EOL.'Retrying to call method...'.PHP_EOL);
continue;
}
if ($server_answer == null) {

View File

@ -16,9 +16,13 @@ class TL
{
public function __construct($filename)
{
$TL_dict = [];
foreach ($filename as $file) {
$TL_dict = array_replace(json_decode(file_get_contents($file), true), $TL_dict);
if(is_array($filename)) {
$TL_dict = [];
foreach ($filename as $file) {
$TL_dict = array_replace(json_decode(file_get_contents($file), true), $TL_dict);
}
} else {
$TL_dict = json_decode(file_get_contents($file), true);
}
$this->constructors = $TL_dict['constructors'];
$this->constructor_id = [];