This commit is contained in:
2022-10-23 01:39:27 +02:00
parent 8c17aab483
commit 1929b84685
4130 changed files with 479334 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
<?php
namespace Fbns\Client\Auth;
use Fbns\Client\AuthInterface;
use Fbns\Client\Json;
class DeviceAuth implements AuthInterface
{
const TYPE = 'device_auth';
/**
* @var string
*/
private $json;
/**
* @var int
*/
private $clientId;
/**
* @var int
*/
private $userId;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $deviceId;
/**
* @var string
*/
private $deviceSecret;
/**
* @return string
*/
private function randomUuid()
{
return sprintf(
'%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0xffff)
);
}
/**
* Constructor.
*/
public function __construct()
{
$this->clientId = substr($this->randomUuid(), 0, 20);
$this->userId = 0;
$this->password = '';
$this->deviceSecret = '';
$this->deviceId = '';
}
/**
* @param string $json
*/
public function read($json)
{
$data = Json::decode($json);
$this->json = $json;
if (isset($data->ck)) {
$this->userId = $data->ck;
} else {
$this->userId = 0;
}
if (isset($data->cs)) {
$this->password = $data->cs;
} else {
$this->password = '';
}
if (isset($data->di)) {
$this->deviceId = $data->di;
$this->clientId = substr($this->deviceId, 0, 20);
} else {
$this->deviceId = '';
$this->clientId = substr($this->randomUuid(), 0, 20);
}
if (isset($data->ds)) {
$this->deviceSecret = $data->ds;
} else {
$this->deviceSecret = '';
}
// TODO: sr ?
// TODO: rc ?
}
/**
* @return string
*/
public function __toString()
{
return $this->json !== null ? $this->json : '';
}
/**
* @return int
*/
public function getUserId()
{
return $this->userId;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @return string
*/
public function getDeviceId()
{
return $this->deviceId;
}
/**
* @return string
*/
public function getDeviceSecret()
{
return $this->deviceSecret;
}
/**
* @return string
*/
public function getClientType()
{
return self::TYPE;
}
/**
* @return string
*/
public function getClientId()
{
return $this->clientId;
}
}

41
vendor/valga/fbns-react/src/AuthInterface.php vendored Executable file
View File

@@ -0,0 +1,41 @@
<?php
namespace Fbns\Client;
interface AuthInterface
{
/**
* @return string
*/
public function getClientId();
/**
* @return string
*/
public function getClientType();
/**
* @return int
*/
public function getUserId();
/**
* @return string
*/
public function getPassword();
/**
* @return string
*/
public function getDeviceId();
/**
* @return string
*/
public function getDeviceSecret();
/**
* @return string
*/
public function __toString();
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of net-mqtt.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Common;
use BinSoul\Net\Mqtt\Packet\BasePacket;
use BinSoul\Net\Mqtt\Packet\PublishAckPacket as BasePublishAckPacket;
use BinSoul\Net\Mqtt\PacketStream;
/**
* Represents the PUBACK packet.
*/
class PublishAckPacket extends BasePublishAckPacket
{
public function read(PacketStream $stream)
{
BasePacket::read($stream);
//$this->assertPacketFlags($this->getExpectedPacketFlags());
$this->assertRemainingPacketLength(2);
$this->identifier = $stream->readWord();
}
}

339
vendor/valga/fbns-react/src/Connection.php vendored Executable file
View File

@@ -0,0 +1,339 @@
<?php
namespace Fbns\Client;
use Fbns\Client\Thrift\Compact;
use Fbns\Client\Thrift\Writer;
class Connection
{
const FBNS_CLIENT_CAPABILITIES = 439;
const FBNS_ENDPOINT_CAPABILITIES = 128;
const FBNS_APP_ID = '567310203415052';
const FBNS_CLIENT_STACK = 3;
const FBNS_PUBLISH_FORMAT = 1;
const CLIENT_ID = 1;
const CLIENT_INFO = 4;
const PASSWORD = 5;
const USER_ID = 1;
const USER_AGENT = 2;
const CLIENT_CAPABILITIES = 3;
const ENDPOINT_CAPABILITIES = 4;
const PUBLISH_FORMAT = 5;
const NO_AUTOMATIC_FOREGROUND = 6;
const MAKE_USER_AVAILABLE_IN_FOREGROUND = 7;
const DEVICE_ID = 8;
const IS_INITIALLY_FOREGROUND = 9;
const NETWORK_TYPE = 10;
const NETWORK_SUBTYPE = 11;
const CLIENT_MQTT_SESSION_ID = 12;
const SUBSCRIBE_TOPICS = 14;
const CLIENT_TYPE = 15;
const APP_ID = 16;
const DEVICE_SECRET = 20;
const CLIENT_STACK = 21;
/** @var AuthInterface */
private $auth;
/** @var string */
private $userAgent;
/** @var int */
private $clientCapabilities;
/** @var int */
private $endpointCapabilities;
/** @var int */
private $publishFormat;
/** @var bool */
private $noAutomaticForeground;
/** @var bool */
private $makeUserAvailableInForeground;
/** @var bool */
private $isInitiallyForeground;
/** @var int */
private $networkType;
/** @var int */
private $networkSubtype;
/** @var int */
private $clientMqttSessionId;
/** @var int[] */
private $subscribeTopics;
/** @var int */
private $appId;
/** @var int */
private $clientStack;
/**
* Connection constructor.
*
* @param AuthInterface $auth
* @param string $userAgent
*/
public function __construct(AuthInterface $auth, $userAgent)
{
$this->auth = $auth;
$this->userAgent = $userAgent;
$this->clientCapabilities = self::FBNS_CLIENT_CAPABILITIES;
$this->endpointCapabilities = self::FBNS_ENDPOINT_CAPABILITIES;
$this->publishFormat = self::FBNS_PUBLISH_FORMAT;
$this->noAutomaticForeground = true;
$this->makeUserAvailableInForeground = false;
$this->isInitiallyForeground = false;
$this->networkType = 1;
$this->networkSubtype = 0;
$this->subscribeTopics = [(int) Lite::MESSAGE_TOPIC_ID, (int) Lite::REG_RESP_TOPIC_ID];
$this->appId = self::FBNS_APP_ID;
$this->clientStack = self::FBNS_CLIENT_STACK;
}
/**
* @return string
*/
public function toThrift()
{
$writer = new Writer();
$writer->writeString(self::CLIENT_ID, $this->auth->getClientId());
$writer->writeStruct(self::CLIENT_INFO);
$writer->writeInt64(self::USER_ID, $this->auth->getUserId());
$writer->writeString(self::USER_AGENT, $this->userAgent);
$writer->writeInt64(self::CLIENT_CAPABILITIES, $this->clientCapabilities);
$writer->writeInt64(self::ENDPOINT_CAPABILITIES, $this->endpointCapabilities);
$writer->writeInt32(self::PUBLISH_FORMAT, $this->publishFormat);
$writer->writeBool(self::NO_AUTOMATIC_FOREGROUND, $this->noAutomaticForeground);
$writer->writeBool(self::MAKE_USER_AVAILABLE_IN_FOREGROUND, $this->makeUserAvailableInForeground);
$writer->writeString(self::DEVICE_ID, $this->auth->getDeviceId());
$writer->writeBool(self::IS_INITIALLY_FOREGROUND, $this->isInitiallyForeground);
$writer->writeInt32(self::NETWORK_TYPE, $this->networkType);
$writer->writeInt32(self::NETWORK_SUBTYPE, $this->networkSubtype);
if ($this->clientMqttSessionId === null) {
$sessionId = (int) ((microtime(true) - strtotime('Last Monday')) * 1000);
} else {
$sessionId = $this->clientMqttSessionId;
}
$writer->writeInt64(self::CLIENT_MQTT_SESSION_ID, $sessionId);
$writer->writeList(self::SUBSCRIBE_TOPICS, Compact::TYPE_I32, $this->subscribeTopics);
$writer->writeString(self::CLIENT_TYPE, $this->auth->getClientType());
$writer->writeInt64(self::APP_ID, $this->appId);
$writer->writeString(self::DEVICE_SECRET, $this->auth->getDeviceSecret());
$writer->writeInt8(self::CLIENT_STACK, $this->clientStack);
$writer->writeStop();
$writer->writeString(self::PASSWORD, $this->auth->getPassword());
$writer->writeStop();
return (string) $writer;
}
/**
* @return string
*/
public function getUserAgent()
{
return $this->userAgent;
}
/**
* @param string $userAgent
*/
public function setUserAgent($userAgent)
{
$this->userAgent = $userAgent;
}
/**
* @return int
*/
public function getClientCapabilities()
{
return $this->clientCapabilities;
}
/**
* @param int $clientCapabilities
*/
public function setClientCapabilities($clientCapabilities)
{
$this->clientCapabilities = $clientCapabilities;
}
/**
* @return int
*/
public function getEndpointCapabilities()
{
return $this->endpointCapabilities;
}
/**
* @param int $endpointCapabilities
*/
public function setEndpointCapabilities($endpointCapabilities)
{
$this->endpointCapabilities = $endpointCapabilities;
}
/**
* @return bool
*/
public function isNoAutomaticForeground()
{
return $this->noAutomaticForeground;
}
/**
* @param bool $noAutomaticForeground
*/
public function setNoAutomaticForeground($noAutomaticForeground)
{
$this->noAutomaticForeground = $noAutomaticForeground;
}
/**
* @return bool
*/
public function isMakeUserAvailableInForeground()
{
return $this->makeUserAvailableInForeground;
}
/**
* @param bool $makeUserAvailableInForeground
*/
public function setMakeUserAvailableInForeground($makeUserAvailableInForeground)
{
$this->makeUserAvailableInForeground = $makeUserAvailableInForeground;
}
/**
* @return bool
*/
public function isInitiallyForeground()
{
return $this->isInitiallyForeground;
}
/**
* @param bool $isInitiallyForeground
*/
public function setIsInitiallyForeground($isInitiallyForeground)
{
$this->isInitiallyForeground = $isInitiallyForeground;
}
/**
* @return int
*/
public function getNetworkType()
{
return $this->networkType;
}
/**
* @param int $networkType
*/
public function setNetworkType($networkType)
{
$this->networkType = $networkType;
}
/**
* @return int
*/
public function getNetworkSubtype()
{
return $this->networkSubtype;
}
/**
* @param int $networkSubtype
*/
public function setNetworkSubtype($networkSubtype)
{
$this->networkSubtype = $networkSubtype;
}
/**
* @return int
*/
public function getClientMqttSessionId()
{
return $this->clientMqttSessionId;
}
/**
* @param int $clientMqttSessionId
*/
public function setClientMqttSessionId($clientMqttSessionId)
{
$this->clientMqttSessionId = $clientMqttSessionId;
}
/**
* @return int[]
*/
public function getSubscribeTopics()
{
return $this->subscribeTopics;
}
/**
* @param int[] $subscribeTopics
*/
public function setSubscribeTopics($subscribeTopics)
{
$this->subscribeTopics = $subscribeTopics;
}
/**
* @return int
*/
public function getAppId()
{
return $this->appId;
}
/**
* @param int $appId
*/
public function setAppId($appId)
{
$this->appId = $appId;
}
/**
* @return int
*/
public function getClientStack()
{
return $this->clientStack;
}
/**
* @param int $clientStack
*/
public function setClientStack($clientStack)
{
$this->clientStack = $clientStack;
}
/**
* @return AuthInterface
*/
public function getAuth()
{
return $this->auth;
}
/**
* @param AuthInterface $auth
*/
public function setAuth(AuthInterface $auth)
{
$this->auth = $auth;
}
}

28
vendor/valga/fbns-react/src/Json.php vendored Executable file
View File

@@ -0,0 +1,28 @@
<?php
namespace Fbns\Client;
class Json
{
/**
* Special decoder to keep big numbers on x86 PHP builds.
*
* @param string $json
*
* @return mixed
*/
public static function decode($json)
{
$flags = 0;
if (PHP_INT_SIZE === 4) {
$flags |= JSON_BIGINT_AS_STRING;
}
$data = json_decode($json, false, 512, $flags);
$error = json_last_error();
if ($error !== JSON_ERROR_NONE) {
throw new \InvalidArgumentException(sprintf('Failed to decode JSON (%d): %s.', $error, json_last_error_msg()));
}
return $data;
}
}

379
vendor/valga/fbns-react/src/Lite.php vendored Executable file
View File

@@ -0,0 +1,379 @@
<?php
namespace Fbns\Client;
use BinSoul\Net\Mqtt\DefaultMessage;
use BinSoul\Net\Mqtt\Message;
use Evenement\EventEmitterInterface;
use Evenement\EventEmitterTrait;
use Fbns\Client\Lite\ConnectResponsePacket;
use Fbns\Client\Lite\OutgoingConnectFlow;
use Fbns\Client\Lite\ReactMqttClient;
use Fbns\Client\Lite\StreamParser;
use Fbns\Client\Message\Push;
use Fbns\Client\Message\Register;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use React\EventLoop\LoopInterface;
use React\EventLoop\Timer\TimerInterface;
use React\Promise\Deferred;
use React\Promise\FulfilledPromise;
use React\Promise\PromiseInterface;
use React\Socket\Connector;
use React\Socket\ConnectorInterface;
use React\Socket\SecureConnector;
class Lite implements EventEmitterInterface
{
use EventEmitterTrait;
const QOS_LEVEL = 1;
const MESSAGE_TOPIC = '/fbns_msg';
const MESSAGE_TOPIC_ID = '76';
const REG_REQ_TOPIC = '/fbns_reg_req';
const REG_REQ_TOPIC_ID = '79';
const REG_RESP_TOPIC = '/fbns_reg_resp';
const REG_RESP_TOPIC_ID = '80';
const ID_TO_TOPIC_ENUM = [
self::MESSAGE_TOPIC_ID => self::MESSAGE_TOPIC,
self::REG_REQ_TOPIC_ID => self::REG_REQ_TOPIC,
self::REG_RESP_TOPIC_ID => self::REG_RESP_TOPIC,
];
const TOPIC_TO_ID_ENUM = [
self::MESSAGE_TOPIC => self::MESSAGE_TOPIC_ID,
self::REG_REQ_TOPIC => self::REG_REQ_TOPIC_ID,
self::REG_RESP_TOPIC => self::REG_RESP_TOPIC_ID,
];
/**
* @var LoopInterface
*/
private $loop;
/**
* @var ConnectorInterface
*/
private $connector;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @var ReactMqttClient
*/
private $client;
/**
* @var TimerInterface
*/
private $keepaliveTimer;
/**
* Constructor.
*
* @param LoopInterface $loop
* @param ConnectorInterface|null $connector
* @param LoggerInterface|null $logger
*/
public function __construct(LoopInterface $loop, ConnectorInterface $connector = null, LoggerInterface $logger = null)
{
$this->loop = $loop;
if ($connector === null) {
$this->connector = new SecureConnector(new Connector($loop), $loop);
} else {
$this->connector = $connector;
}
if ($logger !== null) {
$this->logger = $logger;
} else {
$this->logger = new NullLogger();
}
$this->client = new ReactMqttClient($this->connector, $this->loop, null, new StreamParser());
$this->client
->on('open', function () {
$this->logger->info('Connection has been established.');
})
->on('close', function () {
$this->logger->info('Network connection has been closed.');
$this->cancelKeepaliveTimer();
$this->emit('disconnect', [$this]);
})
->on('warning', function (\Exception $e) {
$this->logger->warning($e->getMessage());
})
->on('error', function (\Exception $e) {
$this->logger->error($e->getMessage());
$this->emit('error', [$e]);
})
->on('connect', function (ConnectResponsePacket $responsePacket) {
$this->logger->info('Connected to a broker.');
$this->setKeepaliveTimer();
$this->emit('connect', [$responsePacket]);
})
->on('disconnect', function () {
$this->logger->info('Disconnected from the broker.');
})
->on('message', function (Message $message) {
$this->setKeepaliveTimer();
$this->onMessage($message);
})
->on('publish', function () {
$this->logger->info('Publish flow has been completed.');
$this->setKeepaliveTimer();
})
->on('ping', function () {
$this->logger->info('Ping flow has been completed.');
$this->setKeepaliveTimer();
});
}
private function cancelKeepaliveTimer()
{
if ($this->keepaliveTimer !== null) {
if ($this->keepaliveTimer->isActive()) {
$this->logger->info('Existing keepalive timer has been canceled.');
$this->keepaliveTimer->cancel();
}
$this->keepaliveTimer = null;
}
}
private function onKeepalive()
{
$this->logger->info('Keepalive timer has been fired.');
$this->cancelKeepaliveTimer();
$this->disconnect();
}
private function setKeepaliveTimer()
{
$this->cancelKeepaliveTimer();
$keepaliveInterval = OutgoingConnectFlow::KEEPALIVE;
$this->logger->info(sprintf('Setting up keepalive timer to %d seconds', $keepaliveInterval));
$this->keepaliveTimer = $this->loop->addTimer($keepaliveInterval, function () {
$this->onKeepalive();
});
}
/**
* @param string $payload
*/
private function onRegister($payload)
{
try {
$message = new Register($payload);
} catch (\Exception $e) {
$this->logger->warning(sprintf('Failed to decode register message: %s', $e->getMessage()), [$payload]);
return;
}
$this->emit('register', [$message]);
}
/**
* @param string $payload
*/
private function onPush($payload)
{
try {
$message = new Push($payload);
} catch (\Exception $e) {
$this->logger->warning(sprintf('Failed to decode push message: %s', $e->getMessage()), [$payload]);
return;
}
$this->emit('push', [$message]);
}
/**
* @param Message $message
*/
private function onMessage(Message $message)
{
$payload = @zlib_decode($message->getPayload());
if ($payload === false) {
$this->logger->warning('Failed to inflate a payload.');
return;
}
$topic = $this->unmapTopic($message->getTopic());
$this->logger->info(sprintf('Received a message from topic "%s".', $topic), [$payload]);
switch ($topic) {
case self::MESSAGE_TOPIC:
$this->onPush($payload);
break;
case self::REG_RESP_TOPIC:
$this->onRegister($payload);
break;
default:
$this->logger->warning(sprintf('Received a message from unknown topic "%s".', $topic), [$payload]);
}
}
/**
* Establishes a connection to the FBNS server.
*
* @param string $host
* @param int $port
* @param Connection $connection
* @param int $timeout
*
* @return PromiseInterface
*/
private function establishConnection($host, $port, Connection $connection, $timeout)
{
$this->logger->info(sprintf('Connecting to %s:%d...', $host, $port));
return $this->client->connect($host, $port, $connection, $timeout);
}
/**
* Connects to a FBNS server.
*
* @param string $host
* @param int $port
* @param Connection $connection
* @param int $timeout
*
* @return PromiseInterface
*/
public function connect($host, $port, Connection $connection, $timeout = 5)
{
$deferred = new Deferred();
$this->disconnect()
->then(function () use ($deferred, $host, $port, $connection, $timeout) {
$this->establishConnection($host, $port, $connection, $timeout)
->then(function () use ($deferred) {
$deferred->resolve($this);
})
->otherwise(function (\Exception $error) use ($deferred) {
$deferred->reject($error);
});
})
->otherwise(function () use ($deferred) {
$deferred->reject($this);
});
return $deferred->promise();
}
/**
* @return PromiseInterface
*/
public function disconnect()
{
if ($this->client->isConnected()) {
$deferred = new Deferred();
$this->client->disconnect()
->then(function () use ($deferred) {
$deferred->resolve($this);
})
->otherwise(function () use ($deferred) {
$deferred->reject($this);
});
return $deferred->promise();
} else {
return new FulfilledPromise($this);
}
}
/**
* Maps human readable topic to its ID.
*
* @param string $topic
*
* @return string
*/
private function mapTopic($topic)
{
if (array_key_exists($topic, self::TOPIC_TO_ID_ENUM)) {
$result = self::TOPIC_TO_ID_ENUM[$topic];
$this->logger->debug(sprintf('Topic "%s" has been mapped to "%s".', $topic, $result));
} else {
$result = $topic;
$this->logger->debug(sprintf('Topic "%s" does not exist in enum.', $topic));
}
return $result;
}
/**
* Maps topic ID to human readable name.
*
* @param string $topic
*
* @return string
*/
private function unmapTopic($topic)
{
if (array_key_exists($topic, self::ID_TO_TOPIC_ENUM)) {
$result = self::ID_TO_TOPIC_ENUM[$topic];
$this->logger->debug(sprintf('Topic ID "%s" has been unmapped to "%s".', $topic, $result));
} else {
$result = $topic;
$this->logger->debug(sprintf('Topic ID "%s" does not exist in enum.', $topic));
}
return $result;
}
/**
* Publish a message to a topic.
*
* @param string $topic
* @param string $message
* @param int $qosLevel
*
* @return \React\Promise\ExtendedPromiseInterface
*/
private function publish($topic, $message, $qosLevel)
{
$this->logger->info(sprintf('Sending message to topic "%s".', $topic), [$message]);
$topic = $this->mapTopic($topic);
$payload = zlib_encode($message, ZLIB_ENCODING_DEFLATE, 9);
return $this->client->publish(new DefaultMessage($topic, $payload, $qosLevel));
}
/**
* Registers an application.
*
* @param string $packageName
* @param string|int $appId
*
* @return PromiseInterface
*/
public function register($packageName, $appId)
{
$this->logger->info(sprintf('Registering application "%s" (%s).', $packageName, $appId));
$message = json_encode([
'pkg_name' => (string) $packageName,
'appid' => (string) $appId,
]);
return $this->publish(self::REG_REQ_TOPIC, $message, self::QOS_LEVEL);
}
/**
* Checks whether underlying client is connected.
*
* @return bool
*/
public function isConnected()
{
return $this->client->isConnected();
}
}

View File

@@ -0,0 +1,198 @@
<?php
/*
* This file is part of net-mqtt.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\Packet;
use BinSoul\Net\Mqtt\Packet\BasePacket;
use BinSoul\Net\Mqtt\PacketStream;
/**
* Represents the CONNECT packet.
*/
class ConnectRequestPacket extends BasePacket
{
/** @var int */
private $protocolLevel = 3;
/** @var string */
private $protocolName = 'MQTToT';
/** @var int */
private $flags = 194;
/** @var int */
private $keepAlive = 900;
/** @var string */
private $payload;
protected static $packetType = Packet::TYPE_CONNECT;
public function read(PacketStream $stream)
{
parent::read($stream);
$this->assertPacketFlags(0);
$this->assertRemainingPacketLength();
$originalPosition = $stream->getPosition();
$this->protocolName = $stream->readString();
$this->protocolLevel = $stream->readByte();
$this->flags = $stream->readByte();
$this->keepAlive = $stream->readWord();
$payloadLength = $this->remainingPacketLength - ($stream->getPosition() - $originalPosition);
$this->payload = $stream->read($payloadLength);
}
public function write(PacketStream $stream)
{
$data = new PacketStream();
$data->writeString($this->protocolName);
$data->writeByte($this->protocolLevel);
$data->writeByte($this->flags);
$data->writeWord($this->keepAlive);
$data->write($this->payload);
$this->remainingPacketLength = $data->length();
parent::write($stream);
$stream->write($data->getData());
}
/**
* Returns the protocol level.
*
* @return int
*/
public function getProtocolLevel()
{
return $this->protocolLevel;
}
/**
* Sets the protocol level.
*
* @param int $value
*
* @throws \InvalidArgumentException
*/
public function setProtocolLevel($value)
{
if ($value != 3) {
throw new \InvalidArgumentException(sprintf('Unknown protocol level %d.', $value));
}
$this->protocolLevel = $value;
}
/**
* Returns the payload.
*
* @return string
*/
public function getPayload()
{
return $this->payload;
}
/**
* Sets the payload.
*
* @param string $value
*/
public function setPayload($value)
{
$this->payload = $value;
}
/**
* Returns the flags.
*
* @return int
*/
public function getFlags()
{
return $this->flags;
}
/**
* Sets the flags.
*
* @param int $value
*
* @throws \InvalidArgumentException
*/
public function setFlags($value)
{
if ($value > 255) {
throw new \InvalidArgumentException(
sprintf(
'Expected a flags lower than 255 but got %d.',
$value
)
);
}
$this->flags = $value;
}
/**
* Returns the keep alive time in seconds.
*
* @return int
*/
public function getKeepAlive()
{
return $this->keepAlive;
}
/**
* Sets the keep alive time in seconds.
*
* @param int $value
*
* @throws \InvalidArgumentException
*/
public function setKeepAlive($value)
{
if ($value > 65535) {
throw new \InvalidArgumentException(
sprintf(
'Expected a keep alive time lower than 65535 but got %d.',
$value
)
);
}
$this->keepAlive = $value;
}
/**
* Returns the protocol name.
*
* @return string
*/
public function getProtocolName()
{
return $this->protocolName;
}
/**
* Sets the protocol name.
*
* @param string $value
*
* @throws \InvalidArgumentException
*/
public function setProtocolName($value)
{
$this->assertValidStringLength($value, false);
$this->protocolName = $value;
}
}

View File

@@ -0,0 +1,145 @@
<?php
/*
* This file is part of net-mqtt.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\Packet;
use BinSoul\Net\Mqtt\Packet\BasePacket;
use BinSoul\Net\Mqtt\PacketStream;
/**
* Represents the CONNACK packet.
*/
class ConnectResponsePacket extends BasePacket
{
/** @var string[][] */
private static $returnCodes = [
0 => [
'Connection accepted',
'',
],
1 => [
'Unacceptable protocol version',
'The Server does not support the level of the MQTT protocol requested by the client.',
],
2 => [
'Identifier rejected',
'The client identifier is correct UTF-8 but not allowed by the server.',
],
3 => [
'Server unavailable',
'The network connection has been made but the MQTT service is unavailable',
],
4 => [
'Bad user name or password',
'The data in the user name or password is malformed.',
],
5 => [
'Not authorized',
'The client is not authorized to connect.',
],
];
/** @var int */
private $flags = 0;
/** @var int */
private $returnCode;
/** @var string */
private $auth;
protected static $packetType = Packet::TYPE_CONNACK;
public function read(PacketStream $stream)
{
parent::read($stream);
$this->assertPacketFlags(0);
$this->assertRemainingPacketLength();
$originalPosition = $stream->getPosition();
$this->flags = $stream->readByte();
$this->returnCode = $stream->readByte();
$authLength = $this->remainingPacketLength - ($stream->getPosition() - $originalPosition);
if ($authLength) {
$this->auth = $stream->readString();
} else {
$this->auth = '';
}
}
public function write(PacketStream $stream)
{
$data = new PacketStream();
$data->writeByte($this->flags);
$data->writeByte($this->returnCode);
if ($this->auth !== null && strlen($this->auth)) {
$data->writeString($this->auth);
}
$this->remainingPacketLength = $data->length();
parent::write($stream);
$stream->write($data->getData());
}
/**
* Returns the return code.
*
* @return int
*/
public function getReturnCode()
{
return $this->returnCode;
}
/**
* Indicates if the connection was successful.
*
* @return bool
*/
public function isSuccess()
{
return $this->returnCode === 0;
}
/**
* Indicates if the connection failed.
*
* @return bool
*/
public function isError()
{
return $this->returnCode > 0;
}
/**
* Returns a string representation of the returned error code.
*
* @return int
*/
public function getErrorName()
{
if (isset(self::$returnCodes[$this->returnCode])) {
return self::$returnCodes[$this->returnCode][0];
}
return 'Error '.$this->returnCode;
}
/**
* @return string
*/
public function getAuth()
{
return $this->auth;
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of net-mqtt.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\Flow\AbstractFlow;
use BinSoul\Net\Mqtt\Packet;
use BinSoul\Net\Mqtt\Packet\ConnectResponsePacket;
use Fbns\Client\Connection;
/**
* Represents a flow starting with an outgoing CONNECT packet.
*/
class OutgoingConnectFlow extends AbstractFlow
{
const PROTOCOL_LEVEL = 3;
const PROTOCOL_NAME = 'MQTToT';
const KEEPALIVE = 900;
const KEEPALIVE_TIMEOUT = 60;
/** @var Connection */
private $connection;
/**
* Constructs an instance of this class.
*
* @param Connection $connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function getCode()
{
return 'connect';
}
public function start()
{
$packet = new ConnectRequestPacket();
$packet->setProtocolLevel(self::PROTOCOL_LEVEL);
$packet->setProtocolName(self::PROTOCOL_NAME);
$packet->setKeepAlive(self::KEEPALIVE);
$packet->setFlags(194);
$packet->setPayload(zlib_encode($this->connection->toThrift(), ZLIB_ENCODING_DEFLATE, 9));
return $packet;
}
public function accept(Packet $packet)
{
return $packet->getPacketType() === Packet::TYPE_CONNACK;
}
public function next(Packet $packet)
{
/** @var ConnectResponsePacket $packet */
if ($packet->isSuccess()) {
$this->succeed($packet);
} else {
$this->fail($packet->getErrorName());
}
}
}

View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of net-mqtt.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\Exception\UnknownPacketTypeException;
use BinSoul\Net\Mqtt\Packet;
use BinSoul\Net\Mqtt\Packet\DisconnectRequestPacket;
use BinSoul\Net\Mqtt\Packet\PingRequestPacket;
use BinSoul\Net\Mqtt\Packet\PingResponsePacket;
use BinSoul\Net\Mqtt\Packet\PublishCompletePacket;
use BinSoul\Net\Mqtt\Packet\PublishReceivedPacket;
use BinSoul\Net\Mqtt\Packet\PublishReleasePacket;
use BinSoul\Net\Mqtt\Packet\PublishRequestPacket;
use BinSoul\Net\Mqtt\Packet\SubscribeRequestPacket;
use BinSoul\Net\Mqtt\Packet\SubscribeResponsePacket;
use BinSoul\Net\Mqtt\Packet\UnsubscribeRequestPacket;
use BinSoul\Net\Mqtt\Packet\UnsubscribeResponsePacket;
use Fbns\Client\Common\PublishAckPacket;
/**
* Builds instances of the {@see Packet} interface.
*/
class PacketFactory
{
/**
* Map of packet types to packet classes.
*
* @var string[]
*/
private static $mapping = [
Packet::TYPE_CONNECT => ConnectRequestPacket::class,
Packet::TYPE_CONNACK => ConnectResponsePacket::class,
Packet::TYPE_PUBLISH => PublishRequestPacket::class,
Packet::TYPE_PUBACK => PublishAckPacket::class,
Packet::TYPE_PUBREC => PublishReceivedPacket::class,
Packet::TYPE_PUBREL => PublishReleasePacket::class,
Packet::TYPE_PUBCOMP => PublishCompletePacket::class,
Packet::TYPE_SUBSCRIBE => SubscribeRequestPacket::class,
Packet::TYPE_SUBACK => SubscribeResponsePacket::class,
Packet::TYPE_UNSUBSCRIBE => UnsubscribeRequestPacket::class,
Packet::TYPE_UNSUBACK => UnsubscribeResponsePacket::class,
Packet::TYPE_PINGREQ => PingRequestPacket::class,
Packet::TYPE_PINGRESP => PingResponsePacket::class,
Packet::TYPE_DISCONNECT => DisconnectRequestPacket::class,
];
/**
* Builds a packet object for the given type.
*
* @param int $type
*
* @throws UnknownPacketTypeException
*
* @return Packet
*/
public function build($type)
{
if (!isset(self::$mapping[$type])) {
throw new UnknownPacketTypeException(sprintf('Unknown packet type %d.', $type));
}
$class = self::$mapping[$type];
return new $class();
}
}

120
vendor/valga/fbns-react/src/Lite/ReactFlow.php vendored Executable file
View File

@@ -0,0 +1,120 @@
<?php
/*
* This file is part of net-mqtt-client-react.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\Flow;
use BinSoul\Net\Mqtt\Packet;
use React\Promise\Deferred;
/**
* Decorates flows with data required for the {@see ReactMqttClient} class.
*/
class ReactFlow implements Flow
{
/** @var Flow */
private $decorated;
/** @var Deferred */
private $deferred;
/** @var Packet */
private $packet;
/** @var bool */
private $isSilent;
/**
* Constructs an instance of this class.
*
* @param Flow $decorated
* @param Deferred $deferred
* @param Packet $packet
* @param bool $isSilent
*/
public function __construct(Flow $decorated, Deferred $deferred, Packet $packet = null, $isSilent = false)
{
$this->decorated = $decorated;
$this->deferred = $deferred;
$this->packet = $packet;
$this->isSilent = $isSilent;
}
public function getCode()
{
return $this->decorated->getCode();
}
public function start()
{
$this->packet = $this->decorated->start();
return $this->packet;
}
public function accept(Packet $packet)
{
return $this->decorated->accept($packet);
}
public function next(Packet $packet)
{
$this->packet = $this->decorated->next($packet);
return $this->packet;
}
public function isFinished()
{
return $this->decorated->isFinished();
}
public function isSuccess()
{
return $this->decorated->isSuccess();
}
public function getResult()
{
return $this->decorated->getResult();
}
public function getErrorMessage()
{
return $this->decorated->getErrorMessage();
}
/**
* Returns the associated deferred.
*
* @return Deferred
*/
public function getDeferred()
{
return $this->deferred;
}
/**
* Returns the current packet.
*
* @return Packet
*/
public function getPacket()
{
return $this->packet;
}
/**
* Indicates if the flow should emit events.
*
* @return bool
*/
public function isSilent()
{
return $this->isSilent;
}
}

View File

@@ -0,0 +1,695 @@
<?php
/*
* This file is part of net-mqtt-client-react.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\DefaultConnection;
use BinSoul\Net\Mqtt\DefaultIdentifierGenerator;
use BinSoul\Net\Mqtt\DefaultMessage;
use BinSoul\Net\Mqtt\Flow;
use BinSoul\Net\Mqtt\Flow\IncomingPublishFlow;
use BinSoul\Net\Mqtt\Flow\OutgoingDisconnectFlow;
use BinSoul\Net\Mqtt\Flow\OutgoingPingFlow;
use BinSoul\Net\Mqtt\Flow\OutgoingPublishFlow;
use BinSoul\Net\Mqtt\Flow\OutgoingSubscribeFlow;
use BinSoul\Net\Mqtt\Flow\OutgoingUnsubscribeFlow;
use BinSoul\Net\Mqtt\IdentifierGenerator;
use BinSoul\Net\Mqtt\Message;
use BinSoul\Net\Mqtt\Packet;
use BinSoul\Net\Mqtt\Packet\PublishRequestPacket;
use BinSoul\Net\Mqtt\StreamParser;
use BinSoul\Net\Mqtt\Subscription;
use Evenement\EventEmitter;
use Fbns\Client\Connection;
use React\EventLoop\LoopInterface;
use React\EventLoop\Timer\TimerInterface;
use React\Promise\Deferred;
use React\Promise\ExtendedPromiseInterface;
use React\Promise\RejectedPromise;
use React\Socket\ConnectorInterface;
use React\Stream\DuplexStreamInterface;
/**
* Connects to a MQTT broker and subscribes to topics or publishes messages.
*
* The following events are emitted:
* - open - The network connection to the server is established.
* - close - The network connection to the server is closed.
* - warning - An event of severity "warning" occurred.
* - error - An event of severity "error" occurred.
*
* If a flow finishes it's result is also emitted, e.g.:
* - connect - The client connected to the broker.
* - disconnect - The client disconnected from the broker.
* - subscribe - The client subscribed to a topic filter.
* - unsubscribe - The client unsubscribed from topic filter.
* - publish - A message was published.
* - message - A message was received.
*/
class ReactMqttClient extends EventEmitter
{
/** @var ConnectorInterface */
private $connector;
/** @var LoopInterface */
private $loop;
/** @var DuplexStreamInterface */
private $stream;
/** @var StreamParser */
private $parser;
/** @var IdentifierGenerator */
private $identifierGenerator;
/** @var string */
private $host;
/** @var int */
private $port;
/** @var Connection */
private $connection;
/** @var bool */
private $isConnected = false;
/** @var bool */
private $isConnecting = false;
/** @var bool */
private $isDisconnecting = false;
/** @var TimerInterface[] */
private $timer = [];
/** @var ReactFlow[] */
private $receivingFlows = [];
/** @var ReactFlow[] */
private $sendingFlows = [];
/** @var ReactFlow */
private $writtenFlow;
/**
* Constructs an instance of this class.
*
* @param ConnectorInterface $connector
* @param LoopInterface $loop
* @param IdentifierGenerator $identifierGenerator
* @param StreamParser $parser
*/
public function __construct(
ConnectorInterface $connector,
LoopInterface $loop,
IdentifierGenerator $identifierGenerator = null,
StreamParser $parser = null
) {
$this->connector = $connector;
$this->loop = $loop;
$this->parser = $parser;
if ($this->parser === null) {
$this->parser = new StreamParser();
}
$this->parser->onError(function (\Exception $e) {
$this->emitWarning($e);
});
$this->identifierGenerator = $identifierGenerator;
if ($this->identifierGenerator === null) {
$this->identifierGenerator = new DefaultIdentifierGenerator();
}
}
/**
* Return the host.
*
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* Return the port.
*
* @return string
*/
public function getPort()
{
return $this->port;
}
/**
* Indicates if the client is connected.
*
* @return bool
*/
public function isConnected()
{
return $this->isConnected;
}
/**
* Returns the underlying stream or null if the client is not connected.
*
* @return DuplexStreamInterface|null
*/
public function getStream()
{
return $this->stream;
}
/**
* Connects to a broker.
*
* @param string $host
* @param int $port
* @param Connection $connection
* @param int $timeout
*
* @return ExtendedPromiseInterface
*/
public function connect($host, $port, Connection $connection, $timeout = 5)
{
if ($this->isConnected || $this->isConnecting) {
return new RejectedPromise(new \LogicException('The client is already connected.'));
}
$this->isConnecting = true;
$this->isConnected = false;
$this->host = $host;
$this->port = $port;
$deferred = new Deferred();
$this->establishConnection($this->host, $this->port, $timeout)
->then(function (DuplexStreamInterface $stream) use ($connection, $deferred, $timeout) {
$this->stream = $stream;
$this->emit('open', [$connection, $this]);
$this->registerClient($connection, $timeout)
->then(function (ConnectResponsePacket $responsePacket) use ($deferred, $connection) {
$this->isConnecting = false;
$this->isConnected = true;
$this->connection = $connection;
$this->emit('connect', [$responsePacket, $this]);
$deferred->resolve($responsePacket);
})
->otherwise(function (\Exception $e) use ($deferred, $connection) {
$this->isConnecting = false;
$this->emitError($e);
$deferred->reject($e);
if ($this->stream !== null) {
$this->stream->close();
}
$this->emit('close', [$connection, $this]);
});
})
->otherwise(function (\Exception $e) use ($deferred) {
$this->isConnecting = false;
$this->emitError($e);
$deferred->reject($e);
});
return $deferred->promise();
}
/**
* Disconnects from a broker.
*
* @return ExtendedPromiseInterface
*/
public function disconnect()
{
if (!$this->isConnected || $this->isDisconnecting) {
return new RejectedPromise(new \LogicException('The client is not connected.'));
}
$this->isDisconnecting = true;
$deferred = new Deferred();
$connection = new DefaultConnection();
$this->startFlow(new OutgoingDisconnectFlow($connection), true)
->then(function () use ($connection, $deferred) {
$this->isDisconnecting = false;
$this->isConnected = false;
$this->emit('disconnect', [$connection, $this]);
$deferred->resolve($connection);
if ($this->stream !== null) {
$this->stream->close();
}
})
->otherwise(function () use ($deferred) {
$this->isDisconnecting = false;
$deferred->reject($this->connection);
});
return $deferred->promise();
}
/**
* Subscribes to a topic filter.
*
* @param Subscription $subscription
*
* @return ExtendedPromiseInterface
*/
public function subscribe(Subscription $subscription)
{
if (!$this->isConnected) {
return new RejectedPromise(new \LogicException('The client is not connected.'));
}
return $this->startFlow(new OutgoingSubscribeFlow([$subscription], $this->identifierGenerator));
}
/**
* Unsubscribes from a topic filter.
*
* @param Subscription $subscription
*
* @return ExtendedPromiseInterface
*/
public function unsubscribe(Subscription $subscription)
{
if (!$this->isConnected) {
return new RejectedPromise(new \LogicException('The client is not connected.'));
}
return $this->startFlow(new OutgoingUnsubscribeFlow([$subscription], $this->identifierGenerator));
}
/**
* Publishes a message.
*
* @param Message $message
*
* @return ExtendedPromiseInterface
*/
public function publish(Message $message)
{
if (!$this->isConnected) {
return new RejectedPromise(new \LogicException('The client is not connected.'));
}
return $this->startFlow(new OutgoingPublishFlow($message, $this->identifierGenerator));
}
/**
* Calls the given generator periodically and publishes the return value.
*
* @param int $interval
* @param Message $message
* @param callable $generator
*
* @return ExtendedPromiseInterface
*/
public function publishPeriodically($interval, Message $message, callable $generator)
{
if (!$this->isConnected) {
return new RejectedPromise(new \LogicException('The client is not connected.'));
}
$deferred = new Deferred();
$this->timer[] = $this->loop->addPeriodicTimer(
$interval,
function () use ($message, $generator, $deferred) {
$this->publish($message->withPayload($generator($message->getTopic())))->then(
function ($value) use ($deferred) {
$deferred->notify($value);
},
function (\Exception $e) use ($deferred) {
$deferred->reject($e);
}
);
}
);
return $deferred->promise();
}
/**
* Emits warnings.
*
* @param \Exception $e
*/
private function emitWarning(\Exception $e)
{
$this->emit('warning', [$e, $this]);
}
/**
* Emits errors.
*
* @param \Exception $e
*/
private function emitError(\Exception $e)
{
$this->emit('error', [$e, $this]);
}
/**
* Establishes a network connection to a server.
*
* @param string $host
* @param int $port
* @param int $timeout
*
* @return ExtendedPromiseInterface
*/
private function establishConnection($host, $port, $timeout)
{
$deferred = new Deferred();
$timer = $this->loop->addTimer(
$timeout,
function () use ($deferred, $timeout) {
$exception = new \RuntimeException(sprintf('Connection timed out after %d seconds.', $timeout));
$deferred->reject($exception);
}
);
$this->connector->connect($host.':'.$port)
->always(function () use ($timer) {
$this->loop->cancelTimer($timer);
})
->then(function (DuplexStreamInterface $stream) use ($deferred) {
$stream->on('data', function ($data) {
$this->handleReceive($data);
});
$stream->on('close', function () {
$this->handleClose();
});
$stream->on('error', function (\Exception $e) {
$this->handleError($e);
});
$deferred->resolve($stream);
})
->otherwise(function (\Exception $e) use ($deferred) {
$deferred->reject($e);
});
return $deferred->promise();
}
/**
* Registers a new client with the broker.
*
* @param Connection $connection
* @param int $timeout
*
* @return ExtendedPromiseInterface
*/
private function registerClient(Connection $connection, $timeout)
{
$deferred = new Deferred();
$responseTimer = $this->loop->addTimer(
$timeout,
function () use ($deferred, $timeout) {
$exception = new \RuntimeException(sprintf('No response after %d seconds.', $timeout));
$deferred->reject($exception);
}
);
$this->startFlow(new OutgoingConnectFlow($connection), true)
->always(function () use ($responseTimer) {
$this->loop->cancelTimer($responseTimer);
})->then(function (ConnectResponsePacket $responsePacket) use ($deferred) {
$this->timer[] = $this->loop->addPeriodicTimer(
OutgoingConnectFlow::KEEPALIVE - OutgoingConnectFlow::KEEPALIVE_TIMEOUT,
function () {
$this->startFlow(new OutgoingPingFlow());
}
);
$deferred->resolve($responsePacket);
})->otherwise(function (\Exception $e) use ($deferred) {
$deferred->reject($e);
});
return $deferred->promise();
}
/**
* Handles incoming data.
*
* @param string $data
*/
private function handleReceive($data)
{
if (!$this->isConnected && !$this->isConnecting) {
return;
}
$flowCount = count($this->receivingFlows);
$packets = $this->parser->push($data);
foreach ($packets as $packet) {
$this->handlePacket($packet);
}
if ($flowCount > count($this->receivingFlows)) {
$this->receivingFlows = array_values($this->receivingFlows);
}
$this->handleSend();
}
/**
* Handles an incoming packet.
*
* @param Packet $packet
*/
private function handlePacket(Packet $packet)
{
switch ($packet->getPacketType()) {
case Packet::TYPE_PUBLISH:
/* @var PublishRequestPacket $packet */
$message = new DefaultMessage(
$packet->getTopic(),
$packet->getPayload(),
$packet->getQosLevel(),
$packet->isRetained(),
$packet->isDuplicate()
);
$this->startFlow(new IncomingPublishFlow($message, $packet->getIdentifier()));
break;
case Packet::TYPE_CONNACK:
case Packet::TYPE_PINGRESP:
case Packet::TYPE_SUBACK:
case Packet::TYPE_UNSUBACK:
case Packet::TYPE_PUBREL:
case Packet::TYPE_PUBACK:
case Packet::TYPE_PUBREC:
case Packet::TYPE_PUBCOMP:
$flowFound = false;
foreach ($this->receivingFlows as $index => $flow) {
if ($flow->accept($packet)) {
$flowFound = true;
unset($this->receivingFlows[$index]);
$this->continueFlow($flow, $packet);
break;
}
}
if (!$flowFound) {
$this->emitWarning(
new \LogicException(sprintf('Received unexpected packet of type %d.', $packet->getPacketType()))
);
}
break;
default:
$this->emitWarning(
new \LogicException(sprintf('Cannot handle packet of type %d.', $packet->getPacketType()))
);
}
}
/**
* Handles outgoing packets.
*/
private function handleSend()
{
$flow = null;
if ($this->writtenFlow !== null) {
$flow = $this->writtenFlow;
$this->writtenFlow = null;
}
if (count($this->sendingFlows) > 0) {
$this->writtenFlow = array_shift($this->sendingFlows);
$this->stream->write($this->writtenFlow->getPacket());
}
if ($flow !== null) {
if ($flow->isFinished()) {
$this->loop->nextTick(function () use ($flow) {
$this->finishFlow($flow);
});
} else {
$this->receivingFlows[] = $flow;
}
}
}
/**
* Handles closing of the stream.
*/
private function handleClose()
{
foreach ($this->timer as $timer) {
$this->loop->cancelTimer($timer);
}
$this->timer = [];
$this->cleanPreviousSession();
$connection = $this->connection;
$this->isConnecting = false;
$this->isDisconnecting = false;
$this->isConnected = false;
$this->connection = null;
$this->stream = null;
if ($connection !== null) {
$this->emit('close', [$connection, $this]);
}
}
/**
* Handles errors of the stream.
*
* @param \Exception $e
*/
private function handleError(\Exception $e)
{
$this->emitError($e);
}
/**
* Starts the given flow.
*
* @param Flow $flow
* @param bool $isSilent
*
* @return ExtendedPromiseInterface
*/
private function startFlow(Flow $flow, $isSilent = false)
{
try {
$packet = $flow->start();
} catch (\Exception $e) {
$this->emitError($e);
return new RejectedPromise($e);
}
$deferred = new Deferred();
$internalFlow = new ReactFlow($flow, $deferred, $packet, $isSilent);
if ($packet !== null) {
if ($this->writtenFlow !== null) {
$this->sendingFlows[] = $internalFlow;
} else {
$this->stream->write($packet);
$this->writtenFlow = $internalFlow;
$this->handleSend();
}
} else {
$this->loop->nextTick(function () use ($internalFlow) {
$this->finishFlow($internalFlow);
});
}
return $deferred->promise();
}
/**
* Continues the given flow.
*
* @param ReactFlow $flow
* @param Packet $packet
*/
private function continueFlow(ReactFlow $flow, Packet $packet)
{
try {
$response = $flow->next($packet);
} catch (\Exception $e) {
$this->emitError($e);
return;
}
if ($response !== null) {
if ($this->writtenFlow !== null) {
$this->sendingFlows[] = $flow;
} else {
$this->stream->write($response);
$this->writtenFlow = $flow;
$this->handleSend();
}
} elseif ($flow->isFinished()) {
$this->loop->nextTick(function () use ($flow) {
$this->finishFlow($flow);
});
}
}
/**
* Finishes the given flow.
*
* @param ReactFlow $flow
*/
private function finishFlow(ReactFlow $flow)
{
if ($flow->isSuccess()) {
if (!$flow->isSilent()) {
$this->emit($flow->getCode(), [$flow->getResult(), $this]);
}
$flow->getDeferred()->resolve($flow->getResult());
} else {
$result = new \RuntimeException($flow->getErrorMessage());
$this->emitWarning($result);
$flow->getDeferred()->reject($result);
}
}
/**
* Cleans previous session by rejecting all pending flows.
*/
private function cleanPreviousSession()
{
$error = new \RuntimeException('Connection has been closed.');
foreach ($this->receivingFlows as $receivingFlow) {
$receivingFlow->getDeferred()->reject($error);
}
$this->receivingFlows = [];
foreach ($this->sendingFlows as $sendingFlow) {
$sendingFlow->getDeferred()->reject($error);
}
$this->sendingFlows = [];
}
}

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of net-mqtt.
*
* Copyright (c) 2015 Sebastian Mößler code@binsoul.de
*
* This source file is subject to the MIT license.
*/
namespace Fbns\Client\Lite;
use BinSoul\Net\Mqtt\Exception\EndOfStreamException;
use BinSoul\Net\Mqtt\Exception\MalformedPacketException;
use BinSoul\Net\Mqtt\Exception\UnknownPacketTypeException;
use BinSoul\Net\Mqtt\Packet;
use BinSoul\Net\Mqtt\PacketStream;
use BinSoul\Net\Mqtt\StreamParser as BaseStreamParser;
/**
* Provides methods to parse a stream of bytes into packets.
*/
class StreamParser extends BaseStreamParser
{
/** @var PacketStream */
private $buffer;
/** @var PacketFactory */
private $factory;
/** @var callable */
private $errorCallback;
/**
* Constructs an instance of this class.
*/
public function __construct()
{
parent::__construct();
$this->buffer = new PacketStream();
$this->factory = new PacketFactory();
}
/**
* Registers an error callback.
*
* @param callable $callback
*/
public function onError($callback)
{
$this->errorCallback = $callback;
}
/**
* Appends the given data to the internal buffer and parses it.
*
* @param string $data
*
* @return Packet[]
*/
public function push($data)
{
$this->buffer->write($data);
$result = [];
while ($this->buffer->getRemainingBytes() > 0) {
$type = $this->buffer->readByte() >> 4;
try {
$packet = $this->factory->build($type);
} catch (UnknownPacketTypeException $e) {
$this->handleError($e);
continue;
}
$this->buffer->seek(-1);
$position = $this->buffer->getPosition();
try {
$packet->read($this->buffer);
$result[] = $packet;
$this->buffer->cut();
} catch (EndOfStreamException $e) {
$this->buffer->setPosition($position);
break;
} catch (MalformedPacketException $e) {
$this->handleError($e);
}
}
return $result;
}
/**
* Executes the registered error callback.
*
* @param \Throwable $exception
*/
private function handleError($exception)
{
if ($this->errorCallback !== null) {
$callback = $this->errorCallback;
$callback($exception);
}
}
}

153
vendor/valga/fbns-react/src/Message/Push.php vendored Executable file
View File

@@ -0,0 +1,153 @@
<?php
namespace Fbns\Client\Message;
use Fbns\Client\Json;
class Push
{
/**
* @var string
*/
private $json;
/**
* @var string
*/
private $token;
/**
* @var string
*/
private $connectionKey;
/**
* @var string
*/
private $packageName;
/**
* @var string
*/
private $collapseKey;
/**
* @var string
*/
private $payload;
/**
* @var string
*/
private $notificationId;
/**
* @var string
*/
private $isBuffered;
/**
* @param string $json
*/
private function parseJson($json)
{
$data = Json::decode($json);
$this->json = $json;
if (isset($data->token)) {
$this->token = (string) $data->token;
}
if (isset($data->ck)) {
$this->connectionKey = (string) $data->ck;
}
if (isset($data->pn)) {
$this->packageName = (string) $data->pn;
}
if (isset($data->cp)) {
$this->collapseKey = (string) $data->cp;
}
if (isset($data->fbpushnotif)) {
$this->payload = (string) $data->fbpushnotif;
}
if (isset($data->nid)) {
$this->notificationId = (string) $data->nid;
}
if (isset($data->bu)) {
$this->isBuffered = (string) $data->bu;
}
}
/**
* Message constructor.
*
* @param string $json
*/
public function __construct($json)
{
$this->parseJson($json);
}
/**
* @return string
*/
public function __toString()
{
return $this->json;
}
/**
* @return string
*/
public function getToken()
{
return $this->token;
}
/**
* @return string
*/
public function getConnectionKey()
{
return $this->connectionKey;
}
/**
* @return string
*/
public function getPackageName()
{
return $this->packageName;
}
/**
* @return string
*/
public function getCollapseKey()
{
return $this->collapseKey;
}
/**
* @return string
*/
public function getPayload()
{
return $this->payload;
}
/**
* @return string
*/
public function getNotificationId()
{
return $this->notificationId;
}
/**
* @return string
*/
public function getIsBuffered()
{
return $this->isBuffered;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Fbns\Client\Message;
use Fbns\Client\Json;
class Register
{
/**
* @var string
*/
private $json;
/**
* @var string
*/
private $packageName;
/**
* @var string
*/
private $token;
/**
* @var string
*/
private $error;
/**
* @param string $json
*/
private function parseJson($json)
{
$data = Json::decode($json);
$this->json = $json;
if (isset($data->pkg_name)) {
$this->packageName = (string) $data->pkg_name;
}
if (isset($data->token)) {
$this->token = (string) $data->token;
}
if (isset($data->error)) {
$this->error = (string) $data->error;
}
}
/**
* Message constructor.
*
* @param string $json
*/
public function __construct($json)
{
$this->parseJson($json);
}
/**
* @return string
*/
public function __toString()
{
return $this->json;
}
/**
* @return string
*/
public function getPackageName()
{
return $this->packageName;
}
/**
* @return string
*/
public function getToken()
{
return $this->token;
}
/**
* @return string
*/
public function getError()
{
return $this->error;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Fbns\Client\Thrift;
/**
* @see https://thrift.apache.org/
*/
class Compact
{
const TYPE_STOP = 0x00;
const TYPE_TRUE = 0x01;
const TYPE_FALSE = 0x02;
const TYPE_BYTE = 0x03;
const TYPE_I16 = 0x04;
const TYPE_I32 = 0x05;
const TYPE_I64 = 0x06;
const TYPE_DOUBLE = 0x07;
const TYPE_BINARY = 0x08;
const TYPE_LIST = 0x09;
const TYPE_SET = 0x0A;
const TYPE_MAP = 0x0B;
const TYPE_STRUCT = 0x0C;
const TYPE_FLOAT = 0x0D;
}

52
vendor/valga/fbns-react/src/Thrift/Debug.php vendored Executable file
View File

@@ -0,0 +1,52 @@
<?php
namespace Fbns\Client\Thrift;
class Debug extends Reader
{
/**
* @param string $context
* @param int $field
* @param mixed $value
* @param int $type
*/
private function handler($context, $field, $value, $type)
{
if (strlen($context)) {
$field = $context.'/'.$field;
}
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
} elseif (is_array($value)) {
$value = array_map(function ($value) {
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
} elseif (is_string($value)) {
$value = '"'.$value.'"';
} else {
$value = (string) $value;
}
return $value;
}, $value);
$value = '['.implode(', ', $value).']';
} elseif (is_string($value)) {
$value = '"'.$value.'"';
} else {
$value = (string) $value;
}
printf('%s (%02x): %s%s', $field, $type, $value, PHP_EOL);
}
/**
* Debug constructor.
*
* @param string $buffer
*/
public function __construct($buffer = '')
{
parent::__construct($buffer, function ($context, $field, $value, $type) {
$this->handler($context, $field, $value, $type);
});
}
}

263
vendor/valga/fbns-react/src/Thrift/Reader.php vendored Executable file
View File

@@ -0,0 +1,263 @@
<?php
namespace Fbns\Client\Thrift;
/**
* WARNING: This implementation is not complete.
*
* @see https://thrift.apache.org/
*/
class Reader
{
/**
* @var int[]
*/
private $stack;
/**
* @var int
*/
private $field;
/**
* @var string
*/
private $buffer;
/**
* @var int
*/
private $length;
/**
* @var int
*/
private $position;
/**
* @var callable|null
*/
private $handler;
/**
* Reader constructor.
*
* @param string $buffer
* @param callable|null $handler
*/
public function __construct($buffer = '', callable $handler = null)
{
if (PHP_INT_SIZE === 4 && !extension_loaded('gmp')) {
throw new \RuntimeException('You need to install GMP extension to run this code with x86 PHP build.');
}
$this->buffer = $buffer;
$this->position = 0;
$this->length = strlen($buffer);
$this->field = 0;
$this->stack = [];
$this->handler = $handler;
$this->parse();
}
/**
* Parser.
*/
private function parse()
{
$context = '';
while ($this->position < $this->length) {
$type = $this->readField();
switch ($type) {
case Compact::TYPE_STRUCT:
array_push($this->stack, $this->field);
$this->field = 0;
$context = implode('/', $this->stack);
break;
case Compact::TYPE_STOP:
if (!count($this->stack)) {
return;
}
$this->field = array_pop($this->stack);
$context = implode('/', $this->stack);
break;
case Compact::TYPE_LIST:
$sizeAndType = $this->readUnsignedByte();
$size = $sizeAndType >> 4;
$listType = $sizeAndType & 0x0f;
if ($size === 0x0f) {
$size = $this->readVarint();
}
$this->handleField($context, $this->field, $this->readList($size, $listType), $listType);
break;
case Compact::TYPE_TRUE:
case Compact::TYPE_FALSE:
$this->handleField($context, $this->field, $type === Compact::TYPE_TRUE, $type);
break;
case Compact::TYPE_BYTE:
$this->handleField($context, $this->field, $this->readSignedByte(), $type);
break;
case Compact::TYPE_I16:
case Compact::TYPE_I32:
case Compact::TYPE_I64:
$this->handleField($context, $this->field, $this->fromZigZag($this->readVarint()), $type);
break;
case Compact::TYPE_BINARY:
$this->handleField($context, $this->field, $this->readString($this->readVarint()), $type);
break;
}
}
}
/**
* @param int $size
* @param int $type
*
* @return array
*/
private function readList($size, $type)
{
$result = [];
switch ($type) {
case Compact::TYPE_TRUE:
case Compact::TYPE_FALSE:
for ($i = 0; $i < $size; $i++) {
$result[] = $this->readSignedByte() === Compact::TYPE_TRUE;
}
break;
case Compact::TYPE_BYTE:
for ($i = 0; $i < $size; $i++) {
$result[] = $this->readSignedByte();
}
break;
case Compact::TYPE_I16:
case Compact::TYPE_I32:
case Compact::TYPE_I64:
for ($i = 0; $i < $size; $i++) {
$result[] = $this->fromZigZag($this->readVarint());
}
break;
case Compact::TYPE_BINARY:
$result[] = $this->readString($this->readVarint());
break;
}
return $result;
}
/**
* @return int
*/
private function readField()
{
$typeAndDelta = ord($this->buffer[$this->position++]);
if ($typeAndDelta === Compact::TYPE_STOP) {
return Compact::TYPE_STOP;
}
$delta = $typeAndDelta >> 4;
if ($delta === 0) {
$this->field = $this->fromZigZag($this->readVarint());
} else {
$this->field += $delta;
}
$type = $typeAndDelta & 0x0f;
return $type;
}
/**
* @return int
*/
private function readSignedByte()
{
$result = $this->readUnsignedByte();
if ($result > 0x7f) {
$result = 0 - (($result - 1) ^ 0xff);
}
return $result;
}
/**
* @return int
*/
private function readUnsignedByte()
{
return ord($this->buffer[$this->position++]);
}
/**
* @return int
*/
private function readVarint()
{
$shift = 0;
$result = 0;
if (PHP_INT_SIZE === 4) {
$result = gmp_init($result, 10);
}
while ($this->position < $this->length) {
$byte = ord($this->buffer[$this->position++]);
if (PHP_INT_SIZE === 4) {
$byte = gmp_init($byte, 10);
}
$result |= ($byte & 0x7f) << $shift;
if (PHP_INT_SIZE === 4) {
$byte = (int) gmp_strval($byte, 10);
}
if ($byte >> 7 === 0) {
break;
}
$shift += 7;
}
if (PHP_INT_SIZE === 4) {
$result = gmp_strval($result, 10);
}
return $result;
}
/**
* @param int $n
*
* @return int
*/
private function fromZigZag($n)
{
if (PHP_INT_SIZE === 4) {
$n = gmp_init($n, 10);
}
$result = ($n >> 1) ^ -($n & 1);
if (PHP_INT_SIZE === 4) {
$result = gmp_strval($result, 10);
}
return $result;
}
/**
* @param int $length
*
* @return string
*/
private function readString($length)
{
$result = substr($this->buffer, $this->position, $length);
$this->position += $length;
return $result;
}
/**
* @param string $context
* @param int $field
* @param mixed $value
* @param int $type
*/
private function handleField($context, $field, $value, $type)
{
if (!is_callable($this->handler)) {
return;
}
call_user_func($this->handler, $context, $field, $value, $type);
}
}

279
vendor/valga/fbns-react/src/Thrift/Writer.php vendored Executable file
View File

@@ -0,0 +1,279 @@
<?php
namespace Fbns\Client\Thrift;
/**
* WARNING: This implementation is not complete.
*
* @see https://thrift.apache.org/
*/
class Writer
{
/**
* @var string
*/
private $buffer;
/**
* @var int
*/
private $field;
/**
* @var int[]
*/
private $stack;
/**
* @param int $number
* @param int $bits
*
* @return int
*/
private function toZigZag($number, $bits)
{
if (PHP_INT_SIZE === 4) {
$number = gmp_init($number, 10);
}
$result = ($number << 1) ^ ($number >> ($bits - 1));
if (PHP_INT_SIZE === 4) {
$result = gmp_strval($result, 10);
}
return $result;
}
/**
* @param int $number
*/
private function writeByte($number)
{
$this->buffer .= chr($number);
}
/**
* @param int $number
*/
private function writeWord($number)
{
$this->writeVarint($this->toZigZag($number, 16));
}
/**
* @param int $number
*/
private function writeInt($number)
{
$this->writeVarint($this->toZigZag($number, 32));
}
/**
* @param int $number
*/
private function writeLongInt($number)
{
$this->writeVarint($this->toZigZag($number, 64));
}
/**
* @param int $field
* @param int $type
*/
private function writeField($field, $type)
{
$delta = $field - $this->field;
if ((0 < $delta) && ($delta <= 15)) {
$this->writeByte(($delta << 4) | $type);
} else {
$this->writeByte($type);
$this->writeWord($field);
}
$this->field = $field;
}
/**
* @param int $number
*/
private function writeVarint($number)
{
if (PHP_INT_SIZE === 4) {
$number = gmp_init($number, 10);
}
while (true) {
$byte = $number & (~0x7f);
if (PHP_INT_SIZE === 4) {
$byte = (int) gmp_strval($byte, 10);
}
if ($byte === 0) {
if (PHP_INT_SIZE === 4) {
$number = (int) gmp_strval($number, 10);
}
$this->buffer .= chr($number);
break;
} else {
$byte = ($number & 0xff) | 0x80;
if (PHP_INT_SIZE === 4) {
$byte = (int) gmp_strval($byte, 10);
}
$this->buffer .= chr($byte);
$number = $number >> 7;
}
}
}
/**
* @param string $data
*/
private function writeBinary($data)
{
$this->buffer .= $data;
}
/**
* @param int $field
* @param bool $value
*/
public function writeBool($field, $value)
{
$this->writeField($field, $value ? Compact::TYPE_TRUE : Compact::TYPE_FALSE);
}
/**
* @param int $field
* @param string $string
*/
public function writeString($field, $string)
{
$this->writeField($field, Compact::TYPE_BINARY);
$this->writeVarint(strlen($string));
$this->writeBinary($string);
}
public function writeStop()
{
$this->buffer .= chr(Compact::TYPE_STOP);
if (count($this->stack)) {
$this->field = array_pop($this->stack);
}
}
/**
* @param int $field
* @param int $number
*/
public function writeInt8($field, $number)
{
$this->writeField($field, Compact::TYPE_BYTE);
$this->writeByte($number);
}
/**
* @param int $field
* @param int $number
*/
public function writeInt16($field, $number)
{
$this->writeField($field, Compact::TYPE_I16);
$this->writeWord($number);
}
/**
* @param int $field
* @param int $number
*/
public function writeInt32($field, $number)
{
$this->writeField($field, Compact::TYPE_I32);
$this->writeInt($number);
}
/**
* @param int $field
* @param int $number
*/
public function writeInt64($field, $number)
{
$this->writeField($field, Compact::TYPE_I64);
$this->writeLongInt($number);
}
/**
* @param int $field
* @param int $type
* @param array $list
*/
public function writeList($field, $type, array $list)
{
$this->writeField($field, Compact::TYPE_LIST);
$size = count($list);
if ($size < 0x0f) {
$this->writeByte(($size << 4) | $type);
} else {
$this->writeByte(0xf0 | $type);
$this->writeVarint($size);
}
switch ($type) {
case Compact::TYPE_TRUE:
case Compact::TYPE_FALSE:
foreach ($list as $value) {
$this->writeByte($value ? Compact::TYPE_TRUE : Compact::TYPE_FALSE);
}
break;
case Compact::TYPE_BYTE:
foreach ($list as $number) {
$this->writeByte($number);
}
break;
case Compact::TYPE_I16:
foreach ($list as $number) {
$this->writeWord($number);
}
break;
case Compact::TYPE_I32:
foreach ($list as $number) {
$this->writeInt($number);
}
break;
case Compact::TYPE_I64:
foreach ($list as $number) {
$this->writeLongInt($number);
}
break;
case Compact::TYPE_BINARY:
foreach ($list as $string) {
$this->writeVarint(strlen($string));
$this->writeBinary($string);
}
break;
}
}
/**
* @param int $field
*/
public function writeStruct($field)
{
$this->writeField($field, Compact::TYPE_STRUCT);
$this->stack[] = $this->field;
$this->field = 0;
}
public function __construct()
{
if (PHP_INT_SIZE === 4 && !extension_loaded('gmp')) {
throw new \RuntimeException('You need to install GMP extension to run this code with x86 PHP build.');
}
$this->buffer = '';
$this->field = 0;
$this->stack = [];
}
/**
* @return string
*/
public function __toString()
{
return $this->buffer;
}
}