protected function body() { if (!$this->userHasPrivileges(User::pluginsAdd)) { return false; } $inputs = array('name' => array('isName', 'isNotEmpty')); if (!$this->isInputValid($inputs)) { return false; } $name = $this->getParams('name'); $existingPluginsWithSameName = Repositories::getRepository(Repositories::Plugin)->findBy(['name' => $name]); if (count($existingPluginsWithSameName) > 0) { return $this->death(StringID::PluginNameAlreadyExists); } $pluginFile = $this->getUploadedFile('plugin'); if (!$pluginFile) { return false; } $pluginFolder = Config::get('paths', 'plugins') . $name; if (file_exists($pluginFolder)) { return $this->death(StringID::PluginFolderAlreadyExists); } if (!Filesystem::createDir($pluginFolder)) { return $this->death(StringID::FileSystemError); } if (!Compression::unzip($pluginFile, $pluginFolder)) { $this->death(StringID::UnzipUnsuccessful); goto cleanup_error; } $manifestFile = $pluginFolder . DIRECTORY_SEPARATOR . 'manifest.xml'; $manifest = null; if (!($manifest = $this->parsePluginManifest($manifestFile))) { $this->death(StringID::BadlyFormedPlugin); goto cleanup_error; } if (!file_exists($pluginFolder . DIRECTORY_SEPARATOR . $manifest['mainFile'])) { $this->death(StringID::BadlyFormedPlugin); goto cleanup_error; } $plugin = new \Plugin(); $plugin->setIdentifier($manifest['identifier']); $plugin->setDescription($manifest['description']); $plugin->setConfig($manifest['arguments']); $plugin->setMainfile($name . '/' . $manifest['mainFile']); $plugin->setName($name); $plugin->setType($manifest['type']); Repositories::persistAndFlush($plugin); Filesystem::removeFile($pluginFile); return true; cleanup_error: Filesystem::removeDir($pluginFolder); Filesystem::removeFile($pluginFile); return false; }
protected function body() { $plugins = Repositories::getRepository(Repositories::Plugin)->findAll(); $errors = []; foreach ($plugins as $plugin) { /** @var $plugin \Plugin */ $dbPhpFile = $plugin->getMainfile(); $dbDescription = $plugin->getDescription(); $dbIdentifier = $plugin->getIdentifier(); $pluginDirectory = $this->getMainDirectory($dbPhpFile); if ($pluginDirectory === false) { $errors[] = $plugin->getName() . ": " . Language::get(StringID::ReloadManifests_InvalidFolder); continue; } $manifestFile = Filesystem::combinePaths(Config::get('paths', 'plugins'), $pluginDirectory, "manifest.xml"); $xml = new \DOMDocument(); $success = $xml->load(realpath($manifestFile)); if ($success === false) { $errors[] = $plugin->getName() . ": " . Language::get(StringID::ReloadManifests_MalformedXmlOrFileMissing); continue; } $fileDescription = $xml->getElementsByTagName('description')->item(0); $fileArguments = $xml->getElementsByTagName('argument'); $fileIdentifier = $xml->getElementsByTagName('identifier')->item(0); $fileArgumentsArray = []; for ($i = 0; $i < $fileArguments->length; $i++) { $fileArgumentsArray[] = trim($fileArguments->item($i)->nodeValue); } $fileArgumentsString = implode(';', $fileArgumentsArray); if ($dbDescription !== trim($fileDescription->nodeValue)) { $errors[] = $plugin->getName() . ": " . Language::get(StringID::ReloadManifests_DescriptionMismatch); $plugin->setDescription(trim($fileDescription->nodeValue)); Repositories::persist($plugin); } if ($dbIdentifier !== trim($fileIdentifier->nodeValue)) { $errors[] = $plugin->getName() . ": " . Language::get(StringID::ReloadManifests_IdentifierMismatch); $plugin->setIdentifier(trim($fileIdentifier->nodeValue)); Repositories::persist($plugin); } if ($plugin->getConfig() !== $fileArgumentsString) { $errors[] = $plugin->getName() . ": " . Language::get(StringID::ReloadManifests_ArgumentsMismatch); $plugin->setConfig($fileArgumentsString); Repositories::persist($plugin); } } Repositories::flushAll(); if (count($errors) === 0) { $this->addOutput("text", Language::get(StringID::ReloadManifests_DatabaseCorrespondsToManifests)); } else { $this->addOutput("text", implode('<br>', $errors)); } return true; }
public function testRotation() { if (file_exists('log')) { rmdir('log'); } mkdir("log"); $logger = Logger::create("log")->setDatetimeFormat("")->setEntrySeparator("")->setLineSeparator("")->setPrefix("l")->setSuffix("")->setMaxFileCount(4)->setMaxFileSize(10)->setHeader(""); // Header is therefore one space that separates the (empty) date from the (empty) header text $logger->log("A23456789"); $logger->flush(); $logger->log("B23456789"); $logger->flush(); $logger->log("C23456789"); $logger->flush(); $logger->log("D23456789"); $logger->flush(); $logger->log("E23456789"); $logger->flush(); $logger->log("F23456789"); $logger->flush(); $logger->log("G23456789"); $logger->flush(); // We have filled 7 files, and maximum file count is 4. // It should have gone: // A l0 // B l0, l1 // C l0, l1, l2 // D l0, l1, l2, l3 // E l1, l2, l3, l4 // F l0, l2, l3, l4 // G l0, l1, l3, l4, where l1 is newest and l3 oldest $this->assertFileExists('log/l0'); $this->assertFileExists('log/l1'); $this->assertFileNotExists('log/l2'); $this->assertFileExists('log/l3'); $this->assertFileExists('log/l4'); $this->assertFileNotExists('log/l5'); $this->assertFileNotExists('log/l6'); $this->assertFileNotExists('log/l7'); $this->assertSame(' D23456789', file_get_contents('log/l3')); $this->assertSame(' E23456789', file_get_contents('log/l4')); $this->assertSame(' F23456789', file_get_contents('log/l0')); $this->assertSame(' G23456789', file_get_contents('log/l1')); \asm\utils\Filesystem::removeDir("log"); }
protected function body() { if (!$this->isInputValid(array('id' => 'isIndex'))) { return false; } $id = $this->getParams('id'); /** * @var $attachment \Attachment */ $attachment = Repositories::findEntity(Repositories::Attachment, $id); if (!$this->authorizedToManageLecture($attachment->getLecture())) { return false; } $folder = Config::get('paths', 'attachments'); $file = $attachment->getFile(); RemovalManager::deleteAttachmentById($id); Filesystem::removeFile($folder . $file); return true; }
protected function body() { if (!$this->userHasPrivileges(User::pluginsRemove)) { return false; } if (!$this->isInputValid(array('id' => 'isIndex'))) { return false; } $id = $this->getParams('id'); /** * @var $plugin \Plugin */ $plugin = Repositories::findEntity(Repositories::Plugin, $id); $pluginFolder = Filesystem::combinePaths(Config::get('paths', 'plugins'), $plugin->getName()); if (!Filesystem::removeDir($pluginFolder)) { return $this->death(StringID::FileSystemError); } RemovalManager::deletePluginById($id); return true; }
function run($array) { $zipFile = $array[0]; if (count($array) !== 2) { throw new InvalidArgumentException("This must receive 2 parameters in the array exactly."); } $launcher = new \asm\core\JavaLauncher(); $pluginResults = ""; $response = null; $error = $launcher->launch(Filesystem::combinePaths(CheckerRunner::$xmlCheckRoot, "/files/plugins/XML XQuery/XQueryPlugin.jar"), array($zipFile, $array[1]), $responseString); if (!$error) { if (isset($responseString)) { try { $response = \asm\plugin\PluginResponse::fromXmlString($responseString); } catch (Exception $ex) { $response = \asm\plugin\PluginResponse::createError('Internal error. Plugin did not supply valid response XML and this error occured: ' . $ex->getMessage() . '. Plugin instead supplied this response string: ' . $responseString); } } } else { $response = \asm\plugin\PluginResponse::createError('Plugin cannot be launched (' . $error . ').'); } return $response; }
/** * Resolves supplied partial path using supplied base. * @param string $parent base for @c $child * @param string $child partial path to be resolved * @return string absolute path of @c $child appended to @c $parent with * OS-dependent directory separators replaced by UNIX-style slashes (or * @throws Exception when the combined paths don't point to an actual file on the filesystem */ private function resolvePath($parent, $child) { $realPath = realpath(Filesystem::combinePaths($parent, $child)); if ($realPath !== false) { $realPath = str_replace('\\', '/', $realPath); return is_dir($realPath) ? $realPath . '/' : $realPath; } else { throw new Exception("The parent path '{$parent}' and the child path '{$child}' combined do not point to any file on the filesystem. Perhaps your internal.ini file is wrong?'"); } }
/** * (Creates and) sets folder for log files to be saved in. * @param string $folder logfile folder */ private function __construct($folder) { Filesystem::createDir($folder, 0700); $this->folder = realpath($folder); }
public function testGetFiles() { $this->assertEquals(['a.txt', 'b.txt'], Filesystem::getFiles(self::TEST_DIRECTORY)); }
/** * Evaluates XPath expressions on XML and outputs results to files. * @param DOMDocument $xmlDom source XML * @param array $expressions XPath expressions * @param array $comments XPath comments belonging to @a $expressions * @return bool true on success */ protected function evaluateXpathExpressions($xmlDom, $expressions, $comments) { $evaluator = new DOMXpath($xmlDom); $exprNumber = 0; for ($i = 0; $i < count($expressions); ++$i) { $expr = $expressions[$i]; $this->useLibxmlErrors(); $result = $evaluator->query($expr); $errors = $this->getLibxmlErrors(); if ($errors) { return $this->failGoal(self::goalValidXpath, "XPath expression is invalid ({$expr})."); } $exprComments = !isset($comments[$i]) || empty($comments[$i]) ? '' : <<<COMMENTS #\tComments from expression file: {$comments[$i]} COMMENTS; $output = <<<HEADER #\tResults for XPath expression: {$expr} {$exprComments} #\tFound {$result->length} matching DOM nodes. HEADER; $delimiter = "\n\n#\t%s result:\n\n"; for ($j = 0; $j < $result->length; ++$j) { $output .= sprintf($delimiter, "Result " . ($j + 1)) . $xmlDom->saveXML($result->item($j)); } Filesystem::stringToFile(sprintf($this->params[self::outputXpathMask], ++$exprNumber), $output); } return $this->reachGoal(self::goalValidXpath); }
/** * Runs plugin with supplied arguments. * * @li First element in @c $args must be a path to ZIP archive with input data. * @li Plugin execution is stopped on all triggered errors or uncaught exceptions * and plugin error is returned. * @li setUp() and execute() methods are run respectively after input is * unpacked and output folder is created. * @li Plugin creates and later removes two temporary folders. It can also create * one ZIP archive with plugin output (path is returned in plugin results). * * @param array $args simple array with plugin arguments * @return PluginResponse plugin results or error */ public final function run(array $args) { set_error_handler(array('asm\\utils\\Utils', 'turnErrorToException')); $cwd = getcwd(); try { if ($args == null || count($args) < 1) { throw new PluginUseException('Data file argument missing'); } $this->dataFolder = Filesystem::tempDir(); $dataFile = array_shift($args); if (!Compression::unzip($dataFile, $this->dataFolder, $unzipMessage)) { $response = PluginResponse::createError("ZIP extraction failed (" . $unzipMessage . ").\n\nPerhaps you did not submit a ZIP file " . "or that file was corrupted during upload. You may try again. Extraction was attempted to folder " . str_replace("\\", "/", $this->dataFolder)); } else { $this->outputFolder = Filesystem::tempDir(); chdir($this->dataFolder); $this->setUp($args); $this->execute(); chdir($cwd); $outputFile = $this->packOutput(); $outputPath = $outputFile != null ? realpath($outputFile) : null; $response = PluginResponse::create($this->results, $outputPath); } } catch (PluginException $e) { $response = PluginResponse::createError($e->getMessage()); } catch (Exception $e) { $response = PluginResponse::createError('Runtime error: ' . $e->getMessage() . " (file " . $e->getFile() . ", line " . $e->getLine()); } // If an exception occurred during $this->setUp or $this->execute, we must still change the current directory back, // in case more plugins are to be run (in a test case battery) chdir($cwd); restore_error_handler(); if ($this->dataFolder != null) { Filesystem::removeDir($this->dataFolder); } if ($this->outputFolder != null) { Filesystem::removeDir($this->outputFolder); } return $response; }
/** * Attempts to find an XML and an XSL filename in the given folder and adds an error if it cannot find them. * @param $fromWhere string directory from where to load the files * @param $xmlFile string The found XML filename. * @param $xslFile string The found XSL filename. */ private function loadFiles($fromWhere, &$xmlFile, &$xslFile) { $xmlFile = false; $xslFile = false; $files = \asm\utils\Filesystem::getFiles($fromWhere); foreach ($files as $file) { if (Utils::endsWith(strtolower($file), ".xml")) { if ($xmlFile === false) { $xmlFile = \asm\utils\Filesystem::combinePaths($fromWhere, $file); } else { $this->addError("There are two or more .xml files in your submission. There must only be one."); } } if (Utils::endsWith(strtolower($file), ".xsl")) { if ($xslFile === false) { $xslFile = \asm\utils\Filesystem::combinePaths($fromWhere, $file); } else { $this->addError("There are two or more .xsl files in your submission. There must only be one."); } } } if ($xmlFile === false) { $this->addError("Your submission must contain an XML file ending with '.xml'."); } if ($xslFile === false) { $this->addError("Your submission must contain an XSL file ending with '.xsl'."); } }
protected function tearDown() { \asm\utils\Filesystem::removeFile("myconfig.ini"); \asm\utils\Filesystem::removeFile("myconfig2.ini"); }
private function runSchemaTest($zipFile, $fulfillment = null, $details = "") { $result = CheckerRunner::runChecker(new XmlSchemaChecker(), Filesystem::combinePaths(CheckerRunner::$testCasesRoot, "SCHEMA", $zipFile), []); CheckerRunner::assert($this, $zipFile, $result, $fulfillment, $details); }
private function runXsltTest($zipFile, $templateCount, $fulfillment = null, $details = "") { $result = CheckerRunner::runChecker(new XsltChecker(), Filesystem::combinePaths(CheckerRunner::$testCasesRoot, "XSLT", $zipFile), [$templateCount]); CheckerRunner::assert($this, $zipFile, $result, $fulfillment, $details); }
<?php namespace asm\unittests; use asm\core\Config; use asm\utils\Filesystem; $xmlcheckRoot = "../../www"; require_once $xmlcheckRoot . "/vendor/autoload.php"; Config::init(Filesystem::combinePaths($xmlcheckRoot, "core/config.ini"), Filesystem::combinePaths($xmlcheckRoot, "core/internal.ini")); class CheckerRunner { public static $xmlCheckRoot = "../../www"; public static $testCasesRoot = "../plugins/cases"; /** * @param $checker mixed A class with a run() method * @param $zipFile string The path to the zip file with the test case * @param $arguments array Configuration of the plugin * @return \asm\plugin\PluginResponse */ public static function runChecker($checker, $zipFile, $arguments) { $testResult = $checker->run(array_merge([$zipFile], $arguments)); return $testResult; } /** * @param $testCase PHPUnit_Framework_TestCase * @param $result \asm\plugin\PluginResponse * @param $fulfillment int * @param $details string */ public static function assert($testCase, $filename, $result, $fulfillment = null, $details = "")
/** * Save pre-uploaded file to permanent storage [stopping]. * @param string $id file ID * @param string $destination destination to which the file is to be moved * @return bool success * @see getUploadedFile() * @see UploadManager */ protected final function saveUploadedFile($id, $destination) { $src = $this->getUploadedFile($id); if (!$src) { return false; } if (!rename($src, Filesystem::realPath($destination))) { return $this->stop(Language::get(StringID::UploadUnsuccessful)); } return true; }
/** * Deletes test with supplied ID (with input & output files). * @param int $id test ID * @return array error properties provided by removalError() or retrievalError(), * or false in case of success */ public static function deleteTestById($id) { /** * @var $test \PluginTest */ $test = Repositories::findEntity(Repositories::PluginTest, $id); $testFolder = Config::get('paths', 'tests'); // Delete input solution file if (is_file(Filesystem::combinePaths($testFolder, $test->getInput()))) { Filesystem::removeFile(Filesystem::combinePaths($testFolder, $test->getInput())); } // Delete plugin test output if (is_file(Filesystem::combinePaths($testFolder, $test->getOutput()))) { Filesystem::removeFile(Filesystem::combinePaths($testFolder, $test->getOutput())); } Repositories::remove($test); return false; }
private function runDtdTest($zipFile, $fulfillment = null, $details = "") { $result = CheckerRunner::runChecker(new Dtd2014Checker(), Filesystem::combinePaths(CheckerRunner::$testCasesRoot, "DTD", $zipFile), [0, 0]); CheckerRunner::assert($this, $zipFile, $result, $fulfillment, $details); }
/** * Unpack ZIP archive to specified folder. * If it contains only a single directory and nothing else, its contents are extracted instead of the entire ZIP file. This also happens if the only other directory is the "__MACOSX" metadata folder that Macintosh operating systems add to generated ZIP files. * * This function is a security vulnerability. Possible attacks include a very large ZIP file, such as a ZIP bomb, or putting in a file with a relative path such as '../etc/passwd'. * * * @param string $archive source archive path * @param string $destination destination folder path * @param [out]string $errorMessage why did the extraction fail? * @return true on success */ public static function unzip($archive, $destination, &$errorMessage = null) { $zip = new ZipArchive(); $errorCode = $zip->open($archive); if ($errorCode !== true) { $errorMessage = "could not open ZIP file (error code " . $errorCode . ")"; return false; } // Otherwise, this is a normal ZIP file. if (!$zip->extractTo($destination)) { $zip->close(); $errorMessage = "extraction failed"; return false; } $zip->close(); // Now, we'll check if the ZIP file contains only a single folder. If so, // then we'll copy its contests to the root temporary folder, then remove the original folder. $files = scandir($destination); if ($files === false) { $errorMessage = "scanning the temporary directory failed"; return false; } // On Linux, scandir returns the "." and ".." pseudofolders we are not interested in $files = array_diff($files, [".", ".."]); // For ZIP files generated on Mac OS X, we are not interested in the metadata folder. $files = array_diff($files, ["__MACOSX"]); if (count($files) === 0) { $errorMessage = "the ZIP file is empty"; return false; } elseif (count($files) === 1) { // We renumber the remaining file/directory so that it is at index 0. It might not have been because of the subtraction of "." and ".." $files = array_values($files); $soleDirectory = Filesystem::combinePaths($destination, $files[0]); if (is_dir($soleDirectory)) { if (Filesystem::copyIntoDirectory($soleDirectory, $destination)) { Filesystem::removeDir($soleDirectory); return true; } else { $errorMessage = "the ZIP file contained a single directory, but copying its contents to temporary directory failed"; return false; } } } return true; }
private function runXPathTest($zipFile, $fulfillment = null, $details = "") { $result = CheckerRunner::runChecker(new XpathChecker(), Filesystem::combinePaths(CheckerRunner::$testCasesRoot, "XPATH", $zipFile), [5]); CheckerRunner::assert($this, $zipFile, $result, $fulfillment, $details); }