MadelineProto/src/danog/MadelineProto/MTProtoTools/MessageHandler.php

118 lines
6.1 KiB
PHP

<?php
/*
Copyright 2016 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\MTProtoTools;
/**
* Manages packing and unpacking of messages, and the list of sent and received messages.
*/
class MessageHandler extends Crypt
{
/**
* Forming the message frame and sending message to server
* :param message: byte string to send.
*/
public function send_message($message_data, $content_related, $int_message_id = null)
{
if ($int_message_id == null) {
$int_message_id = $this->generate_message_id();
}
if (!is_int($int_message_id)) {
throw new \danog\MadelineProto\Exception("Specified message id isn't an integer");
}
$message_id = \danog\PHP\Struct::pack('<Q', $int_message_id);
if ($this->datacenter->temp_auth_key['auth_key'] == null || $this->datacenter->temp_auth_key['server_salt'] == null) {
$message = $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00').$message_id.\danog\PHP\Struct::pack('<I', strlen($message_data)).$message_data;
} else {
$seq_no = $this->generate_seq_no($content_related);
$encrypted_data = \danog\PHP\Struct::pack('<q', $this->datacenter->temp_auth_key['server_salt']).$this->datacenter->session_id.$message_id.\danog\PHP\Struct::pack('<II', $seq_no, strlen($message_data)).$message_data;
$message_key = substr(sha1($encrypted_data, true), -16);
$padding = \phpseclib\Crypt\Random::string($this->posmod(-strlen($encrypted_data), 16));
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key']);
$message = $this->datacenter->temp_auth_key['id'].$message_key.$this->ige_encrypt($encrypted_data.$padding, $aes_key, $aes_iv);
$this->outgoing_messages[$int_message_id]['seq_no'] = $seq_no;
}
$this->datacenter->send_message($message);
return $int_message_id;
}
/**
* Reading connectionet and receiving message from server. Check the CRC32.
*/
public function recv_message()
{
$payload = $this->datacenter->read_message();
if (fstat($payload)['size'] == 4) {
throw new \danog\MadelineProto\RPCErrorException('Generic', \danog\PHP\Struct::unpack('<i', fread($payload, 4))[0]);
}
$auth_key_id = fread($payload, 8);
if ($auth_key_id == $this->string2bin('\x00\x00\x00\x00\x00\x00\x00\x00')) {
list($message_id, $message_length) = \danog\PHP\Struct::unpack('<QI', fread($payload, 12));
$this->check_message_id($message_id, false);
$message_data = fread($payload, $message_length);
} elseif ($auth_key_id == $this->datacenter->temp_auth_key['id']) {
$message_key = fread($payload, 16);
$encrypted_data = stream_get_contents($payload);
list($aes_key, $aes_iv) = $this->aes_calculate($message_key, $this->datacenter->temp_auth_key['auth_key'], 'from server');
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
$server_salt = \danog\PHP\Struct::unpack('<q', substr($decrypted_data, 0, 8))[0];
if ($server_salt != $this->datacenter->temp_auth_key['server_salt']) {
throw new \danog\MadelineProto\Exception('Server salt mismatch (my server salt '.$this->datacenter->temp_auth_key['server_salt'].' is not equal to server server salt '.$server_salt.').');
}
$session_id = substr($decrypted_data, 8, 8);
if ($session_id != $this->datacenter->session_id) {
throw new \danog\MadelineProto\Exception('Session id mismatch.');
}
$message_id = \danog\PHP\Struct::unpack('<Q', substr($decrypted_data, 16, 8))[0];
$this->check_message_id($message_id, false);
$seq_no = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 24, 4)) [0];
// Dunno how to handle any incorrect sequence numbers
$message_data_length = \danog\PHP\Struct::unpack('<I', substr($decrypted_data, 28, 4)) [0];
if ($message_data_length > strlen($decrypted_data)) {
throw new \danog\MadelineProto\Exception('message_data_length is too big');
}
if ((strlen($decrypted_data) - 32) - $message_data_length > 15) {
throw new \danog\MadelineProto\Exception('difference between message_data_length and the length of the remaining decrypted buffer is too big');
}
if ($message_data_length < 0) {
throw new \danog\MadelineProto\Exception('message_data_length not positive');
}
if ($message_data_length % 4 != 0) {
throw new \danog\MadelineProto\Exception('message_data_length not divisible by 4');
}
$message_data = substr($decrypted_data, 32, $message_data_length);
if ($message_key != substr(sha1(substr($decrypted_data, 0, 32 + $message_data_length), true), -16)) {
throw new \danog\MadelineProto\Exception('msg_key mismatch');
}
$this->incoming_messages[$message_id]['seq_no'] = $seq_no;
} else {
throw new \danog\MadelineProto\Exception('Got unknown auth_key id');
}
$deserialized = $this->tl->deserialize($this->fopen_and_write('php://memory', 'rw+b', $message_data));
$this->incoming_messages[$message_id]['content'] = $deserialized;
return $message_id;
}
}