/** * Opens a new file. The file is always opened in binary mode. * * @param string $filename */ public function __construct($filename) { $this->targetFilename = $filename; $i = 0; while (true) { try { parent::__construct(FileUtil::getTemporaryFilename('atomic_'), 'xb'); break; } catch (SystemException $e) { // allow at most 5 failures if (++$i === 5) { throw $e; } } } if (!flock($this->resource, LOCK_EX)) { throw new SystemException('Could not get lock on temporary file'); } }
/** * Builds nodes for optional packages, whereas each package exists within * one node with the same parent node, seperated by sequence no (which does * not really matter at this point). */ protected function buildOptionalNodes() { $packages = array(); $optionalPackages = $this->installation->getArchive()->getOptionals(); foreach ($optionalPackages as $package) { // check if already installed if (Package::isAlreadyInstalled($package['name'])) { continue; } // extract package $index = $this->installation->getArchive()->getTar()->getIndexByFilename($package['file']); if ($index === false) { throw new SystemException("Unable to find required package '" . $package['file'] . "' within archive."); } $fileName = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\\.(?:tar\\.gz|tgz|tar)$)!i', '', basename($package['file']))); $this->installation->getArchive()->getTar()->extract($index, $fileName); // get archive data $archive = new PackageArchive($fileName); $archive->openArchive(); // check if all requirements are met $isInstallable = true; foreach ($archive->getOpenRequirements() as $packageName => $package) { if (!isset($package['file'])) { // requirement is neither installed nor shipped, check if it is about to be installed if (!isset(self::$pendingPackages[$packageName])) { $isInstallable = false; break; } } } // check for exclusions $excludedPackages = $archive->getConflictedExcludedPackages(); if (!empty($excludedPackages)) { $isInstallable = false; } $excludingPackages = $archive->getConflictedExcludingPackages(); if (!empty($excludingPackages)) { $isInstallable = false; } $packages[] = array('archive' => $fileName, 'isInstallable' => $isInstallable, 'package' => $archive->getPackageInfo('name'), 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'packageDescription' => $archive->getLocalizedPackageInfo('packageDescription'), 'selected' => 0); self::$pendingPackages[$archive->getPackageInfo('name')] = $archive->getPackageInfo('version'); } if (!empty($packages)) { $this->parentNode = $this->node; $this->node = $this->getToken(); $this->sequenceNo = 0; $sql = "INSERT INTO\twcf" . WCF_N . "_package_installation_node\n\t\t\t\t\t\t(queueID, processNo, sequenceNo, node, parentNode, nodeType, nodeData)\n\t\t\t\tVALUES\t\t(?, ?, ?, ?, ?, ?, ?)"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->installation->queue->queueID, $this->installation->queue->processNo, $this->sequenceNo, $this->node, $this->parentNode, 'optionalPackages', serialize($packages))); } }
/** * Exports this style. * * @param boolean $templates * @param boolean $images * @param string $packageName */ public function export($templates = false, $images = false, $packageName = '') { // create style tar $styleTarName = FileUtil::getTemporaryFilename('style_', '.tgz'); $styleTar = new TarWriter($styleTarName, true); // append style preview image if ($this->image && @file_exists(WCF_DIR . 'images/' . $this->image)) { $styleTar->add(WCF_DIR . 'images/' . $this->image, '', FileUtil::addTrailingSlash(dirname(WCF_DIR . 'images/' . $this->image))); } // fetch style description $sql = "SELECT\t\tlanguage.languageCode, language_item.languageItemValue\n\t\t\tFROM\t\twcf" . WCF_N . "_language_item language_item\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_language language\n\t\t\tON\t\t(language.languageID = language_item.languageID)\n\t\t\tWHERE\t\tlanguage_item.languageItem = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->styleDescription)); $styleDescriptions = array(); while ($row = $statement->fetchArray()) { $styleDescriptions[$row['languageCode']] = $row['languageItemValue']; } // create style info file $xml = new XMLWriter(); $xml->beginDocument('style', 'http://www.woltlab.com', 'http://www.woltlab.com/XSD/maelstrom/style.xsd'); // general block $xml->startElement('general'); $xml->writeElement('stylename', $this->styleName); // style description foreach ($styleDescriptions as $languageCode => $value) { $xml->writeElement('description', $value, array('language' => $languageCode)); } $xml->writeElement('date', $this->styleDate); $xml->writeElement('version', $this->styleVersion); if ($this->image) { $xml->writeElement('image', $this->image); } if ($this->copyright) { $xml->writeElement('copyright', $this->copyright); } if ($this->license) { $xml->writeElement('license', $this->license); } $xml->endElement(); // author block $xml->startElement('author'); $xml->writeElement('authorname', $this->authorName); if ($this->authorURL) { $xml->writeElement('authorurl', $this->authorURL); } $xml->endElement(); // files block $xml->startElement('files'); $xml->writeElement('variables', 'variables.xml'); if ($templates) { $xml->writeElement('templates', 'templates.tar'); } if ($images) { $xml->writeElement('images', 'images.tar', array('path' => $this->imagePath)); } $xml->endElement(); // append style info file to style tar $styleTar->addString(self::INFO_FILE, $xml->endDocument()); unset($string); // create variable list $xml->beginDocument('variables', 'http://www.woltlab.com', 'http://www.woltlab.com/XSD/maelstrom/styleVariables.xsd'); // get variables $sql = "SELECT\t\tvariable.variableName, value.variableValue\n\t\t\tFROM\t\twcf" . WCF_N . "_style_variable_value value\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_style_variable variable\n\t\t\tON\t\t(variable.variableID = value.variableID)\n\t\t\tWHERE\t\tvalue.styleID = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->styleID)); while ($row = $statement->fetchArray()) { $xml->writeElement('variable', $row['variableValue'], array('name' => $row['variableName'])); } // append variable list to style tar $styleTar->addString('variables.xml', $xml->endDocument()); unset($string); if ($templates && $this->templateGroupID) { $templateGroup = new TemplateGroup($this->templateGroupID); // create templates tar $templatesTarName = FileUtil::getTemporaryFilename('templates', '.tar'); $templatesTar = new TarWriter($templatesTarName); FileUtil::makeWritable($templatesTarName); // append templates to tar // get templates $sql = "SELECT\t\ttemplate.*, package.package\n\t\t\t\tFROM\t\twcf" . WCF_N . "_template template\n\t\t\t\tLEFT JOIN\twcf" . WCF_N . "_package package\n\t\t\t\tON\t\t(package.packageID = template.packageID)\n\t\t\t\tWHERE\t\ttemplate.templateGroupID = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->templateGroupID)); while ($row = $statement->fetchArray()) { $packageDir = 'com.woltlab.wcf'; $package = null; if ($row['application'] != 'wcf') { $application = ApplicationHandler::getInstance()->getApplication($row['application']); $package = PackageCache::getInstance()->getPackage($application->packageID); $packageDir = $package->package; } else { $application = ApplicationHandler::getInstance()->getWCF(); $package = PackageCache::getInstance()->getPackage($application->packageID); } $filename = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR . $package->packageDir . 'templates/' . $templateGroup->templateGroupFolderName)) . $row['templateName'] . '.tpl'; $templatesTar->add($filename, $packageDir, dirname($filename)); } // append templates tar to style tar $templatesTar->create(); $styleTar->add($templatesTarName, 'templates.tar', $templatesTarName); @unlink($templatesTarName); } if ($images && ($this->imagePath && $this->imagePath != 'images/')) { // create images tar $imagesTarName = FileUtil::getTemporaryFilename('images_', '.tar'); $imagesTar = new TarWriter($imagesTarName); FileUtil::makeWritable($imagesTarName); // append images to tar $path = FileUtil::addTrailingSlash(WCF_DIR . $this->imagePath); if (file_exists($path) && is_dir($path)) { $handle = opendir($path); $regEx = new Regex('\\.(jpg|jpeg|gif|png|svg)$', Regex::CASE_INSENSITIVE); while (($file = readdir($handle)) !== false) { if (is_file($path . $file) && $regEx->match($file)) { $imagesTar->add($path . $file, '', $path); } } } // append images tar to style tar $imagesTar->create(); $styleTar->add($imagesTarName, 'images.tar', $imagesTarName); @unlink($imagesTarName); } // output file content $styleTar->create(); // export as style package if (empty($packageName)) { readfile($styleTarName); } else { // export as package // create package tar $packageTarName = FileUtil::getTemporaryFilename('package_', '.tar.gz'); $packageTar = new TarWriter($packageTarName, true); // append style tar $styleTarName = FileUtil::unifyDirSeparator($styleTarName); $packageTar->add($styleTarName, '', FileUtil::addTrailingSlash(dirname($styleTarName))); // create package.xml $xml->beginDocument('package', 'http://www.woltlab.com', 'http://www.woltlab.com/XSD/maelstrom/package.xsd', array('name' => $packageName)); $xml->startElement('packageinformation'); $xml->writeElement('packagename', $this->styleName); // description foreach ($styleDescriptions as $languageCode => $value) { $xml->writeElement('packagedescription', $value, array('language' => $languageCode)); } $xml->writeElement('version', $this->styleVersion); $xml->writeElement('date', $this->styleDate); $xml->endElement(); $xml->startElement('authorinformation'); $xml->writeElement('author', $this->authorName); if ($this->authorURL) { $xml->writeElement('authorurl', $this->authorURL); } $xml->endElement(); $xml->startElement('requiredpackages'); $xml->writeElement('requiredpackage', 'com.woltlab.wcf', array('minversion' => PackageCache::getInstance()->getPackageByIdentifier('com.woltlab.wcf')->packageVersion)); $xml->endElement(); $xml->startElement('excludedpackages'); $xml->writeElement('excludedpackage', 'com.woltlab.wcf', array('version' => self::EXCLUDE_WCF_VERSION)); $xml->endElement(); $xml->startElement('instructions', array('type' => 'install')); $xml->writeElement('instruction', basename($styleTarName), array('type' => 'style')); $xml->endElement(); // append package info file to package tar $packageTar->addString(PackageArchive::INFO_FILE, $xml->endDocument()); $packageTar->create(); readfile($packageTarName); @unlink($packageTarName); } @unlink($styleTarName); }
/** * Tries to download a package from available update servers. * * @param string $package package identifier * @param array $packageUpdateVersions package update versions * @param boolean $validateInstallInstructions * @return string tmp filename of a downloaded package */ protected function downloadPackage($package, $packageUpdateVersions, $validateInstallInstructions = false) { // get download from cache if ($filename = $this->getCachedDownload($package, $packageUpdateVersions[0]['package'])) { return $filename; } // download file foreach ($packageUpdateVersions as $packageUpdateVersion) { // get auth data $authData = $this->getAuthData($packageUpdateVersion); if ($packageUpdateVersion['filename']) { $request = new HTTPRequest($packageUpdateVersion['filename'], !empty($authData) ? array('auth' => $authData) : array(), array('apiVersion' => PackageUpdate::API_VERSION)); } else { // create request $request = new HTTPRequest($this->packageUpdateServers[$packageUpdateVersion['packageUpdateServerID']]->getDownloadURL(), !empty($authData) ? array('auth' => $authData) : array(), array('apiVersion' => PackageUpdate::API_VERSION, 'packageName' => $packageUpdateVersion['package'], 'packageVersion' => $packageUpdateVersion['packageVersion'])); } try { $request->execute(); } catch (HTTPUnauthorizedException $e) { throw new PackageUpdateUnauthorizedException($request, $this->packageUpdateServers[$packageUpdateVersion['packageUpdateServerID']], $packageUpdateVersion); } $response = $request->getReply(); // check response if ($response['statusCode'] != 200) { throw new SystemException(WCF::getLanguage()->getDynamicVariable('wcf.acp.package.error.downloadFailed', array('__downloadPackage' => $package)) . ' (' . $response['body'] . ')'); } // write content to tmp file $filename = FileUtil::getTemporaryFilename('package_'); $file = new File($filename); $file->write($response['body']); $file->close(); unset($response['body']); // test package $archive = new PackageArchive($filename); $archive->openArchive(); // check install instructions if ($validateInstallInstructions) { $installInstructions = $archive->getInstallInstructions(); if (empty($installInstructions)) { throw new SystemException("Package '" . $archive->getLocalizedPackageInfo('packageName') . "' (" . $archive->getPackageInfo('name') . ") does not contain valid installation instructions."); } } $archive->getTar()->close(); // cache download in session PackageUpdateDispatcher::getInstance()->cacheDownload($package, $packageUpdateVersion['packageVersion'], $filename); return $filename; } return false; }
/** * Validates the upload package input. */ protected function validateUploadPackage() { if (empty($this->uploadPackage['tmp_name'])) { throw new UserInputException('uploadPackage', 'uploadFailed'); } // get filename $this->uploadPackage['name'] = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\.(?:tar\.gz|tgz|tar)$)!i', '', basename($this->uploadPackage['name']))); if (!@move_uploaded_file($this->uploadPackage['tmp_name'], $this->uploadPackage['name'])) { throw new UserInputException('uploadPackage', 'uploadFailed'); } $this->archive = new PackageArchive($this->uploadPackage['name'], $this->package); $this->validateArchive('uploadPackage'); }
/** * Unzips compressed package archives and returns the temporary file name. * * @param string $archive filename * @return string */ public static function unzipPackageArchive($archive) { if (!FileUtil::isURL($archive)) { $tar = new Tar($archive); $tar->close(); if ($tar->isZipped()) { $tmpName = FileUtil::getTemporaryFilename('package_'); if (FileUtil::uncompressFile($archive, $tmpName)) { return $tmpName; } } } return $archive; }
/** * Builds nodes for optional packages, whereas each package exists within * one node with the same parent node, seperated by sequence no (which does * not really matter at this point). */ protected function buildOptionalNodes() { $packages = array(); $optionalPackages = $this->installation->getArchive()->getOptionals(); foreach ($optionalPackages as $package) { // extract package $index = $this->installation->getArchive()->getTar()->getIndexByFilename($package['file']); if ($index === false) { throw new SystemException("Unable to find required package '" . $package['file'] . "' within archive."); } $fileName = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\\.(?:tar\\.gz|tgz|tar)$)!i', '', basename($package['file']))); $this->installation->getArchive()->getTar()->extract($index, $fileName); // get archive data $archive = new PackageArchive($fileName); $archive->openArchive(); $packages[] = array('archive' => $fileName, 'package' => $archive->getPackageInfo('name'), 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'selected' => 0); } if (!empty($packages)) { $this->parentNode = $this->node; $this->node = $this->getToken(); $this->sequenceNo = 0; $sql = "INSERT INTO\twcf" . WCF_N . "_package_installation_node\n\t\t\t\t\t\t(queueID, processNo, sequenceNo, node, parentNode, nodeType, nodeData)\n\t\t\t\tVALUES\t\t(?, ?, ?, ?, ?, ?, ?)"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->installation->queue->queueID, $this->installation->queue->processNo, $this->sequenceNo, $this->node, $this->parentNode, 'optionalPackages', serialize($packages))); } }
/** * Enforces dimensions for given avatar. * * @param string $filename * @return string */ protected function enforceDimensions($filename) { $imageData = getimagesize($filename); if ($imageData[0] > MAX_AVATAR_WIDTH || $imageData[1] > MAX_AVATAR_HEIGHT) { try { $obtainDimensions = true; if (MAX_AVATAR_WIDTH / $imageData[0] < MAX_AVATAR_HEIGHT / $imageData[1]) { if (round($imageData[1] * (MAX_AVATAR_WIDTH / $imageData[0])) < 48) { $obtainDimensions = false; } } else { if (round($imageData[0] * (MAX_AVATAR_HEIGHT / $imageData[1])) < 48) { $obtainDimensions = false; } } $adapter = ImageHandler::getInstance()->getAdapter(); $adapter->loadFile($filename); $filename = FileUtil::getTemporaryFilename(); $thumbnail = $adapter->createThumbnail(MAX_AVATAR_WIDTH, MAX_AVATAR_HEIGHT, $obtainDimensions); $adapter->writeImage($thumbnail, $filename); } catch (SystemException $e) { throw new UserInputException('avatar', 'tooLarge'); } // check filesize (after shrink) if (@filesize($filename) > WCF::getSession()->getPermission('user.profile.avatar.maxSize')) { throw new UserInputException('avatar', 'tooLarge'); } } return $filename; }
/** * Creates the Body (Message, Attachments) for the Mail * Returns the created Body to the function which invoke this class * * @return string mail body */ public function getBody() { $counter = 1; $this->body = ''; if (count($this->getAttachments())) { // add message $this->body .= '--' . $this->getBoundary() . self::$crlf; $this->body .= 'Content-Type: ' . $this->getContentType() . '; charset="UTF-8"' . self::$crlf; $this->body .= 'Content-Transfer-Encoding: 8bit' . self::$crlf; //$this->body .= self::$crlf.self::$crlf; $this->body .= self::$crlf; // wrap lines after 70 characters $this->body .= wordwrap($this->getMessage(), 70); $this->body .= self::$crlf . self::$crlf; $this->body .= '--' . $this->getBoundary() . self::$crlf; // add attachments foreach ($this->getAttachments() as $attachment) { $fileName = $attachment['name']; $path = $attachment['path']; // download file if (FileUtil::isURL($path)) { $tmpPath = FileUtil::getTemporaryFilename('mailAttachment_'); if (!@copy($path, $tmpPath)) { continue; } $path = $tmpPath; } // get file contents $data = @file_get_contents($path); $data = chunk_split(base64_encode($data), 70, self::$crlf); $this->body .= 'Content-Type: application/octetstream; name="' . $fileName . '"' . self::$crlf; $this->body .= 'Content-Transfer-Encoding: base64' . self::$crlf; $this->body .= 'Content-Disposition: attachment; filename="' . $fileName . '"' . self::$crlf . self::$crlf; $this->body .= $data . self::$crlf . self::$crlf; if ($counter < count($this->getAttachments())) { $this->body .= '--' . $this->getBoundary() . self::$crlf; } $counter++; } $this->body .= self::$crlf . '--' . $this->getBoundary() . '--'; } else { //$this->body .= self::$crlf; $this->body .= $this->getMessage(); } return $this->body; }
/** * Validates the upload package input. * * @param string $filename */ protected function validateUploadPackage($filename = '') { $this->activeTabMenuItem = 'upload'; if (empty($filename)) { if (empty($this->uploadPackage['tmp_name'])) { throw new UserInputException('uploadPackage', 'uploadFailed'); } // get filename $this->uploadPackage['name'] = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\\.(?:tar\\.gz|tgz|tar)$)!i', '', basename($this->uploadPackage['name']))); if (!@move_uploaded_file($this->uploadPackage['tmp_name'], $this->uploadPackage['name'])) { throw new UserInputException('uploadPackage', 'uploadFailed'); } $filename = $this->uploadPackage['name']; } if (!PackageValidationManager::getInstance()->validate($filename, false)) { $exception = PackageValidationManager::getInstance()->getException(); if ($exception instanceof PackageValidationException) { switch ($exception->getCode()) { case PackageValidationException::INVALID_PACKAGE_NAME: case PackageValidationException::MISSING_PACKAGE_XML: throw new UserInputException('uploadPackage', 'noValidPackage'); break; } } } $this->package = PackageValidationManager::getInstance()->getPackageValidationArchive()->getPackage(); }
/** * Tries to download a package from available update servers. * * @param string $package package identifier * @param array $packageUpdateVersions package update versions * @return string tmp filename of a downloaded package */ protected function downloadPackage($package, $packageUpdateVersions) { // get download from cache if ($filename = $this->getCachedDownload($package, $packageUpdateVersions[0]['package'])) { return $filename; } // download file $authorizationRequiredException = array(); $systemExceptions = array(); foreach ($packageUpdateVersions as $packageUpdateVersion) { try { // get auth data $authData = $this->getAuthData($packageUpdateVersion); // send request // TODO: Use HTTPRequest if (!empty($packageUpdateVersion['file'])) { $response = PackageUpdateDispatcher::getInstance()->sendRequest($packageUpdateVersion['file'], array(), $authData); } else { $response = PackageUpdateDispatcher::getInstance()->sendRequest($packageUpdateVersion['server'], array('packageName' => $packageUpdateVersion['package'], 'packageVersion' => $packageUpdateVersion['packageVersion']), $authData); } // check response // check http code if ($response['httpStatusCode'] == 401) { throw new PackageUpdateAuthorizationRequiredException($packageUpdateVersion['packageUpdateServerID'], (!empty($packageUpdateVersion['file']) ? $packageUpdateVersion['file'] : $packageUpdateVersion['server']), $response); } if ($response['httpStatusCode'] != 200) { throw new SystemException(WCF::getLanguage()->get('wcf.acp.packageUpdate.error.downloadFailed', array('$package' => $package)) . ' ('.$response['httpStatusLine'].')'); } // write content to tmp file $filename = FileUtil::getTemporaryFilename('package_'); $file = new File($filename); $file->write($response['content']); $file->close(); unset($response['content']); // test package $archive = new PackageArchive($filename); $archive->openArchive(); $archive->getTar()->close(); // cache download in session PackageUpdateDispatcher::getInstance()->cacheDownload($package, $packageUpdateVersion['packageVersion'], $filename); return $filename; } catch (PackageUpdateAuthorizationRequiredException $e) { $authorizationRequiredException[] = $e; } catch (SystemException $e) { $systemExceptions[] = $e; } } if (!empty($authorizationRequiredException)) { throw array_shift($authorizationRequiredException); } if (!empty($systemExceptions)) { throw array_shift($systemExceptions); } return false; }
/** * Exports this style. * * @param boolean $templates * @param boolean $images * @param boolean $icons */ public function export($templates = false, $images = false, $icons = false) { // create style tar $styleTarName = FileUtil::getTemporaryFilename('style_', '.tgz'); $styleTar = new TarWriter($styleTarName, true); // append style preview image if ($this->image && @file_exists(WCF_DIR . $this->image)) { $styleTar->add(WCF_DIR . $this->image, '', FileUtil::addTrailingSlash(dirname(WCF_DIR . $this->image))); } // create style info file $string = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE style SYSTEM \"http://www.woltlab.com/DTDs/SXF/style.dtd\">\n<style>\n"; // general block $string .= "\t<general>\n"; $string .= "\t\t<stylename><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->styleName) : $this->styleName) . "]]></stylename>\n"; // style name if ($this->styleDescription) { $string .= "\t\t<description><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->styleDescription) : $this->styleDescription) . "]]></description>\n"; } // style description if ($this->styleVersion) { $string .= "\t\t<version><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->styleVersion) : $this->styleVersion) . "]]></version>\n"; } // style version if ($this->styleDate) { $string .= "\t\t<date><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->styleDate) : $this->styleDate) . "]]></date>\n"; } // style date if ($this->image) { $string .= "\t\t<image><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', basename($this->image)) : basename($this->image)) . "]]></image>\n"; } // style preview image if ($this->copyright) { $string .= "\t\t<copyright><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->copyright) : $this->copyright) . "]]></copyright>\n"; } // copyright if ($this->license) { $string .= "\t\t<license><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->license) : $this->license) . "]]></license>\n"; } // license $string .= "\t</general>\n"; // author block $string .= "\t<author>\n"; if ($this->authorName) { $string .= "\t\t<authorname><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->authorName) : $this->authorName) . "]]></authorname>\n"; } // author name if ($this->authorURL) { $string .= "\t\t<authorurl><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $this->authorURL) : $this->authorURL) . "]]></authorurl>\n"; } // author URL $string .= "\t</author>\n"; // files block $string .= "\t<files>\n"; $string .= "\t\t<variables>variables.xml</variables>\n"; // variables if ($templates && $this->templateGroupID) { $string .= "\t\t<templates>templates.tar</templates>\n"; } // templates if ($images) { $string .= "\t\t<images>images.tar</images>\n"; } // images if ($icons) { $string .= "\t\t<icons>icons.tar</icons>\n"; } // icons $string .= "\t</files>\n"; $string .= "</style>"; // append style info file to style tar $styleTar->addString(self::INFO_FILE, $string); unset($string); // create variable list $string = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE variables SYSTEM \"http://www.woltlab.com/DTDs/SXF/variables.dtd\">\n<variables>\n"; // get variables $variables = $this->getVariables(); $exportImages = array(); foreach ($variables as $name => $value) { // search images if ($images && $value) { if (preg_match_all('~([^/\\s\\$]+\\.(?:gif|jpg|jpeg|png))~i', $value, $matches)) { $exportImages = array_merge($exportImages, $matches[1]); } } $string .= "\t<variable name=\"" . StringUtil::encodeHTML($name) . "\"><![CDATA[" . StringUtil::escapeCDATA(CHARSET != 'UTF-8' ? StringUtil::convertEncoding(CHARSET, 'UTF-8', $value) : $value) . "]]></variable>\n"; } $string .= "</variables>"; // append variable list to style tar $styleTar->addString('variables.xml', $string); unset($string); if ($templates && $this->templateGroupID) { $templateGroup = new TemplateGroup($this->templateGroupID); // create templates tar $templatesTarName = FileUtil::getTemporaryFilename('templates', '.tar'); $templatesTar = new TarWriter($templatesTarName); @chmod($templatesTarName, 0777); // append templates to tar // get templates $sql = "SELECT\t\ttemplate.*, package.package, package.packageDir,\n\t\t\t\t\t\tparent_package.package AS parentPackage, parent_package.packageDir AS parentPackageDir\n\t\t\t\tFROM\t\twcf" . WCF_N . "_template template\n\t\t\t\tLEFT JOIN\twcf" . WCF_N . "_package package\n\t\t\t\tON\t\t(package.packageID = template.packageID)\n\t\t\t\tLEFT JOIN\twcf" . WCF_N . "_package parent_package\n\t\t\t\tON\t\t(parent_package.packageID = package.parentPackageID)\n\t\t\t\tWHERE\t\ttemplate.templateGroupID = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->templateGroupID)); while ($row = $statement->fetchArray()) { $packageDir = 'com.woltlab.wcf'; if (!empty($row['parentPackageDir'])) { $packageDir = $row['parentPackage']; } else { if (!empty($row['packageDir'])) { $packageDir = $row['package']; } } $filename = FileUtil::addTrailingSlash(FileUtil::getRealPath(WCF_DIR . $row['packageDir'] . 'templates/' . $templateGroup->templateGroupFolderName)) . $row['templateName'] . '.tpl'; $templatesTar->add($filename, $packageDir, dirname($filename)); } // append templates tar to style tar $templatesTar->create(); $styleTar->add($templatesTarName, 'templates.tar', $templatesTarName); @unlink($templatesTarName); } if ($images) { // create images tar $imagesTarName = FileUtil::getTemporaryFilename('images_', '.tar'); $imagesTar = new TarWriter($imagesTarName); @chmod($imagesTarName, 0777); // cache rtl versions foreach ($exportImages as $exportImage) { if (strpos($exportImage, '-ltr')) { $exportImages[] = str_replace('-ltr', '-rtl', $exportImage); } } // append images to tar $path = WCF_DIR . $variables['global.images.location']; if (file_exists($path) && is_dir($path)) { $handle = opendir($path); while (($file = readdir($handle)) !== false) { if (is_file($path . $file) && in_array($file, $exportImages)) { $imagesTar->add($path . $file, '', $path); } } } // append images tar to style tar $imagesTar->create(); $styleTar->add($imagesTarName, 'images.tar', $imagesTarName); @unlink($imagesTarName); } // export icons $iconsLocation = FileUtil::addTrailingSlash($variables['global.icons.location']); if ($icons && $iconsLocation != 'icon/') { // create icons tar $iconsTarName = FileUtil::getTemporaryFilename('icons_', '.tar'); $iconsTar = new TarWriter($iconsTarName); @chmod($iconsTar, 0777); // get package dirs $sql = "SELECT\tpackage, packageDir\n\t\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\t\tWHERE\tisApplication = 1\n\t\t\t\t\tAND (packageDir <> '' OR package = 'com.woltlab.wcf')"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(); while ($row = $statement->fetchArray()) { $iconsDir = FileUtil::getRealPath(WCF_DIR . $row['packageDir']) . $iconsLocation; $packageIcons = array(); if (file_exists($iconsDir)) { $icons = glob($iconsDir . '*.png'); if (is_array($icons)) { foreach ($icons as $icon) { $packageIcons[] = $icon; } } } if (count($packageIcons)) { $iconsTar->add($packageIcons, $row['package'] . '/', $iconsDir); } } $iconsTar->create(); $styleTar->add($iconsTarName, 'icons.tar', $iconsTarName); @unlink($iconsTarName); } // output file content $styleTar->create(); readfile($styleTarName); @unlink($styleTarName); }
/** * Exports post attachments. */ public function exportPostAttachments($offset, $limit) { $sql = "SELECT\t\tchild.*, attach.*, filedata.*\n\t\t\tFROM\t\t" . $this->databasePrefix . "node child\n\t\t\tINNER JOIN\t" . $this->databasePrefix . "node parent\n\t\t\tON\t\tchild.parentid = parent.nodeid\n\t\t\tINNER JOIN\t" . $this->databasePrefix . "node grandparent\n\t\t\tON\t\tparent.parentid = grandparent.nodeid\n\t\t\tINNER JOIN\t" . $this->databasePrefix . "attach attach\n\t\t\tON\t\tchild.nodeid = attach.nodeid\n\t\t\tINNER JOIN\t" . $this->databasePrefix . "filedata filedata\n\t\t\tON\t\tattach.filedataid = filedata.filedataid\n\t\t\t\n\t\t\tINNER JOIN\t(SELECT contenttypeid FROM " . $this->databasePrefix . "contenttype WHERE class IN(?, ?, ?)) x\n\t\t\tON\t\tx.contenttypeid = grandparent.contenttypeid\n\t\t\tINNER JOIN\t(SELECT contenttypeid FROM " . $this->databasePrefix . "contenttype WHERE class = ?) y\n\t\t\tON\t\ty.contenttypeid = parent.contenttypeid\n\t\t\tINNER JOIN\t(SELECT contenttypeid FROM " . $this->databasePrefix . "contenttype WHERE class = ?) z\n\t\t\tON\t\tz.contenttypeid = child.contenttypeid\n\t\t\t\n\t\t\tWHERE\t\tchild.nodeid BETWEEN ? AND ?\n\t\t\tORDER BY\tchild.nodeid ASC"; $statement = $this->database->prepareStatement($sql); // Text in a Text or Poll should be a post // Text in a Channel should be a thread $statement->execute(array('Text', 'Poll', 'Channel', 'Text', 'Attach', $offset + 1, $offset + $limit)); while ($row = $statement->fetchArray()) { $file = null; try { switch ($this->readOption('attachfile')) { case self::ATTACHFILE_DATABASE: $file = FileUtil::getTemporaryFilename('attachment_'); file_put_contents($file, $row['filedata']); break; } // unable to read file -> abort if (!is_file($file) || !is_readable($file)) { continue; } if ($imageSize = @getimagesize($file)) { $row['isImage'] = 1; $row['width'] = $imageSize[0]; $row['height'] = $imageSize[1]; } else { $row['isImage'] = $row['width'] = $row['height'] = 0; } ImportHandler::getInstance()->getImporter('com.woltlab.wbb.attachment')->import($row['nodeid'], array('objectID' => $row['parentid'], 'userID' => $row['userid'] ?: null, 'filename' => $row['filename'], 'filesize' => isset($row['filesize']) ? $row['filesize'] : filesize($file), 'fileType' => FileUtil::getMimeType($file), 'isImage' => $row['isImage'], 'width' => $row['width'], 'height' => $row['height'], 'downloads' => $row['counter'], 'uploadTime' => $row['dateline'], 'showOrder' => isset($row['displayOrder']) ? $row['displayOrder'] : 0), array('fileLocation' => $file)); if ($this->readOption('attachfile') == self::ATTACHFILE_DATABASE) { unlink($file); } } catch (\Exception $e) { if ($this->readOption('attachfile') == self::ATTACHFILE_DATABASE && $file) { @unlink($file); } throw $e; } } }
/** * Exports gallery images. */ public function exportGalleryImages($offset, $limit) { try { // vb 3 $sql = "SELECT\t\tpicture.*, album.albumid, album.dateline, user.username\n\t\t\t\tFROM\t\t" . $this->databasePrefix . "picture picture\n\t\t\t\tLEFT JOIN\t" . $this->databasePrefix . "albumpicture album\n\t\t\t\tON\t\tpicture.pictureid = album.pictureid\n\t\t\t\tLEFT JOIN\t" . $this->databasePrefix . "user user\n\t\t\t\tON\t\tpicture.userid = user.userid\n\t\t\t\tORDER BY\tpicture.pictureid"; $statement = $this->database->prepareStatement($sql, $limit, $offset); $statement->execute(); $vB = 3; } catch (DatabaseException $e) { // vb 4 $sql = "SELECT\t\tattachment.*, attachment.contentid AS albumid, filedata.filedata, filedata.extension,\n\t\t\t\t\t\tfiledata.filesize, filedata.width, filedata.height, user.username\n\t\t\t\tFROM\t\t" . $this->databasePrefix . "attachment attachment\n\t\t\t\tLEFT JOIN\t" . $this->databasePrefix . "filedata filedata\n\t\t\t\tON\t\tattachment.filedataid = filedata.filedataid\n\t\t\t\tLEFT JOIN\t" . $this->databasePrefix . "user user\n\t\t\t\tON\t\tattachment.userid = user.userid\n\t\t\t\tWHERE\t\tattachment.contenttypeid = (SELECT contenttypeid FROM " . $this->databasePrefix . "contenttype contenttype WHERE contenttype.class = 'Album')\n\t\t\t\tORDER BY\tattachment.attachmentid"; $statement = $this->database->prepareStatement($sql, $limit, $offset); $statement->execute(); $vB = 4; } while ($row = $statement->fetchArray()) { try { if ($vB === 4) { switch ($this->readOption('attachfile')) { case self::ATTACHFILE_DATABASE: $file = FileUtil::getTemporaryFilename('attachment_'); file_put_contents($file, $row['filedata']); break; case self::ATTACHFILE_FILESYSTEM: $file = $this->readOption('attachpath'); if (!StringUtil::startsWith($file, '/')) { $file = realpath($this->fileSystemPath . $file); } $file = FileUtil::addTrailingSlash($file); $file .= $row['userid'] . '/' . (isset($row['filedataid']) ? $row['filedataid'] : $row['attachmentid']) . '.attach'; break; case self::ATTACHFILE_FILESYSTEM_SUBFOLDER: $file = $this->readOption('attachpath'); if (!StringUtil::startsWith($file, '/')) { $file = realpath($this->fileSystemPath . $file); } $file = FileUtil::addTrailingSlash($file); $file .= implode('/', str_split($row['userid'])) . '/' . (isset($row['filedataid']) ? $row['filedataid'] : $row['attachmentid']) . '.attach'; break; } } else { switch ($this->readOption('album_dataloc')) { case self::GALLERY_DATABASE: $file = FileUtil::getTemporaryFilename('attachment_'); file_put_contents($file, $row['filedata']); break; case self::GALLERY_FILESYSTEM: case self::GALLERY_FILESYSTEM_DIRECT_THUMBS: $file = $this->readOption('album_picpath'); if (!StringUtil::startsWith($file, '/')) { $file = realpath($this->fileSystemPath . $file); } $file = FileUtil::addTrailingSlash($file); $file .= floor($row['pictureid'] / 1000) . '/' . $row['pictureid'] . '.picture'; break; } } $additionalData = array('fileLocation' => $file); ImportHandler::getInstance()->getImporter('com.woltlab.gallery.image')->import(isset($row['pictureid']) ? $row['pictureid'] : $row['filedataid'], array('userID' => $row['userid'] ?: null, 'username' => $row['username'] ?: '', 'albumID' => $row['albumid'] ?: null, 'title' => $row['caption'], 'description' => '', 'filename' => isset($row['filename']) ? $row['filename'] : '', 'fileExtension' => $row['extension'], 'filesize' => $row['filesize'], 'uploadTime' => $row['dateline'], 'creationTime' => $row['dateline'], 'width' => $row['width'], 'height' => $row['height']), $additionalData); } catch (\Exception $e) { if ($vB === 3 && $this->readOption('album_dataloc') == self::GALLERY_DATABASE && $file) { @unlink($file); } if ($vB === 4 && $this->readOption('attachfile') == self::ATTACHFILE_DATABASE && $file) { @unlink($file); } throw $e; } } }
/** * Handles uploaded preview images. * * @return array<string> */ public function upload() { // save files $files = $this->parameters['__files']->getFiles(); $file = $files[0]; try { if (!$file->getValidationErrorType()) { // shrink preview image if necessary $fileLocation = $file->getLocation(); $imageData = getimagesize($fileLocation); if ($imageData[0] > Style::PREVIEW_IMAGE_MAX_WIDTH || $imageData[1] > Style::PREVIEW_IMAGE_MAX_HEIGHT) { try { $adapter = ImageHandler::getInstance()->getAdapter(); $adapter->loadFile($fileLocation); $fileLocation = FileUtil::getTemporaryFilename(); $thumbnail = $adapter->createThumbnail(Style::PREVIEW_IMAGE_MAX_WIDTH, Style::PREVIEW_IMAGE_MAX_HEIGHT, false); $adapter->writeImage($thumbnail, $fileLocation); $imageData = getimagesize($fileLocation); } catch (SystemException $e) { throw new UserInputException('image'); } } // move uploaded file if (@copy($fileLocation, WCF_DIR . 'images/stylePreview-' . $this->parameters['tmpHash'] . '.' . $file->getFileExtension())) { @unlink($fileLocation); // store extension within session variables WCF::getSession()->register('stylePreview-' . $this->parameters['tmpHash'], $file->getFileExtension()); if ($this->parameters['styleID']) { $this->updateStylePreviewImage($this->style); return array('url' => WCF::getPath() . 'images/stylePreview-' . $this->parameters['styleID'] . '.' . $file->getFileExtension()); } // return result return array('url' => WCF::getPath() . 'images/stylePreview-' . $this->parameters['tmpHash'] . '.' . $file->getFileExtension()); } else { throw new UserInputException('image', 'uploadFailed'); } } } catch (UserInputException $e) { $file->setValidationErrorType($e->getType()); } return array('errorType' => $file->getValidationErrorType()); }
/** * Loads the xml file into a string and returns this string. * * @param string $filename * @return XML $xml */ protected function getXML($filename = '') { if (empty($filename)) { $filename = $this->instruction['value']; } // Search the xml-file in the package archive. // Abort installation in case no file was found. if (($fileIndex = $this->installation->getArchive()->getTar()->getIndexByFilename($filename)) === false) { throw new SystemException("xml file '" . $filename . "' not found in '" . $this->installation->getArchive()->getArchive() . "'"); } // Extract acpmenu file and parse with SimpleXML $xml = new XML(); $tmpFile = FileUtil::getTemporaryFilename('xml_'); try { $this->installation->getArchive()->getTar()->extract($fileIndex, $tmpFile); $xml->load($tmpFile); } catch (\Exception $e) { // bugfix to avoid file caching problems try { $this->installation->getArchive()->getTar()->extract($fileIndex, $tmpFile); $xml->load($tmpFile); } catch (\Exception $e) { $this->installation->getArchive()->getTar()->extract($fileIndex, $tmpFile); $xml->load($tmpFile); } } @unlink($tmpFile); return $xml; }
/** * Strips supernumerous BOMs from a given bytestream. * * If we are dealing with bytestreams being pushed from one program or script to another in a UTF-8 * environment, we might encounter problems with BOMs (Byte Order Marks). E.g., if there's a script * that reads a .tar file via readfile(), and this script is encoded in UTF-8, and being called from another * script which wants to handle the bytestream that results from readfile(). But apparently because of the * UTF-8 encoding of the called script -- at least in some PHP versions -- readfile() adds a UTF-8 BOM * at the beginning of the bytestream. If we do write this bytestream to disk and then try to open the * resulting file, we will get an error because it is no more a valid .tar archive. The same thing happens * if we handle an .xml file and then try to parse it. * So, if bytestreams are being handled in a UTF-8 environment, be sure always to use this function * before writing the bytestream to disk or trying to parse it with an xml parser. * This works regardless of multibyte string support (mb_strpos and friends) being enabled or not. * * Btw, if you try to apply the following to a bytestream read from a .tar file, * you will end up with a file sized zero bytes: * while (($byte = fgetc($fileHandle)) !== false) { * fwrite($fileHandle, $byte); * } * * @param string $sourceContent * @param string $characterEncoding * @return string destinationContent */ public static function stripBoms($sourceContent = '', $characterEncoding = 'UTF-8') { try { // TODO: implement recognition of other BOMs (UTF-7, UTF-16 big endian, UTF-16 little endian etc.) if ($characterEncoding == 'UTF-8') { // get the ASCII codes for the three bytes the UTF-8 BOM is consisting of. $firstByte = intval(0xef); $secondByte = intval(0xbb); $thirdByte = intval(0xbf); } else { return $sourceContent; } // put the bytestream's first three bytes to an array. $workArray = unpack('C3', $sourceContent); if (!is_array($workArray)) { throw new SystemException("Unable to process bytestream."); } // detect the UTF-8 BOM. if ($workArray['1'] == $firstByte && $workArray['2'] == $secondByte && $workArray['3'] == $thirdByte) { $tmpname = FileUtil::getTemporaryFilename('stripBoms_'); $tmpStream = fopen($tmpname, 'w+'); fwrite($tmpStream, $sourceContent); rewind($tmpStream); // cut off the BOM. fseek($tmpStream, 3); // compatibility for PHP < 5.1.0 $destinationContent = stream_get_contents($tmpStream); fclose($tmpStream); @unlink($tmpname); return $destinationContent; } else { return $sourceContent; } } catch (SystemException $e) { throw $e; } }
/** * @see \wcf\form\IForm::validate() */ public function validate() { parent::validate(); if (empty($this->source['name'])) { throw new UserInputException('source'); } if (empty($this->source['tmp_name'])) { throw new UserInputException('source', 'uploadFailed'); } try { // check if the uploaded file is a package $archive = new PackageArchive($this->source['tmp_name']); $archive->openArchive(); // check if the package is an application if ($archive->getPackageInfo('isApplication')) { throw new SystemException("Package is application"); } // check if the package includes a style $containsStyle = false; $installInstructions = $archive->getInstallInstructions(); foreach ($installInstructions as $instruction) { if ($instruction['pip'] == 'style') { $containsStyle = true; break; } } if (!$containsStyle) { throw new SystemException("Package contains no style"); } $filename = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\\.(?:tar\\.gz|tgz|tar)$)!i', '', basename($this->source['name']))); if (!@move_uploaded_file($this->source['tmp_name'], $filename)) { throw new SystemException("Cannot move uploaded file"); } WCF::getSession()->register('stylePackageImportLocation', $filename); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('PackageStartInstall', array('action' => 'install'))); exit; } catch (SystemException $e) { // ignore errors } try { $this->style = StyleEditor::import($this->source['tmp_name']); } catch (\Exception $e) { @unlink($this->source['tmp_name']); throw new UserInputException('source', 'importFailed'); } }