<?php
/**
 * @package sapphire
 * @subpackage tests
 */
class UploadTest extends SapphireTest {
	static $fixture_file = 'sapphire/tests/filesystem/UploadTest.yml';

	function testUpload() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload.txt';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => 'txt',
			'error' => UPLOAD_ERR_OK,
		);
		
		$v = new UploadTest_Validator();
		
		// test upload into default folder
		$u1 = new Upload();
		$u1->setValidator($v);
		$u1->load($tmpFile);
		$file1 = $u1->getFile();
		$this->assertTrue(
			file_exists($file1->getFullPath()), 
			'File upload to standard directory in /assets'
		);
		$this->assertTrue(
			(strpos($file1->getFullPath(), Director::baseFolder() . '/assets/' . Upload::$uploads_folder) !== false),	
			'File upload to standard directory in /assets'
		);
		$file1->delete();

		// test upload into custom folder
		$customFolder = 'UploadTest_testUpload';
		$u2 = new Upload();
		$u2->load($tmpFile, $customFolder);
		$file2 = $u2->getFile();
		$this->assertTrue(
			file_exists($file2->getFullPath()), 
			'File upload to custom directory in /assets'
		);
		$this->assertTrue(
			(strpos($file2->getFullPath(), Director::baseFolder() . '/assets/' . $customFolder) !== false),
			'File upload to custom directory in /assets'
		);
		$file2->delete();
		
		unlink($tmpFilePath);
		rmdir(Director::baseFolder() . '/assets/' . $customFolder);
	}
	
	function testAllowedFilesize() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload.txt';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => 'txt',
			'error' => UPLOAD_ERR_OK,
		);
		
		$v = new UploadTest_Validator();
		$v->setAllowedMaxFileSize(array('txt' => 10));
		
		// test upload into default folder
		$u1 = new Upload();
		$u1->setValidator($v);
		$result = $u1->load($tmpFile);
		
		$this->assertFalse($result, 'Load failed because size was too big');
	}

	function testAllowedSizeOnFileWithNoExtension() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => '',
			'error' => UPLOAD_ERR_OK,
		);
		
		$v = new UploadTest_Validator();
		$v->setAllowedMaxFileSize(array('' => 10));
		
		// test upload into default folder
		$u1 = new Upload();
		$u1->setValidator($v);
		$result = $u1->load($tmpFile);
		
		$this->assertFalse($result, 'Load failed because size was too big');
	}

	function testUploadDoesNotAllowUnknownExtension() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload.php';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => 'php',
			'error' => UPLOAD_ERR_OK,
		);
		
		$v = new UploadTest_Validator();
		$v->setAllowedExtensions(array('txt'));
		
		// test upload into default folder
		$u = new Upload();
		$u->setValidator($v);
		$result = $u->load($tmpFile);
		
		$this->assertFalse($result, 'Load failed because extension was not accepted');
	}
	
	function testUploadAcceptsAllowedExtension() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload.txt';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => 'txt',
			'error' => UPLOAD_ERR_OK,
		);
		
		$v = new UploadTest_Validator();
		$v->setAllowedExtensions(array('txt'));
		
		// test upload into default folder
		$u = new Upload();
		$u->setValidator($v);
		$u->load($tmpFile);
		$file = $u->getFile();
		$this->assertTrue(
			file_exists($file->getFullPath()), 
			'File upload to custom directory in /assets'
		);
		$file->delete();
	}
	
	function testUploadDeniesNoExtensionFilesIfNoEmptyStringSetForValidatorExtensions() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => '',
			'error' => UPLOAD_ERR_OK,
		);

		$v = new UploadTest_Validator();
		$v->setAllowedExtensions(array('txt'));
		
		// test upload into default folder
		$u = new Upload();
		$result = $u->load($tmpFile);
		
		$this->assertFalse($result, 'Load failed because extension was not accepted');
		$this->assertEquals(1, count($u->getErrors()), 'There is a single error of the file extension');
		
	}

	// Delete files in the default uploads directory that match the name pattern.
	// @param String $namePattern	A regular expression applied to files in the directory. If the name matches
	// the pattern, it is deleted. Directories, . and .. are excluded.
	function deleteTestUploadFiles($namePattern) {
		$tmpFolder = ASSETS_PATH . "/" . Upload::$uploads_folder;
		$files = scandir($tmpFolder);
		foreach ($files as $f) {
			if ($f == "." || $f == ".." || is_dir("$tmpFolder/$f")) continue;
			if (preg_match($namePattern, $f)) unlink("$tmpFolder/$f");
		}
	}

	function testUploadTarGzFileTwiceAppendsNumber() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload.tar.gz';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => 'tar.gz',
			'error' => UPLOAD_ERR_OK,
		);

		// Make sure there are none here, otherwise they get renamed incorrectly for the test.
		$this->deleteTestUploadFiles("/UploadTesttestUpload.*tar\.gz/");

		// test upload into default folder
		$u = new Upload();
		$u->load($tmpFile);
		$file = $u->getFile();
		$this->assertEquals(
			'UploadTesttestUpload.tar.gz',
			$file->Name,
			'File has a name without a number because it\'s not a duplicate'
		);
		
		$u = new Upload();
		$u->load($tmpFile);
		$file2 = $u->getFile();
		$this->assertEquals(
			'UploadTesttestUpload2.tar.gz',
			$file2->Name,
			'File receives a number attached to the end before the extension'
		);
		
		$file->delete();
		$file2->delete();
	}
	
	function testUploadFileWithNoExtensionTwiceAppendsNumber() {
		// create tmp file
		$tmpFileName = 'UploadTest_testUpload';
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
		$tmpFileContent = '';
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
		file_put_contents($tmpFilePath, $tmpFileContent);
		
		// emulates the $_FILES array
		$tmpFile = array(
			'name' => $tmpFileName,
			'type' => 'text/plaintext',
			'size' => filesize($tmpFilePath),
			'tmp_name' => $tmpFilePath,
			'extension' => 'txt',
			'error' => UPLOAD_ERR_OK,
		);
		
		// Make sure there are none here, otherwise they get renamed incorrectly for the test.
		$this->deleteTestUploadFiles("/UploadTesttestUpload.*/");

		$v = new UploadTest_Validator();
		$v->setAllowedExtensions(array(''));

		// test upload into default folder
		$u = new Upload();
		$u->setValidator($v);
		$u->load($tmpFile);
		$file = $u->getFile();

		$this->assertEquals(
			'UploadTesttestUpload',
			$file->Name,
			'File is uploaded without extension'
		);
		
		$u = new Upload();
		$u->setValidator($v);
		$u->load($tmpFile);
		$file2 = $u->getFile();
		$this->assertEquals(
			'UploadTesttestUpload_2',
			$file2->Name,
			'File receives a number attached to the end'
		);
		
		$file->delete();
		$file2->delete();
	}

}
class UploadTest_Validator extends Upload_Validator implements TestOnly {

	/**
	 * Looser check validation that doesn't do is_upload_file()
	 * checks as we're faking a POST request that PHP didn't generate
	 * itself.
	 * 
	 * @return boolean
	 */
	public function validate() {
		$pathInfo = pathinfo($this->tmpFile['name']);
		// filesize validation
		
		if(!$this->isValidSize()) {
			$ext = (isset($pathInfo['extension'])) ? $pathInfo['extension'] : '';
			$arg = File::format_size($this->getAllowedMaxFileSize($ext));
			$this->errors[] = sprintf(
				_t(
					'File.TOOLARGE', 
					'Filesize is too large, maximum %s allowed.',
					PR_MEDIUM,
					'Argument 1: Filesize (e.g. 1MB)'
				),
				$arg
			);
			return false;
		}

		// extension validation
		if(!$this->isValidExtension()) {
			$this->errors[] = sprintf(
				_t(
					'File.INVALIDEXTENSION', 
					'Extension is not allowed (valid: %s)',
					PR_MEDIUM,
					'Argument 1: Comma-separated list of valid extensions'
				),
				implode(',', $this->allowedExtensions)
			);
			return false;
		}
		
		return true;
	}

}