/** * We just need to replace the Phar * * If we get to this point: * * 1. We know the signature is signed by the supplier. * 2. The hash was checked into Keyggdrasil, which * was independently vouched for by our peers. * * @param UpdateInfo $info * @param UpdateFile $file * @throws CouldNotUpdate */ protected function install(UpdateInfo $info, UpdateFile $file) { if (!$file->hashMatches($info->getChecksum())) { throw new CouldNotUpdate(\__('Checksum mismatched')); } // Create a backup of the old Gadget: \rename($this->filePath, $this->filePath . '.backup'); \rename($file->getPath(), $this->filePath); $this->log('Begin install process', LogLevel::DEBUG, ['path' => $file->getPath(), 'hash' => $file->getHash(), 'version' => $file->getVersion(), 'size' => $file->getSize()]); // Get metadata from the old version of this Gadget: $oldAlias = Base64UrlSafe::encode(\random_bytes(48)) . '.phar'; $oldGadget = new \Phar($this->filePath, \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME); $oldGadget->setAlias($oldAlias); $oldMetadata = $oldGadget->getMetadata(); unset($oldGadget); unset($oldAlias); // Let's open the update package: $newGadget = new \Phar($this->filePath, \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME, $this->pharAlias); $newGadget->setAlias($this->pharAlias); $metaData = $newGadget->getMetadata(); // We need to do this while we're replacing files. $this->bringSiteDown(); Sandbox::safeRequire('phar://' . $this->pharAlias . '/update_trigger.php', $oldMetadata); // Free up the updater alias $garbageAlias = Base64UrlSafe::encode(\random_bytes(48)) . '.phar'; $newGadget->setAlias($garbageAlias); unset($newGadget); // Now bring it back up. $this->bringSiteBackUp(); // Make sure we update the version info. in the DB cache: $this->updateDBRecord('Gadget', $info); if ($metaData) { $this->updateJSON($info, $metaData); } self::$continuumLogger->store(LogLevel::INFO, 'Gadget update installed', $this->getLogContext($info, $file)); }
/** * Gadget install process. * * 1. Move .phar to the appropriate location. * 2. If this gadget is for a particular cabin, add it to that cabin's * gadgets.json file. * 3. Run the update triggers (install hooks and incremental upgrades). * 4. Clear the cache files. * * @param InstallFile $fileInfo * @return bool */ public function install(InstallFile $fileInfo) : bool { $supplier = $this->supplier->getName(); $fileName = $supplier . '.' . $this->package . '.phar'; $metadata = $this->getMetadata($fileInfo); // Move .phar file to its destination. if (!empty($metadata['cabin'])) { $gadgetConfigFile = ROOT . '/Cabin/' . $metadata['cabin'] . '/config/gadgets.json'; // Cabin-specific gadget $cabin = ROOT . '/Cabin/' . $metadata['cabin'] . '/Gadgets'; if (!\is_dir($cabin)) { $this->log('Could not install; cabin "' . $metadata['cabin'] . '" is not installed.', LogLevel::ERROR); return false; } $filePath = $cabin . '/' . $supplier . '/' . $fileName; if (!\is_dir($cabin . '/' . $supplier)) { \mkdir($cabin . '/' . $supplier, 0775); } } else { $gadgetConfigFile = ROOT . '/config/gadgets.json'; // Universal gadget. (Probably affects the Engine.) $filePath = ROOT . '/Gadgets/' . $supplier . '/' . $fileName; if (!\is_dir(ROOT . '/Gadgets/' . $supplier)) { \mkdir(ROOT . '/Gadgets/' . $supplier, 0775); } } $gadgetConfig = \Airship\loadJSON($gadgetConfigFile); $gadgetConfig[] = [['supplier' => $supplier, 'name' => $this->package, 'version' => $metadata['version'] ?? null, 'path' => $filePath, 'enabled' => true]]; \Airship\saveJSON($gadgetConfigFile, $gadgetConfig); \rename($fileInfo->getPath(), $filePath); // If cabin-specific, add to the cabin's gadget.json if ($metadata['cabin']) { $this->addToCabin($metadata['cabin']); } // Run the update hooks: $alias = 'gadget.' . $fileName; $phar = new \Phar($filePath, \FilesystemIterator::CURRENT_AS_FILEINFO | \FilesystemIterator::KEY_AS_FILENAME); $phar->setAlias($alias); // Run the update trigger. if (\file_exists('phar://' . $alias . '/update_trigger.php')) { Sandbox::safeRequire('phar://' . $alias . '/update_trigger.php'); } self::$continuumLogger->store(LogLevel::INFO, 'Install successful', $this->getLogContext($fileInfo)); // Finally, clear the cache files: return $this->clearCache(); }