diff --git a/docs/docs/PROXY.md b/docs/docs/PROXY.md
index 79c97552..25d51624 100644
--- a/docs/docs/PROXY.md
+++ b/docs/docs/PROXY.md
@@ -77,11 +77,6 @@ Works exactly like the [socket_accept](http://php.net/manual/en/function.socket-
-`public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0);`
-
-Works exactly like the [socket_select](http://php.net/manual/en/function.socket-select.php) function.
-
-
`public function read(int $length, [ int $flags = 0 ]);`
@@ -91,7 +86,7 @@ Works exactly like the [socket_read](http://php.net/manual/en/function.socket-re
`public function write(string $buffer, [ int $length ]);`
-Works exactly like the [socket_read](http://php.net/manual/en/function.socket-write.php) function.
+Works exactly like the [socket_write](http://php.net/manual/en/function.socket-write.php) function.
@@ -120,4 +115,8 @@ Works like [socket_getsockname](http://php.net/manual/en/function.socket-getsock
Can return additional HTTP headers to use when the HTTP protocol is being used.
-
\ No newline at end of file
+`public function getResource();`
+
+Returns the resource used for socket communication: should call `$socket->getResource()`.
+
+
diff --git a/src/CustomHTTPProxy.php b/src/CustomHTTPProxy.php
index 565a22b6..afb5a044 100644
--- a/src/CustomHTTPProxy.php
+++ b/src/CustomHTTPProxy.php
@@ -192,4 +192,9 @@ class CustomHTTPProxy implements \danog\MadelineProto\Proxy
{
$this->options = $extra;
}
+
+ public function getResource()
+ {
+ return $this->sock->getResource();
+ }
}
diff --git a/src/HttpProxy.php b/src/HttpProxy.php
index 459c3b31..2491892e 100644
--- a/src/HttpProxy.php
+++ b/src/HttpProxy.php
@@ -185,4 +185,9 @@ class HttpProxy implements \danog\MadelineProto\Proxy
public function getProxyHeaders()
{
}
+
+ public function getResource()
+ {
+ return $this->sock->getResource();
+ }
}
diff --git a/src/Socket.php b/src/Socket.php
index a024c4c5..909f6c9c 100644
--- a/src/Socket.php
+++ b/src/Socket.php
@@ -81,9 +81,38 @@ If not, see .
return true;
}
- public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0)
+ public static function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0)
{
- return stream_select($read, $write, $except, $tv_sec, $tv_usec);
+ $actual_read = [];
+ foreach ($read as $key => $resource) {
+ $actual_read[$key] = $resource->getResource();
+ }
+ $actual_write = [];
+ foreach ($write as $key => $resource) {
+ $actual_write[$key] = $resource->getResource();
+ }
+ $actual_except = [];
+ foreach ($except as $key => $resource) {
+ $actual_except[$key] = $resource->getResource();
+ }
+ $res = stream_select($actual_read, $actual_write, $actual_except, $tv_sec, $tv_usec);
+ foreach ($read as $key => $resource) {
+ if (!isset($actual_read[$key])) {
+ unset($read[$key]);
+ }
+ }
+ foreach ($write as $key => $resource) {
+ if (!isset($actual_write[$key])) {
+ unset($write[$key]);
+ }
+ }
+ foreach ($except as $key => $resource) {
+ if (!isset($actual_except[$key])) {
+ unset($except[$key]);
+ }
+ }
+
+ return $res;
}
public function read(int $length, int $flags = 0)
@@ -125,6 +154,11 @@ If not, see .
{
return '';
}
+
+ public function getResource()
+ {
+ return $this->sock;
+ }
}
if (!extension_loaded('pthreads')) {
@@ -191,9 +225,38 @@ if (!extension_loaded('pthreads')) {
return socket_connect($this->sock, $address, $port);
}
- public function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0)
+ public static function select(array &$read, array &$write, array &$except, int $tv_sec, int $tv_usec = 0)
{
- return socket_select($read, $write, $except, $tv_sec, $tv_usec);
+ $actual_read = [];
+ foreach ($read as $key => $resource) {
+ $actual_read[$key] = $resource->getResource();
+ }
+ $actual_write = [];
+ foreach ($write as $key => $resource) {
+ $actual_write[$key] = $resource->getResource();
+ }
+ $actual_except = [];
+ foreach ($except as $key => $resource) {
+ $actual_except[$key] = $resource->getResource();
+ }
+ $res = socket_select($actual_read, $actual_write, $actual_except, $tv_sec, $tv_usec);
+ foreach ($read as $key => $resource) {
+ if (!isset($actual_read[$key])) {
+ unset($read[$key]);
+ }
+ }
+ foreach ($write as $key => $resource) {
+ if (!isset($actual_write[$key])) {
+ unset($write[$key]);
+ }
+ }
+ foreach ($except as $key => $resource) {
+ if (!isset($actual_except[$key])) {
+ unset($except[$key]);
+ }
+ }
+
+ return $res;
}
public function read(int $length, int $flags = 0)
@@ -239,6 +302,11 @@ if (!extension_loaded('pthreads')) {
{
return '';
}
+
+ public function getResource()
+ {
+ return $this->sock;
+ }
}
class Socket extends SocketBase
{
diff --git a/src/danog/MadelineProto/Connection.php b/src/danog/MadelineProto/Connection.php
index fa5eeec2..5b90ae78 100644
--- a/src/danog/MadelineProto/Connection.php
+++ b/src/danog/MadelineProto/Connection.php
@@ -20,6 +20,12 @@ class Connection
{
use \danog\Serializable;
use \danog\MadelineProto\Tools;
+ const API_ENDPOINT = 0;
+ const VOIP_UDP_REFLECTOR_ENDPOINT = 1;
+ const VOIP_TCP_REFLECTOR_ENDPOINT = 2;
+ const VOIP_UDP_P2P_ENDPOINT = 3;
+ const VOIP_UDP_LAN_ENDPOINT = 4;
+
public $sock = null;
public $protocol = null;
public $ip = null;
@@ -27,6 +33,8 @@ class Connection
public $timeout = null;
public $parsed = [];
public $time_delta = 0;
+ public $type = 0;
+ public $peer_tag;
public $temp_auth_key;
public $auth_key;
public $session_id;
@@ -208,7 +216,7 @@ class Connection
public function __sleep()
{
- return ['proxy', 'extra', 'protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id', 'obfuscated', 'authorized', 'object_queue', 'ack_queue'];
+ return ['proxy', 'extra', 'protocol', 'ip', 'port', 'timeout', 'parsed', 'time_delta', 'peer_tag', 'temp_auth_key', 'auth_key', 'session_id', 'session_out_seq_no', 'session_in_seq_no', 'ipv6', 'incoming_messages', 'outgoing_messages', 'new_incoming', 'new_outgoing', 'max_incoming_id', 'max_outgoing_id', 'obfuscated', 'authorized', 'object_queue', 'ack_queue'];
}
public function __wakeup()
@@ -405,4 +413,9 @@ class Connection
return ['protocol' => $protocol, 'code' => $code, 'description' => $description, 'body' => $read, 'headers' => $headers];
}
+
+ public function getSocket()
+ {
+ return $this->sock;
+ }
}
diff --git a/src/danog/MadelineProto/DataCenter.php b/src/danog/MadelineProto/DataCenter.php
index 0bf899c9..82bbf1d9 100644
--- a/src/danog/MadelineProto/DataCenter.php
+++ b/src/danog/MadelineProto/DataCenter.php
@@ -144,4 +144,17 @@ class DataCenter
return $all ? array_keys((array) $this->dclist[$test][$ipv6]) : array_keys((array) $this->sockets);
}
+
+ public function select()
+ {
+ $read = [];
+ $write = [];
+ $except = [];
+ foreach ($this->sockets as $dc_id => $socket) {
+ $read[$dc_id] = $socket->getSocket();
+ }
+ \Socket::select($read, $write, $except, 0);
+
+ return array_keys($read);
+ }
}
diff --git a/src/danog/MadelineProto/Proxy.php b/src/danog/MadelineProto/Proxy.php
index 1a5e0916..2df7333c 100644
--- a/src/danog/MadelineProto/Proxy.php
+++ b/src/danog/MadelineProto/Proxy.php
@@ -31,8 +31,6 @@ interface Proxy
public function connect($address, $port = 0);
- public function select(array &$read, array &$write, array &$except, $tv_sec, $tv_usec = 0);
-
public function read($length, $flags = 0);
public function write($buffer, $length = -1);
@@ -48,4 +46,6 @@ interface Proxy
public function getProxyHeaders();
public function setExtra(array $extra = []);
+
+ public function getResource();
}
diff --git a/src/danog/MadelineProto/VoIP.php b/src/danog/MadelineProto/VoIP.php
new file mode 100644
index 00000000..3770e039
--- /dev/null
+++ b/src/danog/MadelineProto/VoIP.php
@@ -0,0 +1,266 @@
+.
+*/
+
+namespace danog\MadelineProto;
+
+if (!extension_loaded('php-libtgvoip') && false) {
+ class VoIP
+ {
+ use \danog\MadelineProto\MTProtoTools\MessageHandler;
+
+ const PHP_LIBTGVOIP_VERSION = '1.1.2';
+ const STATE_CREATED = 0;
+ const STATE_WAIT_INIT = 1;
+ const STATE_WAIT_INIT_ACK = 2;
+ const STATE_ESTABLISHED = 3;
+ const STATE_FAILED = 4;
+ const STATE_RECONNECTING = 5;
+
+ const TGVOIP_ERROR_UNKNOWN = 0;
+ const TGVOIP_ERROR_INCOMPATIBLE = 1;
+ const TGVOIP_ERROR_TIMEOUT = 2;
+ const TGVOIP_ERROR_AUDIO_IO = 3;
+
+ const NET_TYPE_UNKNOWN = 0;
+ const NET_TYPE_GPRS = 1;
+ const NET_TYPE_EDGE = 2;
+ const NET_TYPE_3G = 3;
+ const NET_TYPE_HSPA = 4;
+ const NET_TYPE_LTE = 5;
+ const NET_TYPE_WIFI = 6;
+ const NET_TYPE_ETHERNET = 7;
+ const NET_TYPE_OTHER_HIGH_SPEED = 8;
+ const NET_TYPE_OTHER_LOW_SPEED = 9;
+ const NET_TYPE_DIALUP = 10;
+ const NET_TYPE_OTHER_MOBILE = 11;
+
+ const DATA_SAVING_NEVER = 0;
+ const DATA_SAVING_MOBILE = 1;
+ const DATA_SAVING_ALWAYS = 2;
+
+ const PROXY_NONE = 0;
+ const PROXY_SOCKS5 = 1;
+
+ const AUDIO_STATE_NONE = -1;
+ const AUDIO_STATE_CREATED = 0;
+ const AUDIO_STATE_CONFIGURED = 1;
+ const AUDIO_STATE_RUNNING = 2;
+
+ const CALL_STATE_NONE = -1;
+ const CALL_STATE_REQUESTED = 0;
+ const CALL_STATE_INCOMING = 1;
+ const CALL_STATE_ACCEPTED = 2;
+ const CALL_STATE_CONFIRMED = 3;
+ const CALL_STATE_READY = 4;
+ const CALL_STATE_ENDED = 5;
+
+ private $MadelineProto;
+ public $configuration = ['endpoints' => [], 'shared_config' => []];
+ public $storage = [];
+ public $internalStorage = [];
+ private $signal = 0;
+ private $callState;
+ private $callID;
+ private $creatorID;
+ private $otherID;
+ private $protocol;
+ private $visualization;
+ private $holdFiles = [];
+ private $inputFiles;
+ private $outputFile;
+ private $isPlaying = false;
+
+ private $connection_settings = [];
+ private $dclist = [];
+
+ private $datacenter;
+
+ public function __construct($creator, $otherID, $callID, $MadelineProto, $callState, $protocol)
+ {
+ $this->creator = $creator;
+ $this->otherID = $otherID;
+ $this->callID = $callID;
+ $this->MadelineProto = $MadelineProto;
+ $this->callState = $callState;
+ $this->protocol = $protocol;
+ }
+
+ public function deInitVoIPController()
+ {
+ }
+
+ public function setVisualization($visualization)
+ {
+ $this->visualization = $visualization;
+ }
+
+ public function getVisualization()
+ {
+ return $this->visualization;
+ }
+
+ public function discard($reason = ['_' => 'phoneCallDiscardReasonDisconnect'], $rating = [], $debug = false)
+ {
+ if ($this->callState === self::CALL_STATE_ENDED || empty($this->configuration)) {
+ return false;
+ }
+ $this->MadelineProto->discard_call($this->callID, $reason, $rating, $debug);
+ $this->deinitVoIPController();
+
+ return $this;
+ }
+
+ public function accept()
+ {
+ if ($this->callState !== self::CALL_STATE_INCOMING) {
+ return false;
+ }
+ $this->callState = self::CALL_STATE_ACCEPTED;
+ if (!$this->MadelineProto->accept_call($this->callID)) {
+ $this->discard_call(['_' => 'phoneCallDiscardReasonDisconnect']);
+
+ return false;
+ }
+
+ return $this;
+ }
+
+ public function close()
+ {
+ $this->deinitVoIPController();
+ }
+
+ public function startTheMagic()
+ {
+ return $this;
+ }
+
+ public function play($file)
+ {
+ $this->inputFiles[] = $file;
+
+ return $this;
+ }
+
+ public function playOnHold($files)
+ {
+ $this->holdFiles = $files;
+ }
+
+ public function setOutputFile($file)
+ {
+ $this->outputFile = $file;
+ }
+
+ public function unsetOutputFile()
+ {
+ $this->outputFile = null;
+ }
+
+ public function setMadeline($MadelineProto)
+ {
+ $this->MadelineProto = $MadelineProto;
+ }
+
+ public function getProtocol()
+ {
+ return $this->protocol;
+ }
+
+ public function getOtherID()
+ {
+ return $this->otherID;
+ }
+
+ public function getCallID()
+ {
+ return $this->callID;
+ }
+
+ public function isCreator()
+ {
+ return $this->creator;
+ }
+
+ public function whenCreated()
+ {
+ return isset($this->internalStorage['created']) ? $this->internalStorage['created'] : false;
+ }
+
+ public function parseConfig()
+ {
+ if (count($this->configuration['endpoints'])) {
+ $this->connection_settings['all'] = $this->MadelineProto->settings['connection_settings']['all'];
+ $this->connection_settings['all']['protocol'] = 'obfuscated2';
+ $this->connection_settings['all']['do_not_retry'] = true;
+
+ $test = $this->connection_settings['all']['test_mode'] ? 'test' : 'main';
+ foreach ($this->configuration['endpoints'] as $endpoint) {
+ $this->dclist[$test]['ipv6'][$endpoint['id']] = ['ip_address' => $endpoint['ipv6'], 'port' => $endpoint['port'], 'peer_tag' => $endpoint['peer_tag']];
+ $this->dclist[$test]['ipv4'][$endpoint['id']] = ['ip_address' => $endpoint['ip'], 'port' => $endpoint['port'], 'peer_tag' => $endpoint['peer_tag']];
+ }
+ if (!isset($this->datacenter)) {
+ $this->datacenter = new DataCenter($this->dclist, $this->connection_settings);
+ } else {
+ //$this->datacenter->__construct($this->dclist, $this->connection_settings);
+ }
+ foreach ($this->datacenter->get_dcs() as $new_dc) {
+ $this->datacenter->dc_connect($new_dc);
+ }
+ $this->init_all();
+ foreach ($this->datacenter->get_dcs(false) as $new_dc) {
+ $this->datacenter->dc_connect($new_dc);
+ }
+ $this->init_all();
+ }
+ }
+
+ private function init_all()
+ {
+ foreach ($this->datacenter->sockets as $dc_id => $socket) {
+ if ($socket->auth_key === null) {
+ $socket->auth_key = ['id' => $this->configuration['auth_key_id'], 'auth_key' => $this->configuration['auth_key']];
+ }
+ }
+ }
+
+ public function getCallState()
+ {
+ return $this->callState;
+ }
+
+ public function getVersion()
+ {
+ return 'libponyvoip-1.0';
+ }
+
+ public function getPreferredRelayID()
+ {
+ return 0;
+ }
+
+ public function getLastError()
+ {
+ return '';
+ }
+
+ public function getDebugLog()
+ {
+ return '';
+ }
+
+ public function getSignalBarsCount()
+ {
+ return $this->signal;
+ }
+ }
+}
diff --git a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php
index afcde842..24cfeb66 100644
--- a/src/danog/MadelineProto/VoIP/AuthKeyHandler.php
+++ b/src/danog/MadelineProto/VoIP/AuthKeyHandler.php
@@ -130,7 +130,7 @@ trait AuthKeyHandler
$this->calls[$params['id']]->setVisualization($visualization);
$this->calls[$params['id']]->configuration['shared_config'] = array_merge($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), $this->calls[$params['id']]->configuration['shared_config']);
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$res['connection']], $res['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
- $this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
+ $this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'auth_key_id' => substr(sha1($key, true), -8), 'call_id' => substr(hash('sha256', $key, true), -16), 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig();
$res = $this->calls[$params['id']]->startTheMagic();
$this->handle_pending_updates();
@@ -173,7 +173,7 @@ trait AuthKeyHandler
$this->calls[$params['id']]->setVisualization($visualization);
$this->calls[$params['id']]->configuration['shared_config'] = array_merge($this->method_call('phone.getCallConfig', [], ['datacenter' => $this->datacenter->curdc]), $this->calls[$params['id']]->configuration['shared_config']);
$this->calls[$params['id']]->configuration['endpoints'] = array_merge([$params['connection']], $params['alternative_connections'], $this->calls[$params['id']]->configuration['endpoints']);
- $this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
+ $this->calls[$params['id']]->configuration = array_merge(['recv_timeout' => $this->config['call_receive_timeout_ms'] / 1000, 'init_timeout' => $this->config['call_connect_timeout_ms'] / 1000, 'data_saving' => \danog\MadelineProto\VoIP::DATA_SAVING_NEVER, 'enable_NS' => true, 'enable_AEC' => true, 'enable_AGC' => true, 'auth_key' => $key, 'auth_key_id' => substr(sha1($key, true), -8), 'call_id' => substr(hash('sha256', $key, true), -16), 'network_type' => \danog\MadelineProto\VoIP::NET_TYPE_ETHERNET], $this->calls[$params['id']]->configuration);
$this->calls[$params['id']]->parseConfig();
return $this->calls[$params['id']]->startTheMagic();