Development Snapshot
Opening short files via webdav, that were saved via webdav, now works
This commit is contained in:
parent
bfd47cd2df
commit
bc3550b37b
5 changed files with 172 additions and 41 deletions
|
@ -67,7 +67,9 @@ class Hooks {
|
||||||
|
|
||||||
$_SESSION['enckey'] = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] );
|
$_SESSION['enckey'] = Crypt::symmetricDecryptFileContent( $encryptedKey, $params['password'] );
|
||||||
|
|
||||||
// trigger_error( "\$_SESSION['enckey'] = {$_SESSION['enckey']}" );
|
\OC_FileProxy::$enabled = false;
|
||||||
|
file_put_contents( '/home/samtuke/enckey', $_SESSION['enckey'] );
|
||||||
|
\OC_FileProxy::$enabled = true;
|
||||||
|
|
||||||
$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
|
$view1 = new \OC_FilesystemView( '/' . $params['uid'] );
|
||||||
|
|
||||||
|
|
|
@ -142,14 +142,15 @@ class Proxy extends \OC_FileProxy {
|
||||||
Crypt::mode() == 'server'
|
Crypt::mode() == 'server'
|
||||||
&& Crypt::isEncryptedContent( $data )
|
&& Crypt::isEncryptedContent( $data )
|
||||||
) {
|
) {
|
||||||
|
//trigger_error("bong");
|
||||||
|
|
||||||
$filePath = explode( '/', $path );
|
$filePath = explode( '/', $path );
|
||||||
|
|
||||||
$filePath = array_slice( $filePath, 3 );
|
$filePath = array_slice( $filePath, 3 );
|
||||||
|
|
||||||
$filePath = '/' . implode( '/', $filePath );
|
$filePath = '/' . implode( '/', $filePath );
|
||||||
|
|
||||||
$cached = \OC_FileCache_Cached::get( $path, '' );
|
//$cached = \OC_FileCache_Cached::get( $path, '' );
|
||||||
|
|
||||||
$keyFile = Keymanager::getFileKey( $filePath );
|
$keyFile = Keymanager::getFileKey( $filePath );
|
||||||
|
|
||||||
|
@ -158,8 +159,9 @@ class Proxy extends \OC_FileProxy {
|
||||||
} elseif (
|
} elseif (
|
||||||
Crypt::mode() == 'server'
|
Crypt::mode() == 'server'
|
||||||
&& isset( $_SESSION['legacyenckey'] )
|
&& isset( $_SESSION['legacyenckey'] )
|
||||||
//&& Crypt::isEncryptedMeta( $path )
|
&& Crypt::isEncryptedMeta( $path )
|
||||||
) {
|
) {
|
||||||
|
trigger_error("mong");
|
||||||
|
|
||||||
$data = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] );
|
$data = Crypt::legacyDecrypt( $data, $_SESSION['legacyenckey'] );
|
||||||
//trigger_error($data);
|
//trigger_error($data);
|
||||||
|
@ -180,6 +182,10 @@ class Proxy extends \OC_FileProxy {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reformat path for use with OC_FSV
|
||||||
|
$path_split = explode( '/', $path );
|
||||||
|
$path_f = implode( array_slice( $path_split, 3 ) );
|
||||||
|
|
||||||
// Disable encryption proxy to prevent recursive calls
|
// Disable encryption proxy to prevent recursive calls
|
||||||
\OC_FileProxy::$enabled = false;
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
|
@ -192,14 +198,17 @@ class Proxy extends \OC_FileProxy {
|
||||||
$util = new Util( $view, \OCP\USER::getUser());
|
$util = new Util( $view, \OCP\USER::getUser());
|
||||||
|
|
||||||
// If file is already encrypted, decrypt using crypto protocol
|
// If file is already encrypted, decrypt using crypto protocol
|
||||||
if ( Crypt::mode() == 'server' && $util->isEncryptedPath( $path ) ) {
|
if (
|
||||||
|
Crypt::mode() == 'server'
|
||||||
|
&& $util->isEncryptedPath( $path )
|
||||||
|
) {
|
||||||
|
|
||||||
// Close the original encrypted file
|
// Close the original encrypted file
|
||||||
fclose( $result );
|
fclose( $result );
|
||||||
|
|
||||||
// Open the file using the crypto protocol and let
|
// Open the file using the crypto stream wrapper
|
||||||
// it do the decryption work instead
|
// protocol and let it do the decryption work instead
|
||||||
$result = fopen( 'crypt://' . $path, $meta['mode'] );
|
$result = fopen( 'crypt://' . $path_f, $meta['mode'] );
|
||||||
|
|
||||||
|
|
||||||
} elseif (
|
} elseif (
|
||||||
|
@ -207,14 +216,10 @@ class Proxy extends \OC_FileProxy {
|
||||||
and $meta ['mode'] != 'r'
|
and $meta ['mode'] != 'r'
|
||||||
and $meta['mode'] != 'rb'
|
and $meta['mode'] != 'rb'
|
||||||
) {
|
) {
|
||||||
// If the file should be encrypted and has been opened for
|
// If the file is not yet encrypted, but should be
|
||||||
// reading only
|
// encrypted when it's saved (it's not read only)
|
||||||
|
|
||||||
// Reformat path for use with OC_FSV
|
// NOTE: this is the case for new files saved via WebDAV
|
||||||
$path_split = explode( '/', $path );
|
|
||||||
$path_f = implode( array_slice( $path_split, 3 ) );
|
|
||||||
|
|
||||||
// trigger_error("$path_f = ".var_export($path_f, 1));
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
$view->file_exists( $path )
|
$view->file_exists( $path )
|
||||||
|
@ -222,7 +227,7 @@ class Proxy extends \OC_FileProxy {
|
||||||
) {
|
) {
|
||||||
$x = $view->file_get_contents( $path );
|
$x = $view->file_get_contents( $path );
|
||||||
|
|
||||||
trigger_error( "size = ".var_export( $x, 1 ) );
|
//trigger_error( "size = ".var_export( $x, 1 ) );
|
||||||
|
|
||||||
$tmp = tmpfile();
|
$tmp = tmpfile();
|
||||||
|
|
||||||
|
|
|
@ -39,33 +39,51 @@ namespace OCA\Encryption;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Util {
|
class Util {
|
||||||
|
|
||||||
# DONE: add method to check if file is encrypted using new system
|
|
||||||
# DONE: add method to check if file is encrypted using old system
|
|
||||||
# DONE: add method to fetch legacy key
|
|
||||||
# DONE: add method to decrypt legacy encrypted data
|
|
||||||
# DONE: fix / test the crypt stream proxy class
|
|
||||||
# DONE: replace cryptstream wrapper new AES based system
|
|
||||||
# DONE: Encryption works for writing new text files in web ui
|
|
||||||
# DONE: reading unencrypted files when encryption is enabled works via webdav
|
|
||||||
|
|
||||||
# TODO: file uploaded via web ui get encrypted
|
|
||||||
# TODO: new files created and uploaded via webdav get encrypted
|
|
||||||
|
|
||||||
# TODO: add support for optional recovery user in case of lost passphrase / keys
|
# Web UI:
|
||||||
# TODO: add admin optional required long passphrase for users
|
|
||||||
# TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
|
|
||||||
# TODO: add UI buttons for encrypt / decrypt everything?
|
|
||||||
|
|
||||||
# TODO: add method to encrypt all user files using new system
|
## DONE: files created via web ui are encrypted
|
||||||
# TODO: add method to decrypt all user files using new system
|
## DONE: file created & encrypted via web ui are readable in web ui
|
||||||
# TODO: add method to encrypt all user files using old system
|
|
||||||
# TODO: add method to decrypt all user files using old system
|
|
||||||
|
# WebDAV:
|
||||||
|
|
||||||
|
## DONE: new data filled files added via webdav get encrypted
|
||||||
|
## DONE: new data filled files added via webdav are readable via webdav
|
||||||
|
## DONE: reading unencrypted files when encryption is enabled works via webdav
|
||||||
|
|
||||||
|
# TODO: files created & encrypted via web ui are readable via webdav
|
||||||
|
|
||||||
|
|
||||||
|
# Legacy support:
|
||||||
|
|
||||||
|
## DONE: add method to check if file is encrypted using new system
|
||||||
|
## DONE: add method to check if file is encrypted using old system
|
||||||
|
## DONE: add method to fetch legacy key
|
||||||
|
## DONE: add method to decrypt legacy encrypted data
|
||||||
|
|
||||||
|
## TODO: add method to encrypt all user files using new system
|
||||||
|
## TODO: add method to decrypt all user files using new system
|
||||||
|
## TODO: add method to encrypt all user files using old system
|
||||||
|
## TODO: add method to decrypt all user files using old system
|
||||||
|
|
||||||
|
|
||||||
|
# Admin UI:
|
||||||
|
|
||||||
|
## TODO: add support for optional recovery in case of lost passphrase / keys
|
||||||
|
## TODO: add admin optional required long passphrase for users
|
||||||
|
## TODO: add UI buttons for encrypt / decrypt everything
|
||||||
|
## TODO: implement flag system to allow user to specify encryption by folder, subfolder, etc.
|
||||||
|
|
||||||
|
|
||||||
|
# Integration testing:
|
||||||
|
|
||||||
|
## TODO: test new encryption with webdav
|
||||||
|
## TODO: test new encryption with versioning
|
||||||
|
## TODO: test new encryption with sharing
|
||||||
|
## TODO: test new encryption with proxies
|
||||||
|
|
||||||
# TODO: test new encryption with webdav
|
|
||||||
# TODO: test new encryption with versioning
|
|
||||||
# TODO: test new encryption with sharing
|
|
||||||
# TODO: test new encryption with proxies
|
|
||||||
|
|
||||||
# NOTE: Curretly code on line 206 onwards in lib/proxy.php needs work. This code is executed when webdav writes take place, and appears to need to convert streams into fopen resources. Currently code within the if statement on 215 is not executing. Investigate the paths (handled there (which appear to be blank), and whether oc_fsv is borking them during processing.
|
# NOTE: Curretly code on line 206 onwards in lib/proxy.php needs work. This code is executed when webdav writes take place, and appears to need to convert streams into fopen resources. Currently code within the if statement on 215 is not executing. Investigate the paths (handled there (which appear to be blank), and whether oc_fsv is borking them during processing.
|
||||||
|
|
||||||
|
@ -73,6 +91,7 @@ class Util {
|
||||||
|
|
||||||
# NOTE: for some reason file_get_contents is not working in proxy class postfopen. The same line works in sscce, but always returns an empty string in proxy.php. this is the same regardless of whether oc_fs, oc_fsv, or direct use of phps file_get_contents is used
|
# NOTE: for some reason file_get_contents is not working in proxy class postfopen. The same line works in sscce, but always returns an empty string in proxy.php. this is the same regardless of whether oc_fs, oc_fsv, or direct use of phps file_get_contents is used
|
||||||
|
|
||||||
|
|
||||||
private $view; // OC_FilesystemView object for filesystem operations
|
private $view; // OC_FilesystemView object for filesystem operations
|
||||||
private $pwd; // User Password
|
private $pwd; // User Password
|
||||||
private $client; // Client side encryption mode flag
|
private $client; // Client side encryption mode flag
|
||||||
|
@ -241,8 +260,14 @@ class Util {
|
||||||
*/
|
*/
|
||||||
public function isEncryptedPath( $path ) {
|
public function isEncryptedPath( $path ) {
|
||||||
|
|
||||||
|
// Disable encryption proxy so data retreived is in its
|
||||||
|
// original form
|
||||||
|
\OC_FileProxy::$enabled = false;
|
||||||
|
|
||||||
$data = $this->view->file_get_contents( $path );
|
$data = $this->view->file_get_contents( $path );
|
||||||
|
|
||||||
|
\OC_FileProxy::$enabled = true;
|
||||||
|
|
||||||
return Crypt::isEncryptedContent( $data );
|
return Crypt::isEncryptedContent( $data );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,109 @@
|
||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2012 Robin Appelman <icewind@owncloud.com>
|
* Copyright (c) 2012 Sam Tuke <samtuke@owncloud.com>,
|
||||||
|
* and Robin Appelman <icewind@owncloud.com>
|
||||||
* This file is licensed under the Affero General Public License version 3 or
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
* later.
|
* later.
|
||||||
* See the COPYING-README file.
|
* See the COPYING-README file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
require_once "PHPUnit/Framework/TestCase.php";
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Generator.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/MockInterface.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Mock.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Container.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Configuration.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CompositeExpectation.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/ExpectationDirector.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Expectation.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Exception.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/CountValidatorAbstract.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/Exception.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CountValidator/Exact.php' );
|
||||||
|
|
||||||
|
use \Mockery as m;
|
||||||
|
use OCA\Encryption;
|
||||||
|
|
||||||
|
class Test_Util extends \PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
public function setUp() {
|
||||||
|
|
||||||
|
$this->proxy = new Encryption\Proxy();
|
||||||
|
|
||||||
|
$this->tmpFileName = "tmpFile-".time();
|
||||||
|
|
||||||
|
$this->privateKey = file_get_contents( realpath( dirname(__FILE__).'/data/admin.public.key' ) );
|
||||||
|
$this->publicKey = file_get_contents( realpath( dirname(__FILE__).'/data/admin.private.key' ) );
|
||||||
|
$this->encDataShort = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester-enc' ) );
|
||||||
|
$this->encDataShortKey = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester.key' ) );
|
||||||
|
|
||||||
|
$this->dataShort = file_get_contents( realpath( dirname(__FILE__).'/data/yoga-manchester' ) );
|
||||||
|
$this->dataLong = file_get_contents( realpath( dirname(__FILE__).'/../lib/crypt.php' ) );
|
||||||
|
$this->longDataPath = realpath( dirname(__FILE__).'/../lib/crypt.php' );
|
||||||
|
|
||||||
|
$this->data1 = file_get_contents( realpath( dirname(__FILE__).'/../../../data/admin/files/enc-test.txt' ) );
|
||||||
|
|
||||||
|
$this->userId = 'admin';
|
||||||
|
$this->pass = 'admin';
|
||||||
|
|
||||||
|
$_SESSION['enckey'] = '-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDiH3EA4EpFA7Fx
|
||||||
|
s2dyyfL5jwXeYXrTqQJ6DqKgGn8VsbT3eu8R9KzM2XitVwZe8c8L52DvJ06o5vg0
|
||||||
|
GqPYxilFdOFJe/ggac5Tq8UmJiZS4EqYEMwxBIfIyWTxeGV06/0HOwnVAkqHMcBz
|
||||||
|
64qldtgi5O8kZMEM2/gKBgU0kMLJzM+8oEWhL1+gsUWQhxd8cKLXypS6iWgqFJrz
|
||||||
|
f/X0hJsJR+gyYxNpahtnjzd/LxLAETrOMsl2tue+BAxmjbAM0aG0NEM0div+b59s
|
||||||
|
2uz/iWbxImp5pOdYVKcVW89D4XBMyGegR40trV2VwiuX1blKCfdjMsJhiaL9pymp
|
||||||
|
ug1wzyQFAgMBAAECggEAK6c+PZkPPXuVCgpEcliiW6NM0r2m5K3AGKgypQ34csu3
|
||||||
|
z/8foCvIIFPrhCtEw5eTDQ1CHWlNOjY8vHJYJ0U6Onpx86nHIRrMBkMm8FJ1G5LJ
|
||||||
|
U8oKYXwqaozWu/cuPwA//OFc6I5krOzh5n8WaRMkbrgbor8AtebRX74By0AXGrXe
|
||||||
|
cswJI7zR96oFn4Dm7Pgvpg5Zhk1vFJ+w6QtH+4DDJ6PBvlZsRkGxYBLGVd/3qhAI
|
||||||
|
sBAyjFlSzuP4eCRhHOhHC/e4gmAH9evFVXB88jFyRZm3K+jQ5W5CwrVRBCV2lph6
|
||||||
|
2B6P7CBJN+IjGKMhy+75y13UvvKPv9IwH8Fzl2x1gQKBgQD8qQOr7a6KhSj16wQE
|
||||||
|
jim2xqt9gQ2jH5No405NrKs/PFQQZnzD4YseQsiK//NUjOJiUhaT+L5jhIpzINHt
|
||||||
|
RJpt3bGkEZmLyjdjgTpB3GwZdXa28DNK9VdXZ19qIl/ZH0qAjKmJCRahUDASMnVi
|
||||||
|
M4Pkk9yx9ZIKkri4TcuMWqc0DQKBgQDlHKBTITZq/arYPD6Nl3NsoOdqVRqJrGay
|
||||||
|
0TjXAVbBXe46+z5lnMsqwXb79nx14hdmSEsZULrw/3f+MnQbdjMTYLFP24visZg9
|
||||||
|
MN8vAiALiiiR1a+Crz+DTA1Q8sGOMVCMqMDmD7QBys3ZuWxuapm0txAiIYUtsjJZ
|
||||||
|
XN76T4nZ2QKBgQCHaT3igzwsWTmesxowJtEMeGWomeXpKx8h89EfqA8PkRGsyIDN
|
||||||
|
qq+YxEoe1RZgljEuaLhZDdNcGsjo8woPk9kAUPTH7fbRCMuutK+4ZJ469s1tNkcH
|
||||||
|
QX5SBcEJbOrZvv967ehe3VQXmJZq6kgnHVzuwKBjcC2ZJRGDFY6l5l/+cQKBgCqh
|
||||||
|
+Adf/8NK7paMJ0urqfPFwSodKfICXZ3apswDWMRkmSbqh4La+Uc8dsqN5Dz/VEFZ
|
||||||
|
JHhSeGbN8uMfOlG93eU2MehdPxtw1pZUWMNjjtj23XO9ooob2CKzbSrp8TBnZsi1
|
||||||
|
widNNr66oTFpeo7VUUK6acsgF6sYJJxSVr+XO1yJAoGAEhvitq8shNKcEY0xCipS
|
||||||
|
k1kbgyS7KKB7opVxI5+ChEqyUDijS3Y9FZixrRIWE6i2uGu86UG+v2lbKvSbM4Qm
|
||||||
|
xvbOcX9OVMnlRb7n8woOP10UMY+ZE2x+YEUXQTLtPYq7F66e1OfxltstMxLQA+3d
|
||||||
|
Y1d5piFV8PXK3Fg2F+Cj5qg=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
|
';
|
||||||
|
|
||||||
|
\OC_User::setUserId( $this->userId );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testpreFile_get_contents() {
|
||||||
|
|
||||||
|
// This won't work for now because mocking of the static keymanager class isn't working :(
|
||||||
|
|
||||||
|
// $mock = m::mock( 'alias:OCA\Encryption\Keymanager' );
|
||||||
|
//
|
||||||
|
// $mock->shouldReceive( 'getFileKey' )->times(2)->andReturn( $this->encDataShort );
|
||||||
|
//
|
||||||
|
// $encrypted = $this->proxy->postFile_get_contents( 'data/'.$this->tmpFileName, $this->encDataShortKey );
|
||||||
|
//
|
||||||
|
// $this->assertNotEquals( $this->dataShort, $encrypted );
|
||||||
|
//
|
||||||
|
// var_dump($encrypted);
|
||||||
|
|
||||||
|
$decrypted = $this->proxy->postFile_get_contents( 'data/admin/files/enc-test.txt', $this->data1 );
|
||||||
|
|
||||||
|
var_dump($decrypted);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// class Test_CryptProxy extends UnitTestCase {
|
// class Test_CryptProxy extends UnitTestCase {
|
||||||
// private $oldConfig;
|
// private $oldConfig;
|
||||||
// private $oldKey;
|
// private $oldKey;
|
||||||
|
|
|
@ -9,9 +9,10 @@
|
||||||
require_once "PHPUnit/Framework/TestCase.php";
|
require_once "PHPUnit/Framework/TestCase.php";
|
||||||
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
|
require_once realpath( dirname(__FILE__).'/../../../lib/base.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery.php' );
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Container.php' );
|
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Generator.php' );
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Generator.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/MockInterface.php' );
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/MockInterface.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Mock.php' );
|
||||||
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Container.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Configuration.php' );
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/Configuration.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CompositeExpectation.php' );
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/CompositeExpectation.php' );
|
||||||
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/ExpectationDirector.php' );
|
require_once realpath( dirname(__FILE__).'/../../../3rdparty/mockery/Mockery/ExpectationDirector.php' );
|
||||||
|
|
Loading…
Reference in a new issue