460 lines
24 KiB
PHP
460 lines
24 KiB
PHP
<?php
|
|
|
|
/*
|
|
Copyright 2016-2018 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\VoIP;
|
|
|
|
/**
|
|
* Manages packing and unpacking of messages, and the list of sent and received messages.
|
|
*/
|
|
trait MessageHandler
|
|
{
|
|
public function pack_string($object)
|
|
{
|
|
$l = \strlen($object);
|
|
$concat = '';
|
|
if ($l <= 253) {
|
|
$concat .= \chr($l);
|
|
$concat .= $object;
|
|
$concat .= \pack('@'.$this->posmod(-$l - 1, 4));
|
|
} else {
|
|
$concat .= \chr(254);
|
|
$concat .= \substr($this->pack_signed_int($l), 0, 3);
|
|
$concat .= $object;
|
|
$concat .= \pack('@'.$this->posmod(-$l, 4));
|
|
}
|
|
|
|
return $concat;
|
|
}
|
|
public function unpack_string($stream)
|
|
{
|
|
$l = \ord(\stream_get_contents($stream, 1));
|
|
if ($l > 254) {
|
|
throw new Exception(\danog\MadelineProto\Lang::$current_lang['length_too_big']);
|
|
}
|
|
if ($l === 254) {
|
|
$long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1];
|
|
$x = \stream_get_contents($stream, $long_len);
|
|
$resto = $this->posmod(-$long_len, 4);
|
|
if ($resto > 0) {
|
|
\stream_get_contents($stream, $resto);
|
|
}
|
|
} else {
|
|
$x = \stream_get_contents($stream, $l);
|
|
$resto = $this->posmod(-($l + 1), 4);
|
|
if ($resto > 0) {
|
|
\stream_get_contents($stream, $resto);
|
|
}
|
|
}
|
|
return $x;
|
|
}
|
|
public function send_message($args, $datacenter = null)
|
|
{
|
|
if ($datacenter === null) {
|
|
return $this->send_message($args, \key($this->datacenter->sockets));
|
|
}
|
|
$message = '';
|
|
switch ($args['_']) {
|
|
// streamTypeSimple codec:int8 = StreamType;
|
|
//
|
|
// packetInit#1 protocol:int min_protocol:int flags:# data_saving_enabled:flags.0?true audio_streams:byteVector<streamTypeSimple> video_streams:byteVector<streamTypeSimple> = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_INIT:
|
|
$message .= $this->pack_signed_int($args['protocol']);
|
|
$message .= $this->pack_signed_int($args['min_protocol']);
|
|
$flags = 0;
|
|
$flags = isset($args['data_saving_enabled']) && $args['data_saving_enabled'] ? $flags | 1 : $flags & ~1;
|
|
$message .= $this->pack_unsigned_int($flags);
|
|
$message .= \chr(\count($args['audio_streams']));
|
|
foreach ($args['audio_streams'] as $codec) {
|
|
$message .= \chr($codec);
|
|
}
|
|
$message .= \chr(\count($args['video_streams']));
|
|
foreach ($args['video_streams'] as $codec) {
|
|
$message .= \chr($codec);
|
|
}
|
|
break;
|
|
// streamType id:int8 type:int8 codec:int8 frame_duration:int16 enabled:int8 = StreamType;
|
|
//
|
|
// packetInitAck#2 protocol:int min_protocol:int all_streams:byteVector<streamType> = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_INIT_ACK:
|
|
$message .= $this->pack_signed_int($args['protocol']);
|
|
$message .= $this->pack_signed_int($args['min_protocol']);
|
|
$message .= \chr(\count($args['all_streams']));
|
|
foreach ($args['all_streams'] as $stream) {
|
|
$message .= \chr($stream['id']);
|
|
$message .= \chr($stream['type']);
|
|
$message .= \chr($stream['codec']);
|
|
$message .= \pack('v', $stream['frame_duration']);
|
|
$message .= \chr($stream['enabled']);
|
|
}
|
|
break;
|
|
// streamTypeState id:int8 enabled:int8 = StreamType;
|
|
// packetStreamState#3 state:streamTypeState = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_STATE:
|
|
$message .= \chr($args['id']);
|
|
$message .= \chr($args['enabled']);
|
|
break;
|
|
// streamData flags:int2 stream_id:int6 has_more_flags:flags.1?true length:(flags.0?int16:int8) timestamp:int data:byteArray = StreamData;
|
|
// packetStreamData#4 stream_data:streamData = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_DATA:
|
|
$length = \strlen($args['data']);
|
|
$flags = 0;
|
|
$flags = $length > 255 ? $flags | 1 : $flags & ~1;
|
|
$flags = isset($args['has_more_flags']) && $args['has_more_flags'] ? $flags | 2 : $flags & ~2;
|
|
$flags = $flags << 6;
|
|
$flags = $flags | $args['stream_id'];
|
|
$message .= \chr($flags);
|
|
$message .= $length > 255 ? \pack('v', $length) : \chr($length);
|
|
$message .= $this->pack_unsigned_int($args['timestamp']);
|
|
$message .= $args['data'];
|
|
break;
|
|
/*case \danog\MadelineProto\VoIP::PKT_UPDATE_STREAMS:
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_PING:
|
|
break;*/
|
|
case \danog\MadelineProto\VoIP::PKT_PONG:
|
|
$message .= $this->pack_unsigned_int($args['out_seq_no']);
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_DATA_X2:
|
|
for ($x = 0; $x < 2; $x++) {
|
|
$length = \strlen($args[$x]['data']);
|
|
$flags = 0;
|
|
$flags = $length > 255 ? $flags | 1 : $flags & ~1;
|
|
$flags = isset($args[$x]['has_more_flags']) && $args[$x]['has_more_flags'] ? $flags | 2 : $flags & ~2;
|
|
$flags = $flags << 6;
|
|
$flags = $flags | $args[$x]['stream_id'];
|
|
$message .= \chr($flags);
|
|
$message .= $length > 255 ? \pack('v', $length) : \chr($length);
|
|
$message .= $this->pack_unsigned_int($args[$x]['timestamp']);
|
|
$message .= $args[$x]['data'];
|
|
}
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_DATA_X3:
|
|
for ($x = 0; $x < 3; $x++) {
|
|
$length = \strlen($args[$x]['data']);
|
|
$flags = 0;
|
|
$flags = $length > 255 ? $flags | 1 : $flags & ~1;
|
|
$flags = isset($args[$x]['has_more_flags']) && $args[$x]['has_more_flags'] ? $flags | 2 : $flags & ~2;
|
|
$flags = $flags << 6;
|
|
$flags = $flags | $args[$x]['stream_id'];
|
|
$message .= \chr($flags);
|
|
$message .= $length > 255 ? \pack('v', $length) : \chr($length);
|
|
$message .= $this->pack_unsigned_int($args[$x]['timestamp']);
|
|
$message .= $args[$x]['data'];
|
|
}
|
|
break;
|
|
// packetLanEndpoint#A address:int port:int = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_LAN_ENDPOINT:
|
|
$message .= $this->pack_signed_int($args['address']);
|
|
$message .= $this->pack_signed_int($args['port']);
|
|
break;
|
|
// packetNetworkChanged#B flags:# data_saving_enabled:flags.0?true = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_NETWORK_CHANGED:
|
|
$message .= $this->pack_signed_int(isset($args['data_saving_enabled']) && $args['data_saving_enabled'] ? 1 : 0);
|
|
break;
|
|
// packetSwitchPreferredRelay#C relay_id:long = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_SWITCH_PREF_RELAY:
|
|
$message .= $this->pack_signed_long($args['relay_d']);
|
|
break;
|
|
/*case \danog\MadelineProto\VoIP::PKT_SWITCH_TO_P2P:
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_NOP:
|
|
break;*/
|
|
}
|
|
|
|
$ack_mask = 0;
|
|
for ($x=0;$x<32;$x++) {
|
|
if ($this->received_timestamp_map[$x]>0) {
|
|
$ack_mask|=1;
|
|
}
|
|
if ($x<31) {
|
|
$ack_mask<<=1;
|
|
}
|
|
}
|
|
|
|
if (\in_array($this->voip_state, [\danog\MadelineProto\VoIP::STATE_WAIT_INIT, \danog\MadelineProto\VoIP::STATE_WAIT_INIT_ACK])) {
|
|
$payload = $this->TLID_DECRYPTED_AUDIO_BLOCK;
|
|
$payload .= $this->random(8);
|
|
$payload .= \chr(7);
|
|
$payload .= $this->random(7);
|
|
$flags = 0;
|
|
$flags = $flags | 4; // call_id
|
|
$flags = $flags | 16; // seqno
|
|
$flags = $flags | 32; // ack mask
|
|
$flags = $flags | 8; // proto
|
|
$flags = isset($args['extra']) ? $flags | 2 : $flags & ~2; // extra
|
|
$flags = \strlen($message) ? $flags | 1 : $flags & ~1; // raw_data
|
|
$flags = $flags | ($args['_'] << 24);
|
|
$payload .= $this->pack_unsigned_int($flags);
|
|
$payload .= $this->configuration['call_id'];
|
|
$payload .= $this->pack_unsigned_int($this->session_in_seq_no);
|
|
$payload .= $this->pack_unsigned_int($this->session_out_seq_no);
|
|
$payload .= $this->pack_unsigned_int($ack_mask);
|
|
$payload .= \danog\MadelineProto\VoIP::PROTO_ID;
|
|
if ($flags & 2) {
|
|
$payload .= $this->pack_string($args['extra']);
|
|
}
|
|
if ($flags & 1) {
|
|
$payload .= $this->pack_string($message);
|
|
}
|
|
} else {
|
|
$payload = $this->TLID_SIMPLE_AUDIO_BLOCK;
|
|
$payload .= $this->random(8);
|
|
$payload .= \chr(7);
|
|
$payload .= $this->random(7);
|
|
$message = \chr($args['_']).$this->pack_unsigned_int($this->session_in_seq_no).$this->pack_unsigned_int($this->session_out_seq_no).$this->pack_unsigned_int($ack_mask).$message;
|
|
|
|
$payload .= $this->pack_string($message);
|
|
}
|
|
$this->session_out_seq_no++;
|
|
|
|
$payload = $this->pack_unsigned_int(\strlen($payload)).$payload;
|
|
$payload_key = \substr(\sha1($payload, true), -16);
|
|
list($aes_key, $aes_iv) = $this->old_aes_calculate($payload_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key'], $this->creator);
|
|
$payload .= $this->random($this->posmod(-\strlen($payload), 16));
|
|
$payload = $this->datacenter->sockets[$datacenter]->peer_tag.$this->datacenter->sockets[$datacenter]->auth_key['id'].$payload_key.$this->ige_encrypt($payload, $aes_key, $aes_iv);
|
|
try {
|
|
$this->datacenter->sockets[$datacenter]->send_message($payload);
|
|
} catch (\danog\MadelineProto\Exception $e) {
|
|
unset($this->datacenter->sockets[$datacenter]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reading connection and receiving message from server.
|
|
*/
|
|
public function recv_message($datacenter)
|
|
{
|
|
$payload = \fopen('php://memory', 'rw+b');
|
|
\fwrite($payload, $this->datacenter->sockets[$datacenter]->read_message());
|
|
\fseek($payload, 0);
|
|
|
|
|
|
if (\stream_get_contents($payload, 16) !== $this->datacenter->sockets[$datacenter]->peer_tag) {
|
|
\danog\MadelineProto\Logger::log("Received packet has wrong peer tag", \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
if (\stream_get_contents($payload, 12) === "\0\0\0\0\0\0\0\0\0\0\0\0") {
|
|
$payload = \stream_get_contents($payload);
|
|
} else {
|
|
\fseek($payload, 16);
|
|
if (\stream_get_contents($payload, 8) !== $this->datacenter->sockets[$datacenter]->auth_key['id']) {
|
|
\danog\MadelineProto\Logger::log('Wrong auth key ID', \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
$message_key = \stream_get_contents($payload, 16);
|
|
list($aes_key, $aes_iv) = $this->old_aes_calculate($message_key, $this->datacenter->sockets[$datacenter]->auth_key['auth_key'], !$this->creator);
|
|
$encrypted_data = \stream_get_contents($payload);
|
|
if (\strlen($encrypted_data) % 16 != 0) {
|
|
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16'], \danog\MadelineProto\Logger::ERROR);
|
|
}
|
|
|
|
$decrypted_data = $this->ige_decrypt($encrypted_data, $aes_key, $aes_iv);
|
|
$message_data_length = \unpack('V', \substr($decrypted_data, 0, 4))[1];
|
|
$payload = \substr($decrypted_data, 4, $message_data_length);
|
|
|
|
if ($message_data_length > \strlen($decrypted_data)) {
|
|
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['msg_data_length_too_big'], \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
if ($message_key != \substr(\sha1(\substr($decrypted_data, 0, 4 + $message_data_length), true), -16)) {
|
|
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['msg_key_mismatch'], \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
if (\strlen($decrypted_data) - 4 - $message_data_length > 15) {
|
|
\danog\MadelineProto\Logger::log('difference between message_data_length and the length of the remaining decrypted buffer is too big', \danog\MadelineProto\Logger::ERROR);
|
|
}
|
|
if (\strlen($decrypted_data) % 16 != 0) {
|
|
\danog\MadelineProto\Logger::log(\danog\MadelineProto\Lang::$current_lang['length_not_divisible_16'], \danog\MadelineProto\Logger::ERROR);
|
|
}
|
|
}
|
|
$stream = \fopen('php://memory', 'rw+b');
|
|
\fwrite($stream, $payload);
|
|
$payload = $stream;
|
|
\fseek($payload, 0);
|
|
|
|
$result = [];
|
|
switch ($crc = \stream_get_contents($payload, 4)) {
|
|
case $this->TLID_DECRYPTED_AUDIO_BLOCK:
|
|
\stream_get_contents($payload, 8);
|
|
$this->unpack_string($payload);
|
|
$flags = \unpack('V', \stream_get_contents($payload, 4))[1];
|
|
$result['_'] = $flags >> 24;
|
|
if ($flags & 4) {
|
|
if (\stream_get_contents($payload, 16) !== $this->configuration['call_id']) {
|
|
\danog\MadelineProto\Logger::log('Call ID mismatch', \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
}
|
|
if ($flags & 16) {
|
|
$in_seq_no = \unpack('V', \stream_get_contents($stream, 4))[1];
|
|
$out_seq_no = \unpack('V', \stream_get_contents($stream, 4))[1];
|
|
}
|
|
if ($flags & 32) {
|
|
$ack_mask = \unpack('V', \stream_get_contents($stream, 4))[1];
|
|
}
|
|
if ($flags & 8) {
|
|
if (\stream_get_contents($stream, 4) !== \danog\MadelineProto\VoIP::PROTO_ID) {
|
|
\danog\MadelineProto\Logger::log('Protocol mismatch', \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
}
|
|
if ($flags & 2) {
|
|
$result['extra'] = $this->unpack_string($stream);
|
|
}
|
|
$message = \fopen('php://memory', 'rw+b');
|
|
|
|
if ($flags & 1) {
|
|
\fwrite($message, $this->unpack_string($stream));
|
|
\fseek($message, 0);
|
|
}
|
|
break;
|
|
case $this->TLID_SIMPLE_AUDIO_BLOCK:
|
|
\stream_get_contents($payload, 8);
|
|
$this->unpack_string($payload);
|
|
$flags = \unpack('V', \stream_get_contents($payload, 4))[1];
|
|
|
|
$message = \fopen('php://memory', 'rw+b');
|
|
\fwrite($message, $this->unpack_string($stream));
|
|
\fseek($message, 0);
|
|
$result['_'] = \ord(\stream_get_contents($message, 1));
|
|
$in_seq_no = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
$out_seq_no = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
$ack_mask = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
|
|
break;
|
|
case $this->TLID_REFLECTOR_SELF_INFO:
|
|
$result['date'] = $this->unpack_signed_int(\stream_get_contents($payload, 4));
|
|
$result['query_id'] = $this->unpack_signed_long(\stream_get_contents($payload, 8));
|
|
$result['my_ip'] = \stream_get_contents($payload, 16);
|
|
$result['my_port'] = $this->unpack_signed_int(\stream_get_contents($payload, 4));
|
|
return $result;
|
|
case $this->TLID_REFLECTOR_PEER_INFO:
|
|
$result['my_address'] = $this->unpack_signed_int(\stream_get_contents($payload, 4));
|
|
$result['my_port'] = $this->unpack_signed_int(\stream_get_contents($payload, 4));
|
|
$result['peer_address'] = $this->unpack_signed_int(\stream_get_contents($payload, 4));
|
|
$result['peer_port'] = $this->unpack_signed_int(\stream_get_contents($payload, 4));
|
|
return $result;
|
|
default:
|
|
\danog\MadelineProto\Logger::log('Unknown packet received: '.\bin2hex($crc), \danog\MadelineProto\Logger::ERROR);
|
|
return false;
|
|
}
|
|
if (!$this->received_packet($datacenter, $in_seq_no, $out_seq_no, $ack_mask)) {
|
|
return false;
|
|
}
|
|
switch ($result['_']) {
|
|
// streamTypeSimple codec:int8 = StreamType;
|
|
//
|
|
// packetInit#1 protocol:int min_protocol:int flags:# data_saving_enabled:flags.0?true audio_streams:byteVector<streamTypeSimple> video_streams:byteVector<streamTypeSimple> = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_INIT:
|
|
$result['protocol'] = $this->unpack_signed_int(\stream_get_contents($message, 4));
|
|
$result['min_protocol'] = $this->unpack_signed_int(\stream_get_contents($message, 4));
|
|
$flags = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
$result['data_saving_enabled'] = (bool) ($flags & 1);
|
|
$result['audio_streams'] = [];
|
|
$length = \ord(\stream_get_contents($message, 1));
|
|
for ($x = 0; $x < $length; $x++) {
|
|
$result['audio_streams'][$x] = \ord(\stream_get_contents($message, 1));
|
|
}
|
|
$result['video_streams'] = [];
|
|
$length = \ord(\stream_get_contents($message, 1));
|
|
for ($x = 0; $x < $length; $x++) {
|
|
$result['video_streams'][$x] = \ord(\stream_get_contents($message, 1));
|
|
}
|
|
break;
|
|
// streamType id:int8 type:int8 codec:int8 frame_duration:int16 enabled:int8 = StreamType;
|
|
//
|
|
// packetInitAck#2 protocol:int min_protocol:int all_streams:byteVector<streamType> = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_INIT_ACK:
|
|
$result['protocol'] = $this->unpack_signed_int(\stream_get_contents($message, 4));
|
|
$result['min_protocol'] = $this->unpack_signed_int(\stream_get_contents($message, 4));
|
|
$result['all_streams'] = [];
|
|
$length = \ord(\stream_get_contents($message, 1));
|
|
for ($x = 0; $x < $length; $x++) {
|
|
$result['all_streams'][$x]['id'] = \ord(\stream_get_contents($message, 1));
|
|
$result['all_streams'][$x]['type'] = \ord(\stream_get_contents($message, 1));
|
|
$result['all_streams'][$x]['codec'] = \ord(\stream_get_contents($message, 1));
|
|
$result['all_streams'][$x]['frame_duration'] = \unpack('v', \stream_get_contents($message, 2))[1];
|
|
$result['all_streams'][$x]['enabled'] = \ord(\stream_get_contents($message, 1));
|
|
}
|
|
|
|
break;
|
|
// streamTypeState id:int8 enabled:int8 = StreamType;
|
|
// packetStreamState#3 state:streamTypeState = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_STATE:
|
|
$result['id'] = \ord(\stream_get_contents($message, 1));
|
|
$result['enabled'] = \ord(\stream_get_contents($message, 1));
|
|
break;
|
|
// streamData flags:int2 stream_id:int6 has_more_flags:flags.1?true length:(flags.0?int16:int8) timestamp:int data:byteArray = StreamData;
|
|
// packetStreamData#4 stream_data:streamData = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_DATA:
|
|
$flags = \ord(\stream_get_contents($message, 1));
|
|
$result['stream_id'] = $flags & 0x3F;
|
|
$flags = ($flags & 0xC0) >> 6;
|
|
$result['has_more_flags'] = (bool) ($flags & 2);
|
|
$length = $flags & 1 ? \unpack('v', \stream_get_contents($message, 2))[1] : \ord(\stream_get_contents($message, 1));
|
|
$result['timestamp'] = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
$result['data'] = \stream_get_contents($message, $length);
|
|
break;
|
|
/*case \danog\MadelineProto\VoIP::PKT_UPDATE_STREAMS:
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_PING:
|
|
break;*/
|
|
case \danog\MadelineProto\VoIP::PKT_PONG:
|
|
if (\fstat($stream)['size'] - \ftell($stream)) {
|
|
$result['out_seq_no'] = \unpack('V', \stream_get_contents($stream, 4))[1];
|
|
}
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_DATA_X2:
|
|
for ($x = 0; $x < 2; $x++) {
|
|
$flags = \ord(\stream_get_contents($message, 1));
|
|
$result[$x]['stream_id'] = $flags & 0x3F;
|
|
$flags = ($flags & 0xC0) >> 6;
|
|
$result[$x]['has_more_flags'] = (bool) ($flags & 2);
|
|
$length = $flags & 1 ? \unpack('v', \stream_get_contents($message, 2))[1] : \ord(\stream_get_contents($message, 1));
|
|
$result[$x]['timestamp'] = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
$result[$x]['data'] = \stream_get_contents($message, $length);
|
|
}
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_STREAM_DATA_X3:
|
|
for ($x = 0; $x < 3; $x++) {
|
|
$flags = \ord(\stream_get_contents($message, 1));
|
|
$result[$x]['stream_id'] = $flags & 0x3F;
|
|
$flags = ($flags & 0xC0) >> 6;
|
|
$result[$x]['has_more_flags'] = (bool) ($flags & 2);
|
|
$length = $flags & 1 ? \unpack('v', \stream_get_contents($message, 2))[1] : \ord(\stream_get_contents($message, 1));
|
|
$result[$x]['timestamp'] = \unpack('V', \stream_get_contents($message, 4))[1];
|
|
$result[$x]['data'] = \stream_get_contents($message, $length);
|
|
}
|
|
break;
|
|
// packetLanEndpoint#A address:int port:int = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_LAN_ENDPOINT:
|
|
$result['address'] = \unpack('V', \stream_get_contents($stream, 4))[1];
|
|
$result['port'] = \unpack('V', \stream_get_contents($stream, 4))[1];
|
|
break;
|
|
// packetNetworkChanged#B flags:# data_saving_enabled:flags.0?true = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_NETWORK_CHANGED:
|
|
$result['data_saving_enabled'] = (bool) (\unpack('V', \stream_get_contents($stream, 4))[1] & 1);
|
|
break;
|
|
// packetSwitchPreferredRelay#C relay_id:long = Packet;
|
|
case \danog\MadelineProto\VoIP::PKT_SWITCH_PREF_RELAY:
|
|
$result['relay_id'] = $this->unpack_signed_long(\stream_get_contents($stream, 8));
|
|
break;
|
|
/*case \danog\MadelineProto\VoIP::PKT_SWITCH_TO_P2P:
|
|
break;
|
|
case \danog\MadelineProto\VoIP::PKT_NOP:
|
|
break;*/
|
|
}
|
|
return $result;
|
|
}
|
|
}
|