diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionUtils.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionUtils.java index cefae7610e..8ba9a32cd5 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionUtils.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/ExtensionUtils.java @@ -41,8 +41,8 @@ import utility.application.ApplicationLayout; */ public class ExtensionUtils { - /** - * Magic number that identifies the first bytes of a ZIP archive. This is used to verify that a + /** + * Magic number that identifies the first bytes of a ZIP archive. This is used to verify that a * file is a zip rather than just checking the extension. */ private static final int ZIPFILE = 0x504b0304; @@ -87,7 +87,7 @@ public class ExtensionUtils { * Returns true if the given file or directory is a valid ghidra extension. *
* Note: This means that the zip or directory contains an extension.properties file.
- *
+ *
* @param file the zip or directory to inspect
* @return true if the given file represents a valid extension
*/
@@ -96,12 +96,13 @@ public class ExtensionUtils {
}
public static boolean install(ExtensionDetails extension, File file, TaskMonitor monitor) {
+ boolean success = false;
try {
if (file.isFile()) {
- return unzipToInstallationFolder(extension, file, monitor);
+ success = unzipToInstallationFolder(extension, file, monitor);
}
- return copyToInstallationFolder(file, monitor);
+ success = copyToInstallationFolder(extension, file, monitor);
}
catch (CancelledException e) {
log.info("Extension installation cancelled by user");
@@ -110,7 +111,12 @@ public class ExtensionUtils {
Msg.showError(ExtensionUtils.class, null, "Error Installing Extension",
"Unexpected error installing extension", e);
}
- return false;
+
+ if (success) {
+ extensions.add(extension);
+ }
+
+ return success;
}
public static Set
* Archived extensions may be zip files and directories.
- *
+ *
* @return set of archive extensions
*/
public static Set
* Searching the child directories of a directory allows clients to pick an extension parent
* directory that contains multiple extension directories.
- *
+ *
* @param installDir the directory that contains extension subdirectories
* @return list of extension.properties files
*/
@@ -427,7 +436,7 @@ public class ExtensionUtils {
/**
* Returns an extension.properties or extension.properties.uninstalled file if the given
* directory contains one.
- *
+ *
* @param dir the directory to search
* @return the file, or null if doesn't exist
*/
@@ -448,7 +457,7 @@ public class ExtensionUtils {
/**
* Returns true if the given file is a valid .zip archive.
- *
+ *
* @param file the file to test
* @return true if file is a valid zip
*/
@@ -483,28 +492,30 @@ public class ExtensionUtils {
* location will be deleted.
*
* Note: Any existing folder with the same name will be overwritten.
- *
+ *
+ * @param extension the extension
* @param sourceFolder the extension folder
* @param monitor the task monitor
* @return true if successful
* @throws IOException if the delete or copy fails
* @throws CancelledException if the user cancels the copy
*/
- private static boolean copyToInstallationFolder(File sourceFolder, TaskMonitor monitor)
- throws IOException, CancelledException {
+ private static boolean copyToInstallationFolder(ExtensionDetails extension, File sourceFolder,
+ TaskMonitor monitor) throws IOException, CancelledException {
log.trace("Copying extension from " + sourceFolder);
ApplicationLayout layout = Application.getApplicationLayout();
ResourceFile installDir = layout.getExtensionInstallationDirs().get(0);
File installDirRoot = installDir.getFile(false);
- File newDir = new File(installDirRoot, sourceFolder.getName());
- if (hasExistingExtension(newDir, monitor)) {
+ File destinationFolder = new File(installDirRoot, sourceFolder.getName());
+ if (hasExistingExtension(destinationFolder, monitor)) {
return false;
}
- log.trace("Copying extension to " + newDir);
- FileUtilities.copyDir(sourceFolder, newDir, monitor);
+ log.trace("Copying extension to " + destinationFolder);
+ FileUtilities.copyDir(sourceFolder, destinationFolder, monitor);
+ extension.setInstallDir(destinationFolder);
return true;
}
@@ -514,7 +525,7 @@ public class ExtensionUtils {
*
* Note: This method uses the Apache zip files since they keep track of permissions info;
* the built-in java objects (e.g., ZipEntry) do not.
- *
+ *
* @param extension the extension
* @param file the zip file to unpack
* @param monitor the task monitor
@@ -524,8 +535,7 @@ public class ExtensionUtils {
* @throws IOException if error unzipping zip file
*/
private static boolean unzipToInstallationFolder(ExtensionDetails extension, File file,
- TaskMonitor monitor)
- throws CancelledException, IOException {
+ TaskMonitor monitor) throws CancelledException, IOException {
log.trace("Unzipping extension from " + file);
@@ -554,6 +564,8 @@ public class ExtensionUtils {
}
}
}
+
+ extension.setInstallDir(destinationFolder);
return true;
}
@@ -600,7 +612,7 @@ public class ExtensionUtils {
/**
* Converts Unix permissions to a set of {@link PosixFilePermission}s.
- *
+ *
* @param unixMode integer representation of file permissions
* @return set of POSIX file permissions
*/
diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/Extensions.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/Extensions.java
index 0d553b6a51..b872cacc07 100644
--- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/Extensions.java
+++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/extensions/Extensions.java
@@ -38,12 +38,12 @@ public class Extensions {
}
/**
- * Returns all extensions matching the given details
+ * Returns all extensions matching the given details
* @param e the extension details to match
* @return all matching extensions
*/
public List
* Extensions may be installed/uninstalled by users at runtime, using the
* {@link ExtensionTableProvider}. Installation consists of unzipping the extension archive to an
@@ -57,7 +57,7 @@ public class ExtensionInstaller {
/**
* Installs the given extension file. This can be either an archive (zip) or a directory that
* contains an extension.properties file.
- *
+ *
* @param file the extension to install
* @return true if the extension was successfully installed
*/
@@ -121,8 +121,7 @@ public class ExtensionInstaller {
String archivePath = extension.getArchivePath();
if (archivePath == null) {
- log.error(
- "Cannot install from archive; extension is missing archive path");
+ log.error("Cannot install from archive; extension is missing archive path");
return false;
}
@@ -143,7 +142,7 @@ public class ExtensionInstaller {
* Compares the given extension version to the current Ghidra version. If they are different,
* then the user will be prompted to confirm the installation. This method will return true
* if the versions match or the user has chosen to install anyway.
- *
+ *
* @param extension the extension
* @return true if the versions match or the user has chosen to install anyway
*/
@@ -161,9 +160,7 @@ public class ExtensionInstaller {
String message = "Extension version mismatch.\nName: " + extension.getName() +
"Extension version: " + extVersion + ".\nGhidra version: " + appVersion + ".";
int choice = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null,
- "Extension Version Mismatch",
- message,
- "Install Anyway");
+ "Extension Version Mismatch", message, "Install Anyway");
if (choice != OptionDialog.OPTION_ONE) {
log.info(removeNewlines(message + " Did not install"));
return false;
@@ -201,16 +198,12 @@ public class ExtensionInstaller {
"Installed extension version: " + installedExtension.getVersion() + ".\n\n" +
"To install, click 'Remove Existing', restart Ghidra, then install again.";
int choice = OptionDialog.showOptionDialogWithCancelAsDefaultButton(null,
- "Duplicate Extension",
- message,
- "Remove Existing");
+ "Duplicate Extension", message, "Remove Existing");
String installPath = installedExtension.getInstallPath();
if (choice != OptionDialog.OPTION_ONE) {
- log.info(
- removeNewlines(
- message + " Skipping installation. Original extension still installed: " +
- installPath));
+ log.info(removeNewlines(message +
+ " Skipping installation. Original extension still installed: " + installPath));
return true;
}
@@ -218,10 +211,9 @@ public class ExtensionInstaller {
// At this point the user would like to replace the existing extension. We cannot delete
// the existing extension, as it may be in use; mark it for removal.
//
- log.info(
- removeNewlines(
- message + " Installing new extension. Existing extension will be removed after " +
- "restart: " + installPath));
+ log.info(removeNewlines(
+ message + " Installing new extension. Existing extension will be removed after " +
+ "restart: " + installPath));
installedExtension.markForUninstall();
return true;
}
diff --git a/Ghidra/Framework/Project/src/test/java/ghidra/framework/project/extensions/ExtensionInstallerTest.java b/Ghidra/Framework/Project/src/test/java/ghidra/framework/project/extensions/ExtensionInstallerTest.java
index 47a2858dd1..36e69b1ee6 100644
--- a/Ghidra/Framework/Project/src/test/java/ghidra/framework/project/extensions/ExtensionInstallerTest.java
+++ b/Ghidra/Framework/Project/src/test/java/ghidra/framework/project/extensions/ExtensionInstallerTest.java
@@ -60,6 +60,9 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
setErrorGUIEnabled(false);
+ // clear static caching of extensions
+ ExtensionUtils.clearCache();
+
appLayout = Application.getApplicationLayout();
FileUtilities.deleteDir(appLayout.getExtensionArchiveDir().getFile(false));
@@ -290,7 +293,7 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
//=================================================================================================
// Edge Cases
-//=================================================================================================
+//=================================================================================================
@Test
public void testInstallingNewExtension_SameName_NewVersion() throws Exception {
@@ -317,8 +320,7 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
didInstall.set(ExtensionInstaller.install(extensionFolder2));
});
- DialogComponentProvider confirmDialog =
- waitForDialogComponent("Duplicate Extension");
+ DialogComponentProvider confirmDialog = waitForDialogComponent("Duplicate Extension");
pressButtonByText(confirmDialog, "Remove Existing");
waitForSwing();
@@ -333,7 +335,7 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
didInstall.set(ExtensionInstaller.install(extensionFolder2));
});
- // no longer an installed extension conflict; now we have a version mismatch
+ // no longer an installed extension conflict; now we have a version mismatch
confirmDialog = waitForDialogComponent("Extension Version Mismatch");
pressButtonByText(confirmDialog, "Install Anyway");
@@ -363,8 +365,7 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
didInstall.set(ExtensionInstaller.install(extensionFolder2));
});
- DialogComponentProvider confirmDialog =
- waitForDialogComponent("Duplicate Extension");
+ DialogComponentProvider confirmDialog = waitForDialogComponent("Duplicate Extension");
pressButtonByText(confirmDialog, "Cancel");
waitForSwing();
@@ -397,8 +398,7 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
didInstall.set(ExtensionInstaller.install(extensionFolder2));
});
- DialogComponentProvider confirmDialog =
- waitForDialogComponent("Duplicate Extension");
+ DialogComponentProvider confirmDialog = waitForDialogComponent("Duplicate Extension");
pressButtonByText(confirmDialog, "Remove Existing");
waitForSwing();
@@ -426,14 +426,14 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
/*
Create a zip file that looks something like this:
-
+
/
{Extension Name 1}/
extension.properties
-
+
{Extension Name 2}/
extension.properties
-
+
*/
errorsExpected(() -> {
@@ -446,9 +446,9 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
public void testInstallThenUninstallThenReinstallWhenExtensionNameDoesntMatchFolder()
throws Exception {
- // This tests a previous failure case where an extension could not be reinstalled if its
+ // This tests a previous failure case where an extension could not be reinstalled if its
// name did not match the folder it was installed into. This could happen because the code
- // that installed the extension did not match the code to clear the 'mark for uninstall'
+ // that installed the extension did not match the code to clear the 'mark for uninstall'
// condition.
String nameProperty = "ExtensionNamedFoo";
@@ -493,10 +493,9 @@ public class ExtensionInstallerTest extends AbstractDockingTest {
private void assertExtensionNotInstalled(String name, String version) {
Set