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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,10 @@ $uri = "serverquery://username:password@[fe80::250:56ff:fe16:1447]:10022/?ssh=1"

#### SSL/TLS Connections ([TeaSpeak Server](https://www.teaspeak.de) only)

Secure ServerQuery connections can be established using the optional `tls` parameter:
Secure TeaSpeak ServerQuery connections can be established using the optional `tls` parameter. Certificate verification remains disabled by default for compatibility with self-signed deployments. Enable `tls_verify` only when the server certificate is trusted by PHP and matches the hostname:

```php
$uri = "serverquery://username:password@[fe80::250:56ff:fe16:1447]:10011/?tls=1";
$uri = "serverquery://username:password@teaspeak.example.com:10011/?tls=1&tls_verify=1";
```

#### Custom Protocol Welcome Message and/or MOTD ([TeaSpeak Server](https://www.teaspeak.de) only)
Expand Down
63 changes: 33 additions & 30 deletions src/Adapter/FileTransfer.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public function __destruct()
if ($this->getTransport() instanceof Transport && $this->getTransport()->isConnected()) {
$this->getTransport()->disconnect();
}

Profiler::remove(spl_object_hash($this));
}

/**
Expand Down Expand Up @@ -105,54 +107,55 @@ public function upload(string $ftkey, int $seek, string $data): void
*/
public function download(string $ftkey, int $size, bool $passthru = false)
{
$this->init($ftkey);

if ($passthru) {
$this->passthru($size);
$this->downloadTo($ftkey, $size, static function (StringHelper $data): void {
echo $data;
});
return;
}

$buff = new StringHelper("");
$pack = 4096;

Signal::getInstance()->emit("filetransferDownloadStarted", $ftkey, count($buff), $size);

for ($seek = 0; $seek < $size;) {
$rest = $size - $seek;
$pack = min($rest, $pack);
$data = $this->getTransport()->read(min($rest, $pack));
$seek = $seek + $pack;

$this->downloadTo($ftkey, $size, static function (StringHelper $data) use ($buff): void {
$buff->append($data);

Signal::getInstance()->emit("filetransferDownloadProgress", $ftkey, count($buff), $size);
}

$this->getProfiler()->stop();

Signal::getInstance()->emit("filetransferDownloadFinished", $ftkey, count($buff), $size);

if (strlen($buff) != $size) {
throw new FileTransferException("incomplete file download (" . count($buff) . " of " . $size . " bytes)");
}
});

return $buff;
}

/**
* Outputs all remaining data on a TeamSpeak 3 file transfer stream using PHP's fpassthru()
* function.
* Downloads a file and passes each received chunk to the given consumer.
*
* @param string $ftkey
* @param integer $size
* @param callable $consumer
* @return void
* @throws FileTransferException
*/
protected function passthru(int $size): void
public function downloadTo(string $ftkey, int $size, callable $consumer): void
{
$buff_size = fpassthru($this->getTransport()->getStream());
$this->init($ftkey);
$pack = 4096;
$seek = 0;

Signal::getInstance()->emit("filetransferDownloadStarted", $ftkey, $seek, $size);

try {
while ($seek < $size) {
$data = $this->getTransport()->read(min($size - $seek, $pack));

if ($buff_size != $size) {
throw new FileTransferException("incomplete file download (" . $buff_size . " of " . $size . " bytes)");
if (count($data) === 0) {
throw new FileTransferException("incomplete file download (" . $seek . " of " . $size . " bytes)");
}

$consumer($data);
$seek += count($data);

Signal::getInstance()->emit("filetransferDownloadProgress", $ftkey, $seek, $size);
}
} finally {
$this->getProfiler()->stop();
}

Signal::getInstance()->emit("filetransferDownloadFinished", $ftkey, $seek, $size);
}
}
5 changes: 4 additions & 1 deletion src/Adapter/ServerQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,19 @@ public function __destruct()
{
// do not disconnect, when acting as bot in non-blocking mode
if (! $this->getTransport()->getConfig("blocking")) {
Profiler::remove(spl_object_hash($this));
return;
}

if ($this->getTransport() instanceof Transport && $this->transport->isConnected()) {
try {
$this->request("quit");
} catch (AdapterException) {
return;
// The transport destructor will close an unavailable connection.
}
}

Profiler::remove(spl_object_hash($this));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Adapter/ServerQuery/Reply.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function __construct(array $rpl, string $cmd = "", Host $con = null, bool
*/
public function toString(): ?StringHelper
{
return (!func_num_args()) ? $this->rpl->unescape() : $this->rpl;
return (!func_num_args()) ? (new StringHelper($this->rpl->toString()))->unescape() : $this->rpl;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/Helper/Char.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ public function toHex(): string
*/
public static function fromHex(string $hex): Char
{
if (strlen($hex) != 2) {
if (strlen($hex) != 2 || !ctype_xdigit($hex)) {
throw new HelperException("given parameter '" . $hex . "' is not a valid hexadecimal number");
}

return new self(chr(hexdec($hex)));
return new self(hex2bin($hex));
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/Helper/Profiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ public static function init(string $name = "default"): void
self::$timers[$name] = new Timer($name);
}

/**
* Removes a timer which is no longer associated with a live adapter.
*
* @param string $name
* @return void
*/
public static function remove(string $name): void
{
unset(self::$timers[$name]);
}

/**
* Starts a timer.
*
Expand Down
10 changes: 10 additions & 0 deletions src/Helper/Signal.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,16 @@ public function clearHandlers(string $signal): void
}
}

/**
* Clears all registered signal handlers.
*
* @return void
*/
public function clearAllHandlers(): void
{
$this->sigslots = [];
}

/**
* Returns a singleton instance of PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal.
*
Expand Down
52 changes: 19 additions & 33 deletions src/Helper/StringHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,13 @@ public function isInt(): bool
*/
public function toInt(): int
{
if ($this->string == pow(2, 63) || $this->string == pow(2, 64) || $this->string > pow(2, 31)) {
return -1;
if (ctype_digit($this->string)) {
$value = ltrim($this->string, "0") ?: "0";
$max = (string)PHP_INT_MAX;

if (strlen($value) > strlen($max) || (strlen($value) === strlen($max) && strcmp($value, $max) > 0)) {
return -1;
}
}

return intval($this->string);
Expand Down Expand Up @@ -426,21 +431,7 @@ public function toSha1(): string
*/
public function isUtf8(): bool
{
if (preg_match('/\\A[\\x00-\\x7F]*\\z/', $this->string)) {
return true;
}

$pattern = [];

$pattern[] = "[\xC2-\xDF][\x80-\xBF]"; // non-overlong 2-byte
$pattern[] = "\xE0[\xA0-\xBF][\x80-\xBF]"; // excluding overlongs
$pattern[] = "[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}"; // straight 3-byte
$pattern[] = "\xED[\x80-\x9F][\x80-\xBF]"; // excluding surrogates
$pattern[] = "\xF0[\x90-\xBF][\x80-\xBF]{2}"; // planes 1-3
$pattern[] = "[\xF1-\xF3][\x80-\xBF]{3}"; // planes 4-15
$pattern[] = "\xF4[\x80-\x8F][\x80-\xBF]{2}"; // plane 16

return (bool)preg_match("%(?:" . implode("|", $pattern) . ")+%xs", $this->string);
return mb_check_encoding($this->string, 'UTF-8');
}

/**
Expand Down Expand Up @@ -472,10 +463,17 @@ public function toBase64(): string
*
* @param string $base64
* @return self
* @throws HelperException
*/
public static function fromBase64(string $base64): StringHelper
{
return new self(base64_decode($base64));
$string = base64_decode($base64, true);

if ($string === false) {
throw new HelperException("given parameter is not valid base64 data");
}

return new self($string);
}

/**
Expand All @@ -485,13 +483,7 @@ public static function fromBase64(string $base64): StringHelper
*/
public function toHex(): string
{
$hex = "";

foreach ($this as $char) {
$hex .= $char->toHex();
}

return $hex;
return strtoupper(bin2hex($this->string));
}

/**
Expand All @@ -503,17 +495,11 @@ public function toHex(): string
*/
public static function fromHex(string $hex): StringHelper
{
$string = "";

if (strlen($hex) % 2 == 1) {
if (strlen($hex) % 2 == 1 || ($hex !== "" && !ctype_xdigit($hex))) {
throw new HelperException("given parameter '" . $hex . "' is not a valid hexadecimal number");
}

foreach (str_split($hex, 2) as $chunk) {
$string .= chr(hexdec($chunk));
}

return new self($string);
return new self(hex2bin($hex));
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/Helper/Uri.php
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ public function getQueryVar(string $key, mixed $default = null): mixed
return $default;
}

parse_str(rawurldecode($this->query), $queryArray);
parse_str($this->query, $queryArray);

if (array_key_exists($key, $queryArray)) {
$val = $queryArray[$key];
Expand Down Expand Up @@ -585,7 +585,7 @@ public function getFragment(mixed $default = null): ?StringHelper
*/
public static function getUserParam(string $key, mixed $default = null): mixed
{
return (array_key_exists($key, $_REQUEST) && !empty($_REQUEST[$key])) ? self::stripslashesRecursive($_REQUEST[$key]) : $default;
return array_key_exists($key, $_REQUEST) ? self::stripslashesRecursive($_REQUEST[$key]) : $default;
}

/**
Expand All @@ -597,7 +597,7 @@ public static function getUserParam(string $key, mixed $default = null): mixed
*/
public static function getHostParam(string $key, mixed $default = null): mixed
{
return (array_key_exists($key, $_SERVER) && !empty($_SERVER[$key])) ? $_SERVER[$key] : $default;
return array_key_exists($key, $_SERVER) ? $_SERVER[$key] : $default;
}

/**
Expand All @@ -609,7 +609,7 @@ public static function getHostParam(string $key, mixed $default = null): mixed
*/
public static function getSessParam(string $key, mixed $default = null): mixed
{
return (array_key_exists($key, $_SESSION) && !empty($_SESSION[$key])) ? $_SESSION[$key] : $default;
return array_key_exists($key, $_SESSION) ? $_SESSION[$key] : $default;
}

/**
Expand Down
10 changes: 5 additions & 5 deletions src/Node/Channel.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function subChannelGetById(int $cid): Channel
throw new ServerQueryException("invalid channelID", 0x300);
}

return $this->channelList[$cid];
return $this->subChannelList()[$cid];
}

/**
Expand All @@ -81,7 +81,7 @@ public function subChannelGetById(int $cid): Channel
* @return Channel
* @throws ServerQueryException
*/
public function subChannelGetByName(int $name): Channel
public function subChannelGetByName(string $name): Channel
{
foreach ($this->subChannelList() as $channel) {
if ($channel["channel_name"] == $name) {
Expand Down Expand Up @@ -124,7 +124,7 @@ public function clientGetById(int $clid): Client
throw new ServerQueryException("invalid clientID", 0x200);
}

return $this->clientList[$clid];
return $this->clientList()[$clid];
}

/**
Expand All @@ -134,7 +134,7 @@ public function clientGetById(int $clid): Client
* @return Client
* @throws ServerQueryException
*/
public function clientGetByName(int $name): Client
public function clientGetByName(string $name): Client
{
foreach ($this->clientList() as $client) {
if ($client["client_nickname"] == $name) {
Expand Down Expand Up @@ -392,7 +392,7 @@ public function iconDownload()
return;
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->iconGetName("channel_icon_id"));
$download = $this->getParent()->transferInitDownload(TeamSpeak3::generateTransferClientId(), 0, $this->iconGetName("channel_icon_id"));
$transfer = TeamSpeak3::factory("filetransfer://" . (str_contains($download["host"], ":") ? "[" . $download["host"] . "]" : $download["host"]) . ":" . $download["port"]);

return $transfer->download($download["ftkey"], $download["size"]);
Expand Down
4 changes: 2 additions & 2 deletions src/Node/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public function avatarDownload()
return;
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->avatarGetName());
$download = $this->getParent()->transferInitDownload(TeamSpeak3::generateTransferClientId(), 0, $this->avatarGetName());
$transfer = TeamSpeak3::factory("filetransfer://" . (str_contains($download["host"], ":") ? "[" . $download["host"] . "]" : $download["host"]) . ":" . $download["port"]);

return $transfer->download($download["ftkey"], $download["size"]);
Expand Down Expand Up @@ -398,7 +398,7 @@ public function iconDownload()
return;
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->iconGetName("client_icon_id"));
$download = $this->getParent()->transferInitDownload(TeamSpeak3::generateTransferClientId(), 0, $this->iconGetName("client_icon_id"));
$transfer = TeamSpeak3::factory("filetransfer://" . (str_contains($download["host"], ":") ? "[" . $download["host"] . "]" : $download["host"]) . ":" . $download["port"]);

return $transfer->download($download["ftkey"], $download["size"]);
Expand Down
2 changes: 1 addition & 1 deletion src/Node/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function iconDownload()
return;
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->iconGetName("iconid"));
$download = $this->getParent()->transferInitDownload(TeamSpeak3::generateTransferClientId(), 0, $this->iconGetName("iconid"));
$transfer = TeamSpeak3::factory("filetransfer://" . (str_contains($download["host"], ":") ? "[" . $download["host"] . "]" : $download["host"]) . ":" . $download["port"]);

return $transfer->download($download["ftkey"], $download["size"]);
Expand Down
Loading
Loading