/** * Get the metadata stored in the PHP archive. * * @param InstallFile $fileInfo * @return array */ protected function getMetadata(InstallFile $fileInfo) : array { $alias = Base64UrlSafe::encode(\random_bytes(33)) . '.phar'; $phar = new \Phar($fileInfo->getPath(), \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME); $phar->setAlias($alias); $metadata = $phar->getMetadata(); unset($phar); return $metadata; }
/** * Motif install process. * * 1. Extract files to the appropriate directory. * 2. If this is a cabin-specific motif, update motifs.json. * Otherwise, it's a global Motif. Enable for all cabins. * 3. Create symbolic links. * 4. Clear cache files. * * @param InstallFile $fileInfo * @return bool */ public function install(InstallFile $fileInfo) : bool { $path = $fileInfo->getPath(); $zip = new \ZipArchive(); $res = $zip->open($path); if ($res !== true) { $this->log('Could not open the ZipArchive.', LogLevel::ERROR); return false; } // Extraction destination directory $dir = \implode(DIRECTORY_SEPARATOR, [ROOT, 'Motifs', $this->supplier->getName(), $this->package]); if (!\is_dir($dir)) { \mkdir($dir, 0775, true); } // Grab metadata $metadata = \Airship\parseJSON($zip->getArchiveComment(\ZipArchive::FL_UNCHANGED), true); if (isset($metadata['cabin'])) { $cabin = $this->expandCabinName($metadata['cabin']); if (!\is_dir(ROOT . '/Cabin/' . $cabin)) { $this->log('Could not install; cabin "' . $cabin . '" is not installed.', LogLevel::ERROR); return false; } } else { $cabin = null; } // Extract the new files to the current directory if (!$zip->extractTo($dir)) { $this->log('Could not extract Motif to its destination.', LogLevel::ERROR); return false; } // Add to the relevant motifs.json files if ($cabin) { $this->addMotifToCabin($cabin); } else { foreach (\glob(ROOT . '/Cabin/') as $cabin) { if (\is_dir($cabin)) { $this->addMotifToCabin(\Airship\path_to_filename($cabin)); } } } self::$continuumLogger->store(LogLevel::INFO, 'Motif install successful', $this->getLogContext($fileInfo)); // Finally, nuke the cache: return $this->clearCache(); }
/** * Cabin install process. * * 1. Extract files to proper directory. * 2. Run the update triggers (install hooks and incremental upgrades) * 3. Create/update relevant configuration files. * 4. Create symbolic links. * 5. Clear the cache files. * * @param InstallFile $fileInfo * @return bool */ public function install(InstallFile $fileInfo) : bool { $ns = $this->makeNamespace($this->supplier->getName(), $this->package); $alias = 'cabin.' . $this->supplier->getName() . '.' . $this->package . '.phar'; $updater = new \Phar($fileInfo->getPath(), \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME); $updater->setAlias($alias); $metadata = $updater->getMetadata(); // Overwrite files $updater->extractTo(ROOT . '/Cabin/' . $ns); // Run the update trigger. $updateTrigger = ROOT . '/Cabin/' . $ns . '/update_trigger.php'; if (\file_exists($updateTrigger)) { /** * @security Make sure arbitrary RCE isn't possible here. */ \shell_exec('php -dphar.readonly=0 ' . \escapeshellarg($updateTrigger) . ' >/dev/null 2>&1 &'); } // Free up the updater alias $garbageAlias = Base64UrlSafe::encode(\random_bytes(33)) . '.phar'; $updater->setAlias($garbageAlias); unset($updater); self::$continuumLogger->store(LogLevel::INFO, 'Cabin install successful', $this->getLogContext($fileInfo)); return $this->configure($ns, $metadata); }