Skip to content

Commit

Permalink
Merge branch 'release/v2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
jonjomckay committed Feb 22, 2017
2 parents ce2c1fb + 3414adf commit 55c878a
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 22 deletions.
9 changes: 8 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,26 @@ language: php
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq clamav-daemon
- sudo chmod 777 /tmp/clamav
- sudo sed -i 's#/var/lib/clamav#/tmp/clamav#g' /etc/clamav/clamd.conf
- sudo sed -i 's#/var/lib/clamav#/tmp/clamav#g' /etc/clamav/freshclam.conf
- sudo sh -c 'echo TCPSocket 3310 >> /etc/clamav/clamd.conf'
- sudo freshclam
- sudo rsync -ar /tmp/clamav/ /var/lib/clamav/
- sudo /etc/init.d/clamav-daemon start

cache:
directories:
- $HOME/.composer/cache
- /tmp/clamav

install:
- composer install

php:
- 5.6
- 7.0
- 7.1

script:
- vendor/bin/phpunit
10 changes: 7 additions & 3 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit colors="true"
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.8/phpunit.xsd"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
bootstrap="./vendor/autoload.php"
>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
</logging>
<testsuites>
<testsuite name="Quahog Test Suite">
<directory>./tests/</directory>
Expand All @@ -16,4 +20,4 @@
<directory>./src/</directory>
</whitelist>
</filter>
</phpunit>
</phpunit>
46 changes: 39 additions & 7 deletions src/Quahog/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,25 @@ class Client

/** @var bool $_inSession Has the current connection a Session? */
private $_inSession = false;
/** @var int $_timeout Read timeout */
private $_timeout = 30;
private $_mode;

/**
* Instantiate a Quahog\Client instance.
*
* @param Socket $socket An instance of \Socket\Raw\Socket which points to clamd
* @param int $timeout
* @param int $mode
*/
public function __construct(Socket $socket)
public function __construct(Socket $socket, $timeout = 30, $mode = PHP_BINARY_READ)
{
$this->_mode = $mode;
$this->_socket = $socket;
$this->_timeout = $timeout;
if ($mode === PHP_BINARY_READ) {
trigger_error("Using binary read is deprecated and will be removed in a future release. Explicitly set $mode to PHP_NORMAL_READ to avoid this message.", E_USER_DEPRECATED);
}
}

/**
Expand All @@ -42,7 +52,7 @@ public function ping()
{
$this->_sendCommand('PING');

if ($this->_receiveResponse() === 'PONG') {
if ($this->_receiveResponse(true) === 'PONG') {
return true;
}

Expand All @@ -59,7 +69,7 @@ public function version()
{
$this->_sendCommand('VERSION');

return $this->_receiveResponse();
return $this->_receiveResponse(true);
}

/**
Expand All @@ -71,7 +81,7 @@ public function stats()
{
$this->_sendCommand('STATS');

return $this->_receiveResponse();
return $this->_receiveResponse(true, "END\n");
}

/**
Expand Down Expand Up @@ -172,7 +182,7 @@ public function scanLocalFile($file, $maxChunkSize = 1024)
/**
* Scan a stream.
*
* @param resource $stream A file stream in string form.
* @param resource $stream A file stream
* @param int $maxChunkSize The maximum chunk size in bytes to send to clamd at a time.
*
* @return string
Expand Down Expand Up @@ -257,14 +267,36 @@ private function _sendCommand($command)
/**
* A wrapper to cleanly read a response from clamd.
*
* @param bool $removeId
* @param string $readUntil
* @return string
* @throws ConnectionException
*/
private function _receiveResponse()
private function _receiveResponse($removeId = false, $readUntil = "\n")
{
$result = $this->_socket->read(4096);
$result = null;
$readUntilLen = strlen($readUntil);
do {
if ($this->_socket->selectRead($this->_timeout)) {
$rt = $this->_socket->read(4096, $this->_mode);
if ($rt === "") {
break;
}
$result .= $rt;
if (strcmp(substr($result, 0 - $readUntilLen), $readUntil) == 0) {
break;
}
} else if ($this->_mode === PHP_NORMAL_READ) {
throw new ConnectionException("Timeout waiting to read response");
} else {
break;
}
} while (true);

if (!$this->_inSession) {
$this->_closeConnection();
} else if ($removeId) {
$result = preg_replace('/^\d+: /', "", $result, 1);
}

return trim($result);
Expand Down
237 changes: 237 additions & 0 deletions tests/QuahogITTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?php
namespace Xenolope\Quahog\Tests;

use Socket\Raw\Factory;
use Xenolope\Quahog\Client;

class QuahogITTest extends \PHPUnit_Framework_TestCase
{
const EICAR = 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STAND' . 'ARD-ANTIVIRUS-TEST-FILE!$H+H*';
protected $address = 'unix:///var/run/clamav/clamd.ctl';

public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
umask(0);
}

public function addresses()
{
$addresses = [];
if (array_key_exists('CLAM_UNIX_ADDRESS', $_SERVER)) {
if (!empty($_SERVER['CLAM_UNIX_ADDRESS'])) {
$addresses['unix'] = [$_SERVER['CLAM_UNIX_ADDRESS']];
}
} else {
$addresses['unix'] = ['unix:///var/run/clamav/clamd.ctl'];
}
if (array_key_exists('CLAM_TCP_ADDRESS', $_SERVER)) {
if (!empty($_SERVER['CLAM_TCP_ADDRESS'])) {
$addresses['tcp'] = [$_SERVER['CLAM_TCP_ADDRESS']];
}
} else {
$addresses['tcp'] = ['tcp://127.0.0.1:3310'];
}

return $addresses;
}

/** @dataProvider addresses */
public function testScanStreamClean($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$result = $quahog->scanStream("ABC");
$this->assertSame(
['filename' => 'stream', 'reason' => null, 'status' => 'OK'],
$result
);
}

/** @dataProvider addresses */
public function testScanStreamEicar($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$result = $quahog->scanStream(str_pad(self::EICAR, 1000, " ", STR_PAD_BOTH), 10);
$this->assertSame(
['filename' => 'stream', 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
}

/** @dataProvider addresses */
public function testScanFileClean($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);


$name = tempnam(sys_get_temp_dir(), "");
file_put_contents($name, "ABC");
chmod($name, 0777);

try {
$result = $quahog->scanFile($name);
$this->assertSame(
['filename' => $name, 'reason' => null, 'status' => 'OK'],
$result
);
} finally {
unlink($name);
}
}

/** @dataProvider addresses */
public function testScanFileEicar($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$name = tempnam(sys_get_temp_dir(), "");
file_put_contents($name, self::EICAR);
chmod($name, 0777);

try {
$result = $quahog->scanFile($name);
$this->assertSame(
['filename' => $name, 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
} finally {
unlink($name);
}
}

/** @dataProvider addresses */
public function testScanResourceEicar($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$name = tempnam(sys_get_temp_dir(), "");
file_put_contents($name, self::EICAR);
chmod($name, 0777);

try {
$result = $quahog->scanResourceStream(fopen($name,"r"));
$this->assertSame(
['filename' => 'stream', 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
} finally {
unlink($name);
}
}

/** @dataProvider addresses */
public function testScanMultiScanEicar($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$name = tempnam(sys_get_temp_dir(), "");
unlink($name);
mkdir($name);
$file1 = $name . DIRECTORY_SEPARATOR . "text";
$file2 = $name . DIRECTORY_SEPARATOR . "eicar1";
file_put_contents($file1, "ABC");
file_put_contents($file2, self::EICAR);
chmod($name, 0777);
chmod($file1, 0777);
chmod($file2, 0777);
try {
$result = $quahog->multiscanFile($name);
$this->assertSame(
['filename' => $file2, 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
} finally {
unlink($file1);
unlink($file2);
rmdir($name);
}
}

/** @dataProvider addresses */
public function testScanContScanEicar($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$name = tempnam(sys_get_temp_dir(), "");
unlink($name);
mkdir($name);
$file1 = $name . DIRECTORY_SEPARATOR . "text";
$file2 = $name . DIRECTORY_SEPARATOR . "eicar1";
file_put_contents($file1, "ABC");
file_put_contents($file2, self::EICAR);
chmod($name, 0777);
chmod($file1, 0777);
chmod($file2, 0777);
try {
$result = $quahog->contScan($name);
$this->assertSame(
['filename' => $file2, 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
} finally {
unlink($file1);
unlink($file2);
rmdir($name);
}
}

/** @dataProvider addresses */
public function testScanStreamSessionEicar($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);

$quahog->startSession();
$result = $quahog->scanStream(self::EICAR);
$this->assertSame(
['id' => '1', 'filename' => "stream", 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
$result = $quahog->scanStream(self::EICAR);
$this->assertSame(
['id' => '2', 'filename' => "stream", 'reason' => 'Eicar-Test-Signature', 'status' => 'FOUND'],
$result
);
$result = $quahog->scanStream('ABC');
$this->assertSame(
['id' => '3', 'filename' => "stream", 'reason' => null, 'status' => 'OK'],
$result
);
$quahog->endSession();
$quahog->disconnect();
}

/** @dataProvider addresses */
public function testStatus($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);
$this->assertStringEndsWith("END", $quahog->stats());
}

/** @dataProvider addresses */
public function testPing($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);
$this->assertTrue($quahog->ping());
}

/** @dataProvider addresses */
public function testVersion($address)
{
$socket = (new Factory())->createClient($address);
$quahog = new Client($socket, 30, PHP_NORMAL_READ);
$this->assertNotEmpty($quahog->version());
}

}
Loading

0 comments on commit 55c878a

Please sign in to comment.