Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/coverage/coverage-badge.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions src/Adapter/MockServerQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PlanetTeamSpeak\TeamSpeak3Framework\Exception\TransportException;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Profiler;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\StringHelper;
use PlanetTeamSpeak\TeamSpeak3Framework\TeamSpeak3;
use PlanetTeamSpeak\TeamSpeak3Framework\Transport\MockTCP;

Expand All @@ -27,11 +28,77 @@ protected function syn(): void
Profiler::init(spl_object_hash($this));

$rdy = $this->getTransport()->readLine();
$rdy = StringHelper::factory($rdy ?? '');

if (! $rdy->startsWith(TeamSpeak3::TS3_PROTO_IDENT) && ! $rdy->startsWith(TeamSpeak3::TEA_PROTO_IDENT)) {
throw new AdapterException('invalid reply from the server ('.$rdy.')');
}

Signal::getInstance()->emit('serverqueryConnected', $this);
}

/**
* Return the underlying MockTCP transport.
*/
public function getMockTransport(): MockTCP
{
/** @var MockTCP $transport */
$transport = $this->getTransport();

return $transport;
}

/**
* Register a custom response on the mock transport.
*/
public function registerResponse(string $cmd, string $reply): self
{
$this->getMockTransport()->registerResponse($cmd, $reply);

return $this;
}

/**
* Queue a response on the mock transport.
*/
public function queueResponse(string $reply): self
{
$this->getMockTransport()->queueResponse($reply);

return $this;
}

/**
* Set a custom dynamic command handler.
*/
public function setCommandHandler(callable $handler): self
{
$this->getMockTransport()->setCommandHandler($handler);

return $this;
}

/**
* Get sent commands.
*/
public function getSentCommands(): array
{
return $this->getMockTransport()->getSentCommands();
}

/**
* Get the last sent command.
*/
public function getLastCommand(): ?string
{
return $this->getMockTransport()->getLastCommand();
}

/**
* Check if a command was sent.
*/
public function hasSentCommand(string $command): bool
{
return $this->getMockTransport()->hasSentCommand($command);
}
}
36 changes: 34 additions & 2 deletions src/Helper/Char.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,23 +162,55 @@ public function toAscii(): int
*/
public function toUnicode(): int
{
$length = strlen($this->char);
$h = ord($this->char[0]);

if ($h <= 0x7F) {
return $h;
return $length === 1 ? $h : -1;
} elseif ($h < 0xC2) {
return -1;
} elseif ($h <= 0xDF) {
if ($length !== 2 || ! $this->isContinuationByte(1)) {
return -1;
}

return ($h & 0x1F) << 6 | (ord($this->char[1]) & 0x3F);
} elseif ($h <= 0xEF) {
if ($length !== 3 || ! $this->isContinuationByte(1) || ! $this->isContinuationByte(2)) {
return -1;
}

return ($h & 0x0F) << 12 | (ord($this->char[1]) & 0x3F) << 6 | (ord($this->char[2]) & 0x3F);
} elseif ($h <= 0xF4) {
return ($h & 0x0F) << 18 | (ord($this->char[1]) & 0x3F) << 12 | (ord($this->char[2]) & 0x3F) << 6 | (ord($this->char[3]) & 0x3F);
if (
$length !== 4
|| ! $this->isContinuationByte(1)
|| ! $this->isContinuationByte(2)
|| ! $this->isContinuationByte(3)
) {
return -1;
}

return ($h & 0x07) << 18
| (ord($this->char[1]) & 0x3F) << 12
| (ord($this->char[2]) & 0x3F) << 6
| (ord($this->char[3]) & 0x3F);
} else {
return -1;
}
}

private function isContinuationByte(int $offset): bool
{
if (! isset($this->char[$offset])) {
return false;
}

$byte = ord($this->char[$offset]);

return $byte >= 0x80 && $byte <= 0xBF;
}

/**
* Returns the hexadecimal value of the char.
*
Expand Down
4 changes: 4 additions & 0 deletions src/Helper/Signal.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ public function emit(string $signal, mixed $params = null): mixed
*/
public function getCallbackHash(mixed $callback): string
{
if ($callback instanceof \Closure) {
return spl_object_hash($callback);
}

if (! is_callable($callback, true, $callable_name)) {
throw new SignalException('invalid callback specified');
}
Expand Down
2 changes: 1 addition & 1 deletion src/Node/ChannelGroup.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public function clientList(int $cid = null, int $cldbid = null, bool $resolve =
*/
public function privilegeKeyCreate(int $cid, string $description = null, string $customset = null): StringHelper
{
return $this->getParent()->privilegeKeyCreate($this->getId(), TeamSpeak3::TOKEN_CHANNELGROUP, $cid, $description, $customset);
return $this->getParent()->privilegeKeyCreate($this->getId(), $cid, TeamSpeak3::TOKEN_CHANNELGROUP, $description, $customset);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Node/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ abstract class Group extends Node
public function message(string $msg): void
{
// get all clients in this group
$clients = $this->getParent()->channelGroupClientList($this->getId());
$clients = $this->clientList();

// get client id from dbid and send a textmessage
foreach ($clients as $client) {
try {
$targetClientID = $this->getParent()->clientgetbydbid($client['cldbid'])->getId();
$targetClientID = $this->getParent()->clientGetByDbid($client['cldbid'])->getId();
$this->execute('sendtextmessage', ['msg' => $msg, 'target' => $targetClientID, 'targetmode' => TeamSpeak3::TEXTMSG_CLIENT]);
} catch (ServerQueryException $e) {
/* ERROR_client_invalid_id */
Expand Down
47 changes: 36 additions & 11 deletions src/Node/Host.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,15 @@ public function version(): array
if ($this->version === null) {
$raw = $this->request('version')->toList();

// Find the first array that contains real data
foreach ($raw as $item) {
if (is_array($item) && isset($item['version'])) {
$this->version = $item;
break;
if (isset($raw['version'])) {
$this->version = $raw;
} elseif (is_array($raw)) {
// Find the first array that contains real data
foreach ($raw as $item) {
if (is_array($item) && isset($item['version'])) {
$this->version = $item;
break;
}
}
}

Expand Down Expand Up @@ -189,7 +193,19 @@ public function serverIdGetByPort(int $port): int
{
$sid = $this->execute('serveridgetbyport', ['virtualserver_port' => $port])->toList();

return $sid[1]['server_id'];
if (isset($sid['server_id'])) {
return (int) $sid['server_id'];
}

if (isset($sid[0]['server_id'])) {
return (int) $sid[0]['server_id'];
}

if (isset($sid[1]['server_id'])) {
return (int) $sid[1]['server_id'];
}

return 0;
}

/**
Expand Down Expand Up @@ -757,9 +773,10 @@ public function login(string $username, string $password): void

if ($this->predefined_query_name) {
$clients = $this->request('clientlist -uid')->toList();
$clientRows = isset($clients['clid']) ? [$clients] : (is_array($clients) ? $clients : []);

foreach ($clients as $client) {
if ($client['client_nickname'] === $this->predefined_query_name) {
foreach ($clientRows as $client) {
if (is_array($client) && isset($client['client_nickname']) && $client['client_nickname'] === $this->predefined_query_name) {
// Kick old query with same nickname
$this->execute('clientkick', [
'clid' => $client['clid'],
Expand All @@ -775,7 +792,11 @@ public function login(string $username, string $password): void
]);
}

$crypt = new Crypt($username);
$cryptKey = substr($username, 0, 56);
if ($cryptKey === '') {
$cryptKey = 'serveradmin';
}
$crypt = new Crypt($cryptKey);
$this->setStorage('_login_user', $username);
$this->setStorage('_login_pass', $crypt->encrypt($password));

Expand Down Expand Up @@ -889,8 +910,12 @@ public function whoami(): array
// Execute server request
$response = $this->request('whoami')->toList();

// response[1] contains the actual data
$data = $response[1] ?? [];
// Check if single-row associative array or nested list
if (isset($response['client_id']) || isset($response['virtualserver_id']) || isset($response['client_nickname'])) {
$data = $response;
} else {
$data = $response[1] ?? ($response[0] ?? []);
}

// Automatically convert StringHelper to strings
foreach ($data as $key => $val) {
Expand Down
2 changes: 1 addition & 1 deletion src/Node/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ public function getInfo(bool $extend = true, bool $convert = false): array

// Now include all standard commands
if ($this instanceof Server) {
$standardCommands = ['whoami', 'serverinfo', 'serverinfoserver info'];
$standardCommands = ['whoami', 'serverinfo'];
foreach ($standardCommands as $cmd) {
try {
$result = $this->{$cmd}(); // Calls the respective function
Expand Down
Loading
Loading