Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c7f5f8f
add: TCP transport class to handle filetransfer connections
MajorOli Jul 20, 2026
d2c825f
update: refactor file transfer methods
MajorOli Jul 20, 2026
77e5940
add: FileTransferTest class for testing
MajorOli Jul 20, 2026
f3ac7de
fix: replace static server port into dynamic variable in test classes
MajorOli Jul 21, 2026
b7e6693
fix: typo in FileTransferTest class name
MajorOli Jul 21, 2026
aee1576
fix: unescaped ftkey in file transfer with ts3 server
MajorOli Jul 21, 2026
00df089
change: separate filetransfer tests into upload, download and delete
MajorOli Jul 22, 2026
848868f
add: proof is icon file is readable, replace ftkey unescape chars
MajorOli Jul 27, 2026
5d1c133
add: function delete icon server side
MajorOli Jul 27, 2026
f734ba3
add: test upload, download and delete icon
MajorOli Jul 27, 2026
039c0c7
chore: code-style
MajorOli Jul 27, 2026
75d6b3a
add: testdata for icons
MajorOli Jul 27, 2026
751e9aa
update: code coverage-badge.svg
MajorOli Jul 27, 2026
33b26d4
update: icon file transfer via servergroups
MajorOli Jul 28, 2026
2c17093
add: test for icon file transfer via group icon
MajorOli Jul 28, 2026
b4b0d3d
add: test for icon file transfer via channel icon
MajorOli Jul 28, 2026
a87bc2c
add: test for icon file transfer via client icon
MajorOli Jul 28, 2026
33e604a
add: dev_reset_test_channel
MajorOli Jul 28, 2026
d3e95ce
fix: move icon client transfer to ClientTest.php
MajorOli Jul 28, 2026
dc0a2f4
update: coverag-badge.svg
MajorOli Jul 28, 2026
7d2d33f
chore: update dependencies
MajorOli Jul 29, 2026
11e5809
chore: understood, cleanup
MajorOli Jul 29, 2026
1ff7965
fix: download client avatar
MajorOli Aug 2, 2026
eeda902
add: test download client avatar
MajorOli Aug 2, 2026
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: 3 additions & 1 deletion .env.testing.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ DEV_LIVE_SERVER_UNIT_TEST_CHANNEL=UnitTest
DEV_LIVE_SERVER_UNIT_TEST_USER_ACTIVE=false
DEV_LIVE_SERVER_UNIT_TEST_USER=UnitTestUser
DEV_LIVE_SERVER_UNIT_TEST_SIGNALS=false
DEV_LIVE_SERVER_UNIT_TEST_USER_EXTEND=
DEV_LIVE_SERVER_UNIT_TEST_USER_EXTEND=UnitTestUser2
DEV_LIVE_SERVER_UNIT_TEST_SERVER_PORT=9987
DEV_LIVE_SERVER_UNIT_TEST_SERVER_QUERY_LOGIN_NAME=ts3-bot-dev
194 changes: 95 additions & 99 deletions composer.lock

Large diffs are not rendered by default.

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.
57 changes: 34 additions & 23 deletions src/Adapter/FileTransfer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Profiler;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\Signal;
use PlanetTeamSpeak\TeamSpeak3Framework\Helper\StringHelper;
use PlanetTeamSpeak\TeamSpeak3Framework\Transport\TCP;
use PlanetTeamSpeak\TeamSpeak3Framework\Transport\Transport;

/**
Expand All @@ -21,7 +22,7 @@ class FileTransfer extends Adapter
*/
public function syn(): void
{
$this->initTransport($this->options);
$this->initTransport($this->options, TCP::class);
$this->transport->setAdapter($this);

Profiler::init(spl_object_hash($this));
Expand All @@ -42,14 +43,14 @@ public function __destruct()
/**
* Sends a valid file transfer key to the server to initialize the file transfer.
*
* @param string $ftkey
* @param string $ftkey
* @return void
* @throws FileTransferException
* @throws TransportException
*/
protected function init(string $ftkey): void
{
if (strlen($ftkey) != 32 && strlen($ftkey) != 16) {
if (strlen($ftkey) !== 32 && strlen($ftkey) !== 16) {
throw new FileTransferException('invalid file transfer key format');
}

Expand All @@ -62,9 +63,9 @@ protected function init(string $ftkey): void
/**
* Sends the content of a file to the server.
*
* @param string $ftkey
* @param int $seek
* @param string $data
* @param string $ftkey
* @param int $seek
* @param string $data
* @return void
* @throws FileTransferException
* @throws TransportException
Expand All @@ -78,14 +79,15 @@ public function upload(string $ftkey, int $seek, string $data): void

Signal::getInstance()->emit('filetransferUploadStarted', $ftkey, $seek, $size);

for (; $seek < $size;) {
while ($seek < $size) {
$rest = $size - $seek;
$pack = min($rest, $pack);
$buff = substr($data, $seek, $pack);
$seek = $seek + $pack;
$chunkSize = min($rest, $pack);
$buff = substr($data, $seek, $chunkSize);

$this->getTransport()->send($buff);

$seek += $chunkSize;

Signal::getInstance()->emit('filetransferUploadProgress', $ftkey, $seek, $size);
}

Expand All @@ -97,9 +99,9 @@ public function upload(string $ftkey, int $seek, string $data): void
/**
* Returns the content of a downloaded file as a PlanetTeamSpeak\TeamSpeak3Framework\Helper\StringHelper object.
*
* @param string $ftkey
* @param int $size
* @param bool $passthru
* @param string $ftkey
* @param int $size
* @param bool $passthru
* @return StringHelper|void
* @throws FileTransferException
* @throws TransportException
Expand All @@ -119,13 +121,16 @@ public function download(string $ftkey, int $size, bool $passthru = false)

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;
while (count($buff) < $size) {
$rest = $size - count($buff);
$chunkSize = min($rest, $pack);
$data = $this->getTransport()->read($chunkSize);

if (count($data) === 0) {
break;
}

$buff->append($data);
$buff->append($data->toString());

Signal::getInstance()->emit('filetransferDownloadProgress', $ftkey, count($buff), $size);
}
Expand All @@ -134,7 +139,7 @@ public function download(string $ftkey, int $size, bool $passthru = false)

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

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

Expand All @@ -151,10 +156,16 @@ public function download(string $ftkey, int $size, bool $passthru = false)
*/
protected function passthru(int $size): void
{
$buff_size = fpassthru($this->getTransport()->getStream());
$stream = $this->getTransport()->getStream();

if (! is_resource($stream)) {
throw new FileTransferException('invalid file transfer stream');
}

$buffSize = fpassthru($stream);

if ($buff_size != $size) {
throw new FileTransferException('incomplete file download ('.$buff_size.' of '.$size.' bytes)');
if ($buffSize !== $size) {
throw new FileTransferException('incomplete file download ('.$buffSize.' of '.$size.' bytes)');
}
}
}
43 changes: 41 additions & 2 deletions src/Node/Channel.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,44 @@ public function dirCreate(string $cpw = '', string $dirname = '/'): void
$this->getParent()->channelDirCreate($this->getId(), $cpw, $dirname);
}

/**
* Downloads a file from this channel file repository.
*
* @param string $name
* @param string $cpw
* @param int $seekpos
* @return StringHelper
* @throws AdapterException
* @throws FileTransferException
* @throws ServerQueryException
* @throws TransportException
* @throws \Exception
*/
public function fileDownload(string $name, string $cpw = '', int $seekpos = 0): StringHelper
{
return $this->getParent()->channelFileDownload($this->getId(), $name, $cpw, $seekpos);
}

/**
* Uploads a file into this channel file repository.
*
* @param string $name
* @param string $data
* @param string $cpw
* @param bool $overwrite
* @param bool $resume
* @return void
* @throws AdapterException
* @throws FileTransferException
* @throws ServerQueryException
* @throws TransportException
* @throws \Exception
*/
public function fileUpload(string $name, string $data, string $cpw = '', bool $overwrite = false, bool $resume = false) :void
{
$this->getParent()->channelFileUpload($this->getId(), $name, $data, $cpw, $overwrite, $resume);
}

/**
* Returns the level of the channel.
*
Expand Down Expand Up @@ -417,9 +455,10 @@ public function iconDownload()
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->iconGetName('channel_icon_id'));
$transfer = TeamSpeak3::factory('filetransfer://'.(str_contains($download['host'], ':') ? '['.$download['host'].']' : $download['host']).':'.$download['port']);
$ftkey = str_replace('\\/', '/', (string) $download['ftkey']);
$transfer = TeamSpeak3::factory($this->getParent()->buildFileTransferUri($download));

return $transfer->download($download['ftkey'], $download['size']);
return $transfer->download($ftkey, (int) $download['size']);
}

/**
Expand Down
10 changes: 6 additions & 4 deletions src/Node/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,10 @@ public function avatarDownload()
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->avatarGetName());
$transfer = TeamSpeak3::factory('filetransfer://'.(str_contains($download['host'], ':') ? '['.$download['host'].']' : $download['host']).':'.$download['port']);
$ftkey = str_replace('\\/', '/', (string) $download['ftkey']);
$transfer = TeamSpeak3::factory($this->getParent()->buildFileTransferUri($download));

return $transfer->download($download['ftkey'], $download['size']);
return $transfer->download($ftkey, (int) $download['size']);
}

/**
Expand Down Expand Up @@ -476,9 +477,10 @@ public function iconDownload()
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->iconGetName('client_icon_id'));
$transfer = TeamSpeak3::factory('filetransfer://'.(str_contains($download['host'], ':') ? '['.$download['host'].']' : $download['host']).':'.$download['port']);
$ftkey = str_replace('\\/', '/', (string) $download['ftkey']);
$transfer = TeamSpeak3::factory($this->getParent()->buildFileTransferUri($download));

return $transfer->download($download['ftkey'], $download['size']);
return $transfer->download($ftkey, (int) $download['size']);
}

/**
Expand Down
5 changes: 3 additions & 2 deletions src/Node/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ public function iconDownload()
}

$download = $this->getParent()->transferInitDownload(rand(0x0000, 0xFFFF), 0, $this->iconGetName('iconid'));
$transfer = TeamSpeak3::factory('filetransfer://'.(str_contains($download['host'], ':') ? '['.$download['host'].']' : $download['host']).':'.$download['port']);
$ftkey = str_replace('\\/', '/', (string) $download['ftkey']);
$transfer = TeamSpeak3::factory($this->getParent()->buildFileTransferUri($download));

return $transfer->download($download['ftkey'], $download['size']);
return $transfer->download($ftkey, (int) $download['size']);
}

/**
Expand Down
Loading
Loading