--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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());
+ }
+}