LCOV - code coverage report
Current view: top level - muse/framework/extensions/internal - extensioninstaller.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 81 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : #include "extensioninstaller.h"
       2              : 
       3              : #include "global/io/fileinfo.h"
       4              : #include "global/serialization/zipreader.h"
       5              : #include "global/translation.h"
       6              : #include "global/uuid.h"
       7              : 
       8              : #include "../extensionstypes.h"
       9              : #include "extensionsloader.h"
      10              : 
      11              : #include "log.h"
      12              : 
      13              : using namespace muse;
      14              : using namespace muse::extensions;
      15              : 
      16            0 : Ret ExtensionInstaller::isFileSupported(const io::path_t& path) const
      17              : {
      18            0 :     bool isExt = io::FileInfo(path).suffix() == SINGLE_FILE_EXT;
      19            0 :     if (isExt) {
      20            0 :         return muse::make_ok();
      21              :     }
      22            0 :     return muse::make_ret(Ret::Code::NotSupported);
      23              : }
      24              : 
      25            0 : void ExtensionInstaller::installExtension(const io::path_t& srcPath)
      26              : {
      27              :     // check manifest and duplication
      28              : 
      29            0 :     const ZipReader zip(srcPath);
      30            0 :     const ByteArray data = zip.fileData("manifest.json");
      31            0 :     if (data.empty()) {
      32            0 :         LOGE() << "not found manifest.json in: " << srcPath;
      33              : 
      34            0 :         interactive()->error(trc("extensions", "Failed to install extension"),
      35            0 :                              trc("extensions", "The extension does not contain a valid manifest file."),
      36            0 :                              { interactive()->buttonData(IInteractive::Button::Ok) });
      37              : 
      38            0 :         return;
      39              :     }
      40              : 
      41              :     const ExtensionsLoader loader;
      42            0 :     const RetVal<Manifest> m = loader.parseManifest(data);
      43            0 :     if (!m.ret) {
      44            0 :         LOGE() << "failed parse manifest: " << srcPath << ", err: " << m.ret.toString();
      45            0 :         return;
      46              :     }
      47              : 
      48            0 :     const Manifest existingManifest = provider()->manifest(m.val.uri);
      49            0 :     const bool alreadyInstalled = existingManifest.isValid();
      50              : 
      51            0 :     if (!alreadyInstalled) {
      52            0 :         doInstallExtension(srcPath);
      53            0 :         return;
      54              :     }
      55              : 
      56            0 :     if (existingManifest.version == m.val.version) {
      57            0 :         LOGI() << "already installed: " << m.val.uri;
      58              : 
      59            0 :         interactive()->info(trc("extensions", "The extension is already installed."), std::string(),
      60            0 :                             { interactive()->buttonData(IInteractive::Button::Ok) });
      61              : 
      62            0 :         return;
      63              :     }
      64              : 
      65            0 :     if (!existingManifest.isRemovable) {
      66            0 :         interactive()->error(trc("extensions", "This extension cannot be updated."),
      67            0 :                              trc("extensions", "The currently installed version cannot be uninstalled."),
      68            0 :                              { interactive()->buttonData(IInteractive::Button::Ok) });
      69              : 
      70            0 :         return;
      71              :     }
      72              : 
      73            0 :     const std::string text = qtrc("extensions", "Another version of the extension “%1” is already installed (version %2). "
      74              :                                                 "Do you want to replace it with version %3?")
      75            0 :                              .arg(existingManifest.title, existingManifest.version, m.val.version).toStdString();
      76              : 
      77            0 :     interactive()->question(trc("extensions", "Update extension"),
      78              :                             text,
      79              :                             { IInteractive::Button::Cancel, IInteractive::Button::Ok })
      80            0 :     .onResolve(this, [this, existingManifest, srcPath](const IInteractive::Result& result) {
      81            0 :         if (result.isButton(IInteractive::Button::Ok)) {
      82            0 :             uninstallExtension(existingManifest.uri);
      83            0 :             doInstallExtension(srcPath);
      84              :         }
      85            0 :     });
      86            0 : }
      87              : 
      88            0 : void ExtensionInstaller::doInstallExtension(const io::path_t& srcPath)
      89              : {
      90              :     // unpack
      91            0 :     const io::path_t dstPath = configuration()->userPath() + "/"
      92            0 :                                + io::FileInfo(srcPath).baseName() + "_"
      93            0 :                                + Uuid::gen();
      94              : 
      95              :     ZipUnpack zip;
      96            0 :     Ret ret = zip.unpack(srcPath, dstPath);
      97            0 :     if (!ret) {
      98            0 :         LOGE() << "failed unpack from: " << srcPath << ", to: " << dstPath << ", err: " << ret.toString();
      99              : 
     100            0 :         interactive()->error(trc("extensions", "Failed to install extension"),
     101            0 :                              qtrc("extensions", "Error code: %1").arg(ret.toString()).toStdString(),
     102            0 :                              { interactive()->buttonData(IInteractive::Button::Ok) });
     103              : 
     104            0 :         return;
     105              :     }
     106              : 
     107            0 :     LOGI() << "success unpack from: " << srcPath << ", to: " << dstPath;
     108              : 
     109              :     // reload
     110            0 :     provider()->reloadExtensions();
     111            0 : }
     112              : 
     113            0 : void ExtensionInstaller::uninstallExtension(const Uri& uri)
     114              : {
     115            0 :     Manifest manifest = provider()->manifest(uri);
     116            0 :     if (!manifest.isValid()) {
     117            0 :         LOGE() << "Manifest not found for URI: " << uri.toString();
     118            0 :         return;
     119            0 :     } else if (!manifest.isRemovable) {
     120            0 :         LOGE() << "Extension is not removable: " << manifest.uri.toString();
     121            0 :         return;
     122              :     }
     123              : 
     124            0 :     Ret ret;
     125            0 :     if (manifest.legacyPlugin) {
     126            0 :         DO_ASSERT(io::FileInfo::suffix(manifest.path) == "qml");
     127            0 :         ret = fileSystem()->remove(manifest.path);
     128            0 :         if (ret) {
     129            0 :             LOGI() << "Success removed the legacy qml plugin file: " << manifest.path;
     130              :         } else {
     131            0 :             LOGE() << "Failed to remove the legacy qml plugin file: " << manifest.path << ", err: " << ret.toString();
     132              :         }
     133              :     } else {
     134            0 :         ret = fileSystem()->remove(io::dirpath(manifest.path));
     135            0 :         if (ret) {
     136            0 :             LOGI() << "Success removed the extension dir: " << io::dirpath(manifest.path)
     137            0 :                    << "(manifest: " << manifest.path << ")";
     138              :         } else {
     139            0 :             LOGE() << "Failed to remove the extension dir: " << io::dirpath(manifest.path)
     140            0 :                    << "(manifest: " << manifest.path << ")"
     141            0 :                    << ", err: " << ret.toString();
     142              :         }
     143              :     }
     144              : 
     145            0 :     if (ret) {
     146            0 :         provider()->reloadExtensions();
     147              :     }
     148            0 : }
        

Generated by: LCOV version 2.0-1