@@ -256,6 +256,10 @@ class Server extends SimpleContainer implements IServerContainer { | |||
return new NullQueryLogger(); | |||
} | |||
}); | |||
$this->registerService('TempManager', function ($c) { | |||
/** @var Server $c */ | |||
return new TempManager(get_temp_dir(), $c->getLogger()); | |||
}); | |||
} | |||
/** | |||
@@ -617,4 +621,13 @@ class Server extends SimpleContainer implements IServerContainer { | |||
function getQueryLogger() { | |||
return $this->query('QueryLogger'); | |||
} | |||
/** | |||
* Get the manager for temporary files and folders | |||
* | |||
* @return \OCP\ITempManager | |||
*/ | |||
function getTempManager() { | |||
return $this->query('TempManager'); | |||
} | |||
} |
@@ -0,0 +1,146 @@ | |||
<?php | |||
/** | |||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> | |||
* This file is licensed under the Affero General Public License version 3 or | |||
* later. | |||
* See the COPYING-README file. | |||
*/ | |||
namespace OC; | |||
use OCP\ILogger; | |||
use OCP\ITempManager; | |||
class TempManager implements ITempManager { | |||
/** | |||
* Current temporary files and folders | |||
* | |||
* @var string[] | |||
*/ | |||
protected $current = array(); | |||
/** | |||
* i.e. /tmp on linux systems | |||
* | |||
* @var string | |||
*/ | |||
protected $tmpBaseDir; | |||
/** | |||
* @var \OCP\ILogger | |||
*/ | |||
protected $log; | |||
/** | |||
* @param string $baseDir | |||
* @param \OCP\ILogger $logger | |||
*/ | |||
public function __construct($baseDir, ILogger $logger) { | |||
$this->tmpBaseDir = $baseDir; | |||
$this->log = $logger; | |||
} | |||
protected function generatePath($postFix) { | |||
return $this->tmpBaseDir . '/oc_tmp_' . md5(time() . rand()) . $postFix; | |||
} | |||
/** | |||
* Create a temporary file and return the path | |||
* | |||
* @param string $postFix | |||
* @return string | |||
*/ | |||
public function getTemporaryFile($postFix = '') { | |||
$file = $this->generatePath($postFix); | |||
if (is_writable($this->tmpBaseDir)) { | |||
touch($file); | |||
$this->current[] = $file; | |||
return $file; | |||
} else { | |||
$this->log->warning( | |||
'Can not create a temporary file in directory {dir}. Check it exists and has correct permissions', | |||
array( | |||
'dir' => $this->tmpBaseDir | |||
) | |||
); | |||
return false; | |||
} | |||
} | |||
/** | |||
* Create a temporary folder and return the path | |||
* | |||
* @param string $postFix | |||
* @return string | |||
*/ | |||
public function getTemporaryFolder($postFix = '') { | |||
$path = $this->generatePath($postFix); | |||
if (is_writable($this->tmpBaseDir)) { | |||
mkdir($path); | |||
$this->current[] = $path; | |||
return $path . '/'; | |||
} else { | |||
$this->log->warning( | |||
'Can not create a temporary folder in directory {dir}. Check it exists and has correct permissions', | |||
array( | |||
'dir' => $this->tmpBaseDir | |||
) | |||
); | |||
return false; | |||
} | |||
} | |||
/** | |||
* Remove the temporary files and folders generated during this request | |||
*/ | |||
public function clean() { | |||
$this->cleanFiles($this->current); | |||
} | |||
protected function cleanFiles($files) { | |||
foreach ($files as $file) { | |||
if (file_exists($file)) { | |||
try { | |||
\OC_Helper::rmdirr($file); | |||
} catch (\UnexpectedValueException $ex) { | |||
$this->log->warning( | |||
"Error deleting temporary file/folder: {file} - Reason: {error}", | |||
array( | |||
'file' => $file, | |||
'error' => $ex->getMessage() | |||
) | |||
); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* Remove old temporary files and folders that were failed to be cleaned | |||
*/ | |||
public function cleanOld() { | |||
$this->cleanFiles($this->getOldFiles()); | |||
} | |||
/** | |||
* Get all temporary files and folders generated by oc older than an hour | |||
* | |||
* @return string[] | |||
*/ | |||
protected function getOldFiles() { | |||
$cutOfTime = time() - 3600; | |||
$files = array(); | |||
$dh = opendir($this->tmpBaseDir); | |||
while (($file = readdir($dh)) !== false) { | |||
if (substr($file, 0, 7) === 'oc_tmp_') { | |||
$path = $this->tmpBaseDir . '/' . $file; | |||
$mtime = filemtime($path); | |||
if ($mtime < $cutOfTime) { | |||
$files[] = $path; | |||
} | |||
} | |||
} | |||
return $files; | |||
} | |||
} |
@@ -264,4 +264,11 @@ interface IServerContainer { | |||
* @return \OCP\Diagnostics\IQueryLogger | |||
*/ | |||
function getQueryLogger(); | |||
/** | |||
* Get the manager for temporary files and folders | |||
* | |||
* @return \OCP\ITempManager | |||
*/ | |||
function getTempManager(); | |||
} |
@@ -0,0 +1,38 @@ | |||
<?php | |||
/** | |||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> | |||
* This file is licensed under the Affero General Public License version 3 or | |||
* later. | |||
* See the COPYING-README file. | |||
*/ | |||
namespace OCP; | |||
interface ITempManager { | |||
/** | |||
* Create a temporary file and return the path | |||
* | |||
* @param string $postFix | |||
* @return string | |||
*/ | |||
public function getTemporaryFile($postFix = ''); | |||
/** | |||
* Create a temporary folder and return the path | |||
* | |||
* @param string $postFix | |||
* @return string | |||
*/ | |||
public function getTemporaryFolder($postFix = ''); | |||
/** | |||
* Remove the temporary files and folders generated during this request | |||
*/ | |||
public function clean(); | |||
/** | |||
* Remove old temporary files and folders that were failed to be cleaned | |||
*/ | |||
public function cleanOld(); | |||
} |
@@ -0,0 +1,143 @@ | |||
<?php | |||
/** | |||
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com> | |||
* This file is licensed under the Affero General Public License version 3 or | |||
* later. | |||
* See the COPYING-README file. | |||
*/ | |||
namespace Test; | |||
use OC\Log; | |||
class NullLogger extends Log { | |||
public function __construct($logger = null) { | |||
//disable original constructor | |||
} | |||
public function log($level, $message, array $context = array()) { | |||
//noop | |||
} | |||
} | |||
class TempManager extends \PHPUnit_Framework_TestCase { | |||
protected $baseDir; | |||
public function setUp() { | |||
$this->baseDir = get_temp_dir() . '/oc_tmp_test'; | |||
if (!is_dir($this->baseDir)) { | |||
mkdir($this->baseDir); | |||
} | |||
} | |||
public function tearDown() { | |||
\OC_Helper::rmdirr($this->baseDir); | |||
} | |||
/** | |||
* @param \Psr\Log\LoggerInterface $logger | |||
* @return \OC\TempManager | |||
*/ | |||
protected function getManager($logger = null) { | |||
if (!$logger) { | |||
$logger = new NullLogger(); | |||
} | |||
return new \OC\TempManager($this->baseDir, $logger); | |||
} | |||
public function testGetFile() { | |||
$manager = $this->getManager(); | |||
$file = $manager->getTemporaryFile('.txt'); | |||
$this->assertStringEndsWith('.txt', $file); | |||
$this->assertTrue(is_file($file)); | |||
$this->assertTrue(is_writable($file)); | |||
file_put_contents($file, 'bar'); | |||
$this->assertEquals('bar', file_get_contents($file)); | |||
} | |||
public function testGetFolder() { | |||
$manager = $this->getManager(); | |||
$folder = $manager->getTemporaryFolder(); | |||
$this->assertStringEndsWith('/', $folder); | |||
$this->assertTrue(is_dir($folder)); | |||
$this->assertTrue(is_writable($folder)); | |||
file_put_contents($folder . 'foo.txt', 'bar'); | |||
$this->assertEquals('bar', file_get_contents($folder . 'foo.txt')); | |||
} | |||
public function testCleanFiles() { | |||
$manager = $this->getManager(); | |||
$file1 = $manager->getTemporaryFile('.txt'); | |||
$file2 = $manager->getTemporaryFile('.txt'); | |||
$this->assertTrue(file_exists($file1)); | |||
$this->assertTrue(file_exists($file2)); | |||
$manager->clean(); | |||
$this->assertFalse(file_exists($file1)); | |||
$this->assertFalse(file_exists($file2)); | |||
} | |||
public function testCleanFolder() { | |||
$manager = $this->getManager(); | |||
$folder1 = $manager->getTemporaryFolder(); | |||
$folder2 = $manager->getTemporaryFolder(); | |||
touch($folder1 . 'foo.txt'); | |||
touch($folder1 . 'bar.txt'); | |||
$this->assertTrue(file_exists($folder1)); | |||
$this->assertTrue(file_exists($folder2)); | |||
$this->assertTrue(file_exists($folder1 . 'foo.txt')); | |||
$this->assertTrue(file_exists($folder1 . 'bar.txt')); | |||
$manager->clean(); | |||
$this->assertFalse(file_exists($folder1)); | |||
$this->assertFalse(file_exists($folder2)); | |||
$this->assertFalse(file_exists($folder1 . 'foo.txt')); | |||
$this->assertFalse(file_exists($folder1 . 'bar.txt')); | |||
} | |||
public function testCleanOld() { | |||
$manager = $this->getManager(); | |||
$oldFile = $manager->getTemporaryFile('.txt'); | |||
$newFile = $manager->getTemporaryFile('.txt'); | |||
$folder = $manager->getTemporaryFolder(); | |||
$nonOcFile = $this->baseDir . '/foo.txt'; | |||
file_put_contents($nonOcFile, 'bar'); | |||
$past = time() - 2 * 3600; | |||
touch($oldFile, $past); | |||
touch($folder, $past); | |||
touch($nonOcFile, $past); | |||
$manager2 = $this->getManager(); | |||
$manager2->cleanOld(); | |||
$this->assertFalse(file_exists($oldFile)); | |||
$this->assertFalse(file_exists($folder)); | |||
$this->assertTrue(file_exists($nonOcFile)); | |||
$this->assertTrue(file_exists($newFile)); | |||
} | |||
public function testLogCantCreateFile() { | |||
$logger = $this->getMock('\Test\NullLogger'); | |||
$manager = $this->getManager($logger); | |||
chmod($this->baseDir, 0500); | |||
$logger->expects($this->once()) | |||
->method('warning') | |||
->with($this->stringContains('Can not create a temporary file in directory')); | |||
$this->assertFalse($manager->getTemporaryFile('.txt')); | |||
} | |||
public function testLogCantCreateFolder() { | |||
$logger = $this->getMock('\Test\NullLogger'); | |||
$manager = $this->getManager($logger); | |||
chmod($this->baseDir, 0500); | |||
$logger->expects($this->once()) | |||
->method('warning') | |||
->with($this->stringContains('Can not create a temporary folder in directory')); | |||
$this->assertFalse($manager->getTemporaryFolder()); | |||
} | |||
} |