Skip to content

Commit edd5014

Browse files
authored
php: Allow ForFile users to override filesystem driver (#892)
Allow to pass an optional amphp driver for ForFile() usage. Use case is Nextcloud, where we need to override the default amphp driver selection to work around issues. Callers can already provide their own implementations using ForStream(), but ForFile() has the benefit of hashing client-side. Note that while the driver is overriden for uploading only. When performing SHA256 hashing we use the PHP standard library, which doesn't allow for custom filesystem drivers like amphp.
1 parent 454b074 commit edd5014

File tree

3 files changed

+129
-9
lines changed

3 files changed

+129
-9
lines changed

php/src/vaas/Options/ForFileOptions.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
namespace VaasSdk\Options;
44

5+
use Amp\File\FilesystemDriver;
6+
57
class ForFileOptions
68
{
7-
private const DEFAULT_TIMEOUT = 300;
8-
private const DEFAULT_REQUEST_ID = null;
9+
public const DEFAULT_TIMEOUT = 300;
10+
public const DEFAULT_REQUEST_ID = null;
911

1012
public function __construct(
11-
public bool $useCache = true,
12-
public bool $useHashLookup = true,
13-
public int $timeout = self::DEFAULT_TIMEOUT,
14-
public ?string $vaasRequestId = self::DEFAULT_REQUEST_ID) {}
13+
public bool $useCache = true,
14+
public bool $useHashLookup = true,
15+
public int $timeout = self::DEFAULT_TIMEOUT,
16+
public ?FilesystemDriver $filesystemDriver = null,
17+
public ?string $vaasRequestId = self::DEFAULT_REQUEST_ID) {}
1518

1619
public static function fromVaasOptions(VaasOptions $options): self
1720
{

php/src/vaas/Vaas.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
use VaasSdk\Options\ForUrlOptions;
2424
use VaasSdk\Options\VaasOptions;
2525
use function Amp\async;
26-
use function Amp\File\openFile;
26+
use function Amp\File\filesystem;
2727

2828
class Vaas
2929
{
@@ -140,8 +140,9 @@ public function forFileAsync(string $path, ?ForFileOptions $options = null, ?Can
140140
{
141141
return async(function () use ($path, $options, $cancellation) {
142142
$this->logger->debug("Requesting verdict for file: $path");
143+
$filesystem = filesystem($options->filesystemDriver);
143144

144-
if (!file_exists($path)) {
145+
if (!$filesystem->exists($path)) {
145146
$this->logger->error("File does not exist: $path");
146147
throw new VaasClientException('File does not exist');
147148
}
@@ -167,7 +168,7 @@ public function forFileAsync(string $path, ?ForFileOptions $options = null, ?Can
167168
}
168169

169170
try {
170-
$stream = openFile($path, 'r');
171+
$stream = $filesystem->openFile($path, 'r');
171172
$cancellation?->subscribe(function () use ($stream) { $stream->close(); });
172173
$forStreamOptions = new ForStreamOptions($options->useHashLookup, $options->timeout, $options->vaasRequestId);
173174
$this->logger->debug("Requesting verdict for $path as file stream");

php/tests/VaasTesting/VaasTest.php

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace VaasTesting;
44

5+
use Amp\File\Driver\BlockingFilesystemDriver;
6+
use Amp\File\File;
7+
use Amp\File\FilesystemDriver;
58
use Amp\File\FilesystemException;
69
use Dotenv\Dotenv;
710
use Monolog\Formatter\JsonFormatter;
@@ -14,11 +17,13 @@
1417
use VaasSdk\Authentication\ClientCredentialsGrantAuthenticator;
1518
use VaasSdk\Exceptions\InvalidSha256Exception;
1619
use VaasSdk\Exceptions\VaasClientException;
20+
use VaasSdk\Options\ForFileOptions;
1721
use VaasSdk\Options\ForSha256Options;
1822
use VaasSdk\Options\VaasOptions;
1923
use VaasSdk\Sha256;
2024
use VaasSdk\Vaas;
2125
use VaasSdk\Verdict;
26+
use function Amp\File\filesystem;
2227
use function Amp\File\openFile;
2328

2429
final class VaasTest extends TestCase
@@ -274,6 +279,117 @@ public function testForFile_WithInvalidFile_ThrowsVaasClientException(): void
274279
$this->vaas->forFileAsync(__DIR__ . "/invalid")->await();
275280
}
276281

282+
public function testForFile_WithCustomFilesystemDriver_UtilizesDriver(): void
283+
{
284+
$file = file_get_contents(self::PUP_URL);
285+
file_put_contents(__DIR__ . "/PotentiallyUnwantedCustomDriver.exe", $file);
286+
$customDriverCalled = false;
287+
$customDriver = new class(
288+
function () use (&$customDriverCalled) {
289+
$customDriverCalled = true;
290+
}
291+
) implements FileSystemDriver {
292+
private $callback;
293+
294+
public function __construct(callable $callback)
295+
{
296+
$this->callback = $callback;
297+
}
298+
299+
public function openFile(string $path, string $mode): File
300+
{
301+
($this->callback)();
302+
return filesystem(new BlockingFilesystemDriver())->openFile($path, $mode);
303+
}
304+
305+
public function getStatus(string $path): ?array
306+
{
307+
return filesystem(new BlockingFilesystemDriver())->getStatus($path);
308+
}
309+
310+
public function getLinkStatus(string $path): ?array
311+
{
312+
throw new BadMethodCallException('Not implemented');
313+
}
314+
315+
public function createSymlink(string $target, string $link): void
316+
{
317+
throw new BadMethodCallException('Not implemented');
318+
}
319+
320+
public function createHardlink(string $target, string $link): void
321+
{
322+
throw new BadMethodCallException('Not implemented');
323+
}
324+
325+
public function resolveSymlink(string $target): string
326+
{
327+
throw new BadMethodCallException('Not implemented');
328+
}
329+
330+
public function move(string $from, string $to): void
331+
{
332+
throw new BadMethodCallException('Not implemented');
333+
}
334+
335+
public function deleteFile(string $path): void
336+
{
337+
throw new BadMethodCallException('Not implemented');
338+
}
339+
340+
public function createDirectory(string $path, int $mode = 0777): void
341+
{
342+
throw new BadMethodCallException('Not implemented');
343+
}
344+
345+
public function createDirectoryRecursively(string $path, int $mode = 0777): void
346+
{
347+
throw new BadMethodCallException('Not implemented');
348+
}
349+
350+
public function deleteDirectory(string $path): void
351+
{
352+
throw new BadMethodCallException('Not implemented');
353+
}
354+
355+
public function listFiles(string $path): array
356+
{
357+
throw new BadMethodCallException('Not implemented');
358+
}
359+
360+
public function changePermissions(string $path, int $mode): void
361+
{
362+
throw new BadMethodCallException('Not implemented');
363+
}
364+
365+
public function changeOwner(string $path, ?int $uid, ?int $gid): void
366+
{
367+
throw new BadMethodCallException('Not implemented');
368+
}
369+
370+
public function touch(string $path, ?int $modificationTime, ?int $accessTime): void
371+
{
372+
throw new BadMethodCallException('Not implemented');
373+
}
374+
375+
public function read(string $path): string
376+
{
377+
throw new BadMethodCallException('Not implemented');
378+
}
379+
380+
public function write(string $path, string $contents): void
381+
{
382+
throw new BadMethodCallException('Not implemented');
383+
}
384+
};
385+
386+
$verdict = $this->vaas->forFileAsync(__DIR__ . "/PotentiallyUnwantedCustomDriver.exe", new ForFileOptions(false, false, ForFileOptions::DEFAULT_TIMEOUT, $customDriver))->await();
387+
388+
unlink(__DIR__ . "/PotentiallyUnwantedCustomDriver.exe");
389+
$this->assertEquals(Verdict::PUP, $verdict->verdict);
390+
$this->assertTrue($customDriverCalled, "custom driver not called");
391+
}
392+
277393
public function testForStream_WithCleanStream_GetsCleanResponse(): void
278394
{
279395
try {

0 commit comments

Comments
 (0)