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();