/** * Get the repository XML as a string that can be returned to the browser or cached for future use. * * @return string */ private function _getRepoXML() { $repo = new RepoXML(); $repo->setDescription(ConfigHandler::Get('/package_repository/description')); $dir = Factory::Directory(\ConfigHandler::Get('/package_repository/base_directory')); $coredir = $dir->getPath() . 'core/'; $componentdir = $dir->getPath() . 'components/'; $themedir = $dir->getPath() . 'themes/'; $tmpdir = Factory::Directory('tmp/exports/'); $gpg = new Core\GPG\GPG(); $keysfound = []; $private = ConfigHandler::Get('/package_repository/is_private') || strpos($dir->getPath(), ROOT_PDIR) !== 0; $addedpackages = 0; $failedpackages = 0; $iterator = new \Core\Filestore\DirectoryIterator($dir); // Only find signed packages. $iterator->findExtensions = ['asc']; // Recurse into sub directories $iterator->recursive = true; // No directories $iterator->findDirectories = false; // Just files $iterator->findFiles = true; // And sort them by their filename to make things easy. $iterator->sortBy('filename'); // Ensure that the necessary temp directory exists. $tmpdir->mkdir(); foreach ($iterator as $file) { /** @var \Core\Filestore\File $file */ $fullpath = $file->getFilename(); // Used in the XML file. if ($private) { $relpath = \Core\resolve_link('/packagerepository/download?file=' . substr($file->getFilename(), strlen($dir->getPath()))); } else { $relpath = $file->getFilename(ROOT_PDIR); } // Drop the .asc extension. $basename = $file->getBasename(true); // Tarball of the temporary package $tgz = Factory::File($tmpdir->getPath() . $basename); $output = []; // I need to 1) retrieve and 2) verify the key for this package. try { $signature = $gpg->verifyFileSignature($fullpath); if (!in_array($signature->keyID, $keysfound)) { $repo->addKey($signature->keyID, null, null); $keysfound[] = $signature->keyID; } } catch (\Exception $e) { trigger_error($fullpath . ' was not able to be verified as authentic, (probably because the GPG public key was not available)'); $failedpackages++; continue; } // decode and untar it in a temp directory to get the package.xml file. exec('gpg --homedir "' . GPG_HOMEDIR . '" -q -d "' . $fullpath . '" > "' . $tgz->getFilename() . '" 2>/dev/null', $output, $ret); if ($ret) { trigger_error('Decryption of file ' . $fullpath . ' failed!'); $failedpackages++; continue; } exec('tar -xzf "' . $tgz->getFilename() . '" -C "' . $tmpdir->getPath() . '" ./package.xml', $output, $ret); if ($ret) { trigger_error('Unable to extract package.xml from' . $tgz->getFilename()); unlink($tmpdir->getPath() . $basename); $failedpackages++; continue; } // Read in that package file and append it to the repo xml. $package = new PackageXML($tmpdir->getPath() . 'package.xml'); $package->getRootDOM()->setAttribute('key', $signature->keyID); $package->setFileLocation($relpath); $repo->addPackage($package); $addedpackages++; // But I can still cleanup! unlink($tmpdir->getPath() . 'package.xml'); $tgz->delete(); } return $repo->asPrettyXML(); }
public static function _UploadHandler(Form $form) { $localfile = \Core\Filestore\Factory::File($form->getElement('upload')->get('value')); $localobj = $localfile->getContentsObject(); if(!$localobj instanceof Core\Filestore\Contents\ContentTGZ){ $localfile->delete(); \Core\set_message('Invalid file uploaded', 'error'); return false; } $tmpdir = $localobj->extract('tmp/installer-' . Core::RandomHex(4)); // There should now be a package.xml metafile inside that temporary directory. // Parse it to get the necessary information for this package. $metafile = \Core\Filestore\Factory::File($tmpdir->getPath() . 'package.xml'); if(!$metafile->exists()){ $localfile->delete(); $tmpdir->delete(); \Core\set_message('Invalid package, package does not contain a "package.xml" file.'); return false; } $pkg = new PackageXML($metafile->getFilename()); $key = str_replace(' ', '-', strtolower($pkg->getName())); $name = $pkg->getName(); $type = $pkg->getType(); $version = $pkg->getVersion(); // Validate the contents of the package. if(!( $type == 'component' || $type == 'theme' || $type == 'core' )){ $localfile->delete(); $tmpdir->delete(); \Core\set_message('Invalid package, package does not appear to be a valid Core package.'); return false; } // Now that the data is extracted in a temporary directory, extract every file in the destination. /** @var $datadir \Core\Filestore\Directory */ $datadir = $tmpdir->get('data/'); if(!$datadir){ \Core\set_message('Invalid package, package does not contain a "data" directory.'); return false; } if($type == 'component'){ $destdir = ROOT_PDIR . 'components/' . $key . '/'; } elseif($type == 'theme'){ $destdir = ROOT_PDIR . 'themes/' . $key . '/'; } else{ $destdir = ROOT_PDIR . '/'; } try{ // Will give me an array of Files in the data directory. $files = $datadir->ls(null, true); // Used to get the relative path for each contained file. $datalen = strlen($datadir->getPath()); foreach($files as $file){ if(!$file instanceof \Core\Filestore\Backends\FileLocal) continue; // It's a file, copy it over. // To do so, resolve the directory path inside the temp data dir. $dest = \Core\Filestore\Factory::File($destdir . substr($file->getFilename(), $datalen)); /** @var $dest \Core\Filestore\Backends\FileLocal */ $dest->copyFrom($file, true); } } catch(Exception $e){ // OH NOES! $localfile->delete(); $tmpdir->delete(); \Core\set_message($e->getMessage(), 'error'); return false; } // Cleanup everything $localfile->delete(); $tmpdir->delete(); // Clear the cache so the next pageload will pick up on the new components and goodies. \Core\Cache::Flush(); \Core\Templates\Backends\Smarty::FlushCache(); // Print a nice message to the user that it completed. \Core\set_message('Successfully installed ' . $name . ' ' . $version, 'success'); return '/updater'; }
public function getPackages() { $pkgs = array(); foreach ($this->getElements('core/package|components/package|themes/package') as $p) { $pkg = new PackageXML(null); $pkg->loadFromNode($p); $pkgs[] = $pkg; } return $pkgs; }
continue; } if ($fileInfo->isDir()) { $node = new DOMElement('dir'); $return->appendChild($node); $node->setAttribute('name', $fileInfo->getBasename()); $this->getDirectoryListing($fileInfo->getPathname(), $node); } else { $node = new DOMElement('file'); $return->appendChild($node); $node->setAttribute('name', $fileInfo->getBasename()); $node->setAttribute('role', 'php'); } } } public function usage() { echo <<<STDOUT Usage: package --version= --source= STDOUT; exit(1); } } $a = $argc; $b = $argv; $c = realpath(__FILE__); if (realpath($argv[0]) == realpath(__FILE__)) { $pxml = new PackageXML(); $pxml->run(); }
public static function RebuildPackages() { $dir = \Core\Filestore\Factory::Directory(\ConfigHandler::Get('/package_repository/base_directory')); $coredir = $dir->getPath() . 'core/'; $componentdir = $dir->getPath() . 'components/'; $themedir = $dir->getPath() . 'themes/'; $tmpdir = \Core\Filestore\Factory::Directory('tmp/exports/'); $gpg = new Core\GPG\GPG(); $keysfound = []; $addedpackages = 0; $failedpackages = 0; $skippedpackages = 0; $ls = $dir->ls('asc', true); \Core\CLI\CLI::PrintProgressBar(0); $totalPackages = sizeof($ls); $percentEach = 100 / $totalPackages; $currentPercent = 0; // Ensure that the necessary temp directory exists. $tmpdir->mkdir(); foreach ($ls as $file) { /** @var \Core\Filestore\File $file */ $fullpath = $file->getFilename(); $relpath = substr($file->getFilename(), strlen($dir->getPath())); $tmpdirpath = $tmpdir->getPath(); // Drop the .asc extension. $basename = $file->getBasename(true); // Tarball of the temporary package $tgz = \Core\Filestore\Factory::File($tmpdirpath . $basename); $output = []; // I need to 1) retrieve and 2) verify the key for this package. try { $signature = $gpg->verifyFileSignature($fullpath); } catch (\Exception $e) { trigger_error($fullpath . ' was not able to be verified as authentic, (probably because the GPG public key was not available)'); $failedpackages++; continue; } // decode and untar it in a temp directory to get the package.xml file. exec('gpg --homedir "' . GPG_HOMEDIR . '" -q -d "' . $fullpath . '" > "' . $tgz->getFilename() . '" 2>/dev/null', $output, $ret); if ($ret) { trigger_error('Decryption of file ' . $fullpath . ' failed!'); $failedpackages++; continue; } // Extract the package.xml metafile, this is critical! exec('tar -xzf "' . $tgz->getFilename() . '" -C "' . $tmpdirpath . '" ./package.xml', $output, $ret); if ($ret) { trigger_error('Unable to extract package.xml from' . $tgz->getFilename()); unlink($tmpdirpath . $basename); $failedpackages++; continue; } // Read in that package file and append it to the repo xml. $package = new PackageXML($tmpdirpath . 'package.xml'); $package->getRootDOM()->setAttribute('key', $signature->keyID); $package->setFileLocation($relpath); // Core has a few differences than most components. if ($package->getKeyName() == 'core') { $pkgName = 'Core Plus'; $chngName = 'Core Plus'; $type = 'core'; $chngDepth = 3; $chngFile = './data/core/CHANGELOG'; $xmlFile = './data/core/component.xml'; } else { $pkgName = $package->getName(); $chngName = ($package->getType() == 'theme' ? 'Theme/' : '') . $package->getName(); $type = $package->getType(); $chngDepth = 2; $chngFile = './data/CHANGELOG'; $xmlFile = './data/' . ($package->getType() == 'theme' ? 'theme.xml' : 'component.xml'); } // Lookup this package in the database or create if it doesn't exist. $model = PackageRepositoryPackageModel::Find(['type = ' . $package->getType(), 'key = ' . $package->getKeyName(), 'version = ' . $package->getVersion()], 1); if (!$model) { $model = new PackageRepositoryPackageModel(); $model->set('type', $type); $model->set('key', $package->getKeyName()); $model->set('version', $package->getVersion()); } // Set the data provided by the package.xml file. $model->set('name', $pkgName); $model->set('gpg_key', $package->getKey()); $model->set('packager', $package->getPackager()); $model->set('file', $relpath); $model->set('description', $package->getDescription()); $model->set('requires', $package->getRequires()); $model->set('provides', $package->getProvides()); $model->set('upgrades', $package->getUpgrades()); unlink($tmpdirpath . 'package.xml'); // Extract out the CHANGELOG file, this is not so critical. // I need strip-components=2 to drop off the "." and "data" prefixes. exec('tar -xzf "' . $tgz->getFilename() . '" -C "' . $tmpdirpath . '" --strip-components=' . $chngDepth . ' ' . $chngFile, $output, $ret); // If there is a CHANGELOG, parse that too! if (file_exists($tmpdirpath . 'CHANGELOG')) { try { $ch = new Core\Utilities\Changelog\Parser($chngName, $tmpdirpath . 'CHANGELOG'); $ch->parse(); // Get the version for this iteration. $chsec = $ch->getSection($model->get('version')); $model->set('packager_name', $chsec->getPackagerName()); $model->set('packager_email', $chsec->getPackagerEmail()); $model->set('datetime_released', $chsec->getReleasedDateUTC()); $model->set('changelog', $chsec->fetchAsHTML(null)); } catch (Exception $e) { // meh, we just won't have a changelog. } finally { if (file_exists($tmpdirpath . 'CHANGELOG')) { // Cleanup unlink($tmpdirpath . 'CHANGELOG'); } } } // Retrieve out the screenshots from this component. exec('tar -xzf "' . $tgz->getFilename() . '" -O ' . $xmlFile . ' > "' . $tmpdirpath . 'comp.xml"', $output, $ret); if (file_exists($tmpdirpath . 'comp.xml')) { try { $images = []; $c = new Component_2_1($tmpdirpath . 'comp.xml'); $screens = $c->getScreenshots(); if (sizeof($screens)) { foreach ($screens as $s) { // Extract out this screen and save it to the filesystem. $archivedFile = dirname($xmlFile) . '/' . $s; $localFile = \Core\Filestore\Factory::File('public/packagerepo-screens/' . $model->get('type') . '-' . $model->get('key') . '-' . $model->get('version') . '/' . basename($s)); // Write something into the file so that it exists on the filesystem. $localFile->putContents(''); // And now tar can extract directly to that destination! exec('tar -xzf "' . $tgz->getFilename() . '" -O ' . $archivedFile . ' > "' . $localFile->getFilename() . '"', $output, $ret); if (!$ret) { // Return code should be 0 on a successful write. $images[] = $localFile->getFilename(false); } } } $model->set('screenshots', $images); } catch (Exception $e) { // meh, we just won't have images.. } finally { if (file_exists($tmpdirpath . 'comp.xml')) { // Cleanup unlink($tmpdirpath . 'comp.xml'); } } } if ($model->changed()) { $model->save(true); $addedpackages++; } else { $skippedpackages++; } // But I can still cleanup! $tgz->delete(); $currentPercent += $percentEach; \Core\CLI\CLI::PrintProgressBar($currentPercent); } // Commit everything! PackageRepositoryPackageModel::CommitSaves(); return ['updated' => $addedpackages, 'skipped' => $skippedpackages, 'failed' => $failedpackages]; }
exec('tar -xzf "' . $tmpdir . $basename . '" -C "' . $tmpdir . '" ./package.xml', $output, $ret); if($ret){ echo 'FAILED!' . NL . 'Unable to extract package.xml from the tarball!' . NL; unlink($tmpdir . $basename); $failedpackages++; continue; } $output = array(); // I also need the GPG key for this specific package. exec('gpg --verify "' . $fullpath . '" 2>&1 | grep "key ID" | sed \'s:.*key ID \([A-Z0-9]*\)$:\1:\'', $output, $result); $key = $output[0]; // Read in that package file and append it to the repo xml. $package = new PackageXML($tmpdir . 'package.xml'); $package->getRootDOM()->setAttribute('key', $key); $package->setFileLocation($relpath); $repo->addPackage($package); $addedpackages++; // But I can still cleanup! unlink($tmpdir . 'package.xml'); unlink($tmpdir . $basename); echo "OK!" . NL; } } else{
/** * Save or get the package XML for this component. This is useful for the * packager * * @param boolean $minified * @param bool|string $filename * * @return string|null */ public function savePackageXML($minified = true, $filename = false) { $packagexml = new PackageXML(); $packagexml->setFromComponent($this); $out = ($minified) ? $packagexml->asMinifiedXML() : $packagexml->asPrettyXML(); if ($filename) { file_put_contents($filename, $out); } else { return $out; } }