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 : }
|