mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-22 04:05:39 +00:00
GT-3547 - Patch dir fix - review fixes
This commit is contained in:
parent
3dced733df
commit
87bda2b34d
@ -53,31 +53,6 @@
|
||||
class files in that directory will be used (not jar files within that directory).
|
||||
If the path is a jar file, then classes within the jar file will be used.
|
||||
</P>
|
||||
|
||||
<P>
|
||||
The <I>User Plugin Jar Directory</I> shows the directory that contains jar files to
|
||||
search.
|
||||
|
||||
<P>
|
||||
<IMG SRC="images/note.png" />
|
||||
In addition to the above, Ghidra also searches in the installation directory, in the
|
||||
<code><home>/.ghidra/.ghidra-<version>/plugins</code> directory, if it exists.
|
||||
</P>
|
||||
|
||||
|
||||
<P>
|
||||
The directories noted above, as well as any found jar files, are added to Ghidra's
|
||||
classpath. The search order of these paths is:<A name="SearchOrder"></A></P>
|
||||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<OL>
|
||||
<LI>Jar files in <I>User Plugin Jar Directory</I> (Plugin Path preference)</LI>
|
||||
|
||||
<LI>Jar files in the Ghidra <code>plugins</code> installation directory</LI>
|
||||
|
||||
<LI><I>User Plugin Paths</I> from the Plugin Paths preference</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Editing Plugin Paths</H2>
|
||||
|
||||
@ -142,29 +117,6 @@
|
||||
class that is loaded is the one that you will be using when you run Ghidra. </P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Set the User Plugin Jar Directory</H3>
|
||||
|
||||
<UL>
|
||||
<LI>To set the User Plugin Jar Directory,</LI>
|
||||
</UL>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<OL>
|
||||
<LI>Enter the absolute directory path in the <I>User Plugin Jar Directory</I> field, OR
|
||||
click on the <B>...</B> button to choose a directory from the file system.</LI>
|
||||
|
||||
<LI>
|
||||
Select the <B>Apply or OK</B> button<B>.</B>
|
||||
|
||||
<UL>
|
||||
<LI><B>Apply</B> applies the changes and leaves the dialog up.</LI>
|
||||
|
||||
<LI><B>OK</B> applies the changes and dismisses the dialog. </LI>
|
||||
</UL>
|
||||
</LI>
|
||||
</OL>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Remove Paths</H3>
|
||||
|
||||
<UL>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -20,7 +20,6 @@ import java.util.List;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.GhidraClassLoader;
|
||||
import ghidra.GhidraLauncher;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.net.ApplicationTrustManagerFactory;
|
||||
import ghidra.util.Msg;
|
||||
@ -62,22 +61,8 @@ public class HeadlessGhidraApplicationConfiguration extends ApplicationConfigura
|
||||
if (!(ClassLoader.getSystemClassLoader() instanceof GhidraClassLoader)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GhidraClassLoader loader = (GhidraClassLoader) ClassLoader.getSystemClassLoader();
|
||||
|
||||
// Add user jars
|
||||
String userJarDir = Preferences.getProperty(Preferences.USER_PLUGIN_JAR_DIRECTORY);
|
||||
if (userJarDir != null) {
|
||||
GhidraLauncher.findJarsInDir(new ResourceFile(userJarDir)).forEach(
|
||||
p -> loader.addPath(p));
|
||||
}
|
||||
|
||||
// Add plugins from user settings directory
|
||||
String userSettingsPath = Application.getUserSettingsDirectory().getAbsolutePath();
|
||||
String pluginPath = userSettingsPath + File.separatorChar + "plugins";
|
||||
loader.addPath(pluginPath);
|
||||
GhidraLauncher.findJarsInDir(new ResourceFile(pluginPath)).forEach(p -> loader.addPath(p));
|
||||
|
||||
// Add user plugins
|
||||
for (String path : Preferences.getPluginPaths()) {
|
||||
loader.addPath(path);
|
||||
}
|
||||
|
@ -40,11 +40,6 @@ public class Preferences {
|
||||
*/
|
||||
private final static String USER_PLUGIN_PATH = "UserPluginPath";
|
||||
|
||||
/**
|
||||
* Preference name of the user plugin jar directory.
|
||||
*/
|
||||
public final static String USER_PLUGIN_JAR_DIRECTORY = "UserPluginJarDirectory";
|
||||
|
||||
/**
|
||||
* Preference name for the last opened archive directory.
|
||||
*/
|
||||
@ -194,6 +189,8 @@ public class Preferences {
|
||||
* <p>
|
||||
* Note: all <code>getProperty(...)</code> methods will first check {@link System#getProperty(String)}
|
||||
* for a value first. This allows users to override preferences from the command-line.
|
||||
* @param name the property name
|
||||
* @return the current property value; null if not set
|
||||
*/
|
||||
public static String getProperty(String name) {
|
||||
// prefer system properties, which enables uses to override preferences from the command-line
|
||||
@ -210,6 +207,9 @@ public class Preferences {
|
||||
* <p>
|
||||
* Note: all <code>getProperty(...)</code> methods will first check {@link System#getProperty(String)}
|
||||
* for a value first. This allows users to override preferences from the command-line.
|
||||
* @param name the property name
|
||||
* @param defaultValue the default value
|
||||
* @return the property value; default value if not set
|
||||
*
|
||||
* @see #getProperty(String, String, boolean)
|
||||
*/
|
||||
@ -289,6 +289,7 @@ public class Preferences {
|
||||
|
||||
/**
|
||||
* Get the filename that will be used in the store() method.
|
||||
* @return the filename
|
||||
*/
|
||||
public static String getFilename() {
|
||||
return filename;
|
||||
@ -297,7 +298,7 @@ public class Preferences {
|
||||
/**
|
||||
* Set the filename so that when the store() method is called, the
|
||||
* preferences are written to this file.
|
||||
* @param name
|
||||
* @param name the filename
|
||||
*/
|
||||
public static void setFilename(String name) {
|
||||
filename = name;
|
||||
@ -305,6 +306,7 @@ public class Preferences {
|
||||
|
||||
/**
|
||||
* Store the preferences in a file for the current filename.
|
||||
* @return true if the file was written
|
||||
* @throws RuntimeException if the preferences filename was not set
|
||||
*/
|
||||
public static boolean store() {
|
||||
@ -346,6 +348,7 @@ public class Preferences {
|
||||
/**
|
||||
* Return the paths in the UserPluginPath property.
|
||||
* Return zero length array if this property is not set.
|
||||
* @return the paths
|
||||
*
|
||||
*/
|
||||
public static String[] getPluginPaths() {
|
||||
@ -359,6 +362,7 @@ public class Preferences {
|
||||
|
||||
/**
|
||||
* Set the paths to be used as the UserPluginPath property.
|
||||
* @param paths the paths
|
||||
*/
|
||||
public static void setPluginPaths(String[] paths) {
|
||||
if (paths == null || paths.length == 0) {
|
||||
@ -376,55 +380,6 @@ public class Preferences {
|
||||
properties.setProperty(USER_PLUGIN_PATH, sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plugin path property.
|
||||
* @param pathProperty A string of paths separated by {@link File#pathSeparator} characters
|
||||
*/
|
||||
public static void setPluginPathProperty(String pathProperty) {
|
||||
properties.setProperty(USER_PLUGIN_PATH, pathProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append path to the plugin path.
|
||||
* @param path the plugin path to add
|
||||
*/
|
||||
public static void addPluginPath(String path) {
|
||||
List<String> list = getPluginPathList();
|
||||
if (list == null) {
|
||||
setPluginPaths(new String[] { path });
|
||||
return;
|
||||
}
|
||||
if (!list.contains(path)) {
|
||||
list.add(path);
|
||||
String[] p = new String[list.size()];
|
||||
setPluginPaths(list.toArray(p));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append paths to the plugin path.
|
||||
* @param paths the plugin paths to add
|
||||
*/
|
||||
public static void addPluginPaths(String[] paths) {
|
||||
List<String> list = getPluginPathList();
|
||||
if (list == null) {
|
||||
setPluginPaths(paths);
|
||||
return;
|
||||
}
|
||||
boolean listChanged = false;
|
||||
for (String path : paths) {
|
||||
if (!list.contains(path)) {
|
||||
list.add(path);
|
||||
listChanged = true;
|
||||
}
|
||||
}
|
||||
// update plugin path property only if we added a path to the list
|
||||
if (listChanged) {
|
||||
String[] p = new String[list.size()];
|
||||
setPluginPaths(list.toArray(p));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getPluginPathList() {
|
||||
String path = properties.getProperty(USER_PLUGIN_PATH);
|
||||
if (path == null) {
|
||||
|
@ -17,8 +17,7 @@ package ghidra.util.classfinder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Matcher;
|
||||
@ -28,7 +27,9 @@ import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utility.application.ApplicationLayout;
|
||||
@ -46,20 +47,13 @@ class ClassJar extends ClassLocation {
|
||||
Pattern.compile(".*/(.*)/(?:lib|build/libs)/(.+).jar");
|
||||
|
||||
private static final String PATCH_DIR_PATH_FORWARD_SLASHED = getPatchDirPath();
|
||||
|
||||
private static String getPatchDirPath() {
|
||||
ApplicationLayout layout = Application.getApplicationLayout();
|
||||
ResourceFile installDir = layout.getApplicationInstallationDir();
|
||||
ResourceFile patchDir = new ResourceFile(installDir, "Ghidra/patch");
|
||||
String patchPath = patchDir.getAbsolutePath();
|
||||
String forwardSlashed = patchPath.replaceAll("\\\\", "/");
|
||||
return forwardSlashed;
|
||||
}
|
||||
private static final Set<String> USER_PLUGIN_PATHS = loadUserPluginPaths();
|
||||
|
||||
private String path;
|
||||
|
||||
ClassJar(String path, TaskMonitor monitor) throws CancelledException {
|
||||
this.path = path;
|
||||
loadUserPluginPaths();
|
||||
|
||||
scanJar(monitor);
|
||||
}
|
||||
@ -105,10 +99,22 @@ class ClassJar extends ClassLocation {
|
||||
if (pathName.contains("ExternalLibraries")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Dev and Production Mode
|
||||
//
|
||||
String forwardSlashedPathName = pathName.replaceAll("\\\\", "/");
|
||||
if (isUserPluginJar(forwardSlashedPathName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Production Mode - allow users to enter code in the 'patch' directory
|
||||
//
|
||||
String forwardSlashedPathName = pathName.replaceAll("\\\\", "/");
|
||||
if (isPatchJar(forwardSlashedPathName)) {
|
||||
return false;
|
||||
}
|
||||
@ -123,6 +129,10 @@ class ClassJar extends ClassLocation {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isUserPluginJar(String pathName) {
|
||||
return USER_PLUGIN_PATHS.contains(pathName);
|
||||
}
|
||||
|
||||
// Note: the path is expected to be using forward slashes
|
||||
private static boolean isPatchJar(String pathName) {
|
||||
String jarDirectory = FilenameUtils.getFullPathNoEndSeparator(pathName);
|
||||
@ -168,4 +178,27 @@ class ClassJar extends ClassLocation {
|
||||
public String toString() {
|
||||
return path;
|
||||
}
|
||||
|
||||
private static String getPatchDirPath() {
|
||||
ApplicationLayout layout = Application.getApplicationLayout();
|
||||
ResourceFile patchDir = layout.getPatchDir();
|
||||
if (patchDir == null) {
|
||||
return "<no patch dir>"; // not in a distribution
|
||||
}
|
||||
String patchPath = patchDir.getAbsolutePath();
|
||||
String forwardSlashed = patchPath.replaceAll("\\\\", "/");
|
||||
return forwardSlashed;
|
||||
}
|
||||
|
||||
private static Set<String> loadUserPluginPaths() {
|
||||
Set<String> result = new HashSet<>();
|
||||
String[] paths = Preferences.getPluginPaths();
|
||||
for (String pathName : paths) {
|
||||
// note: lower case because our client uses lower case for paths
|
||||
String forwardSlashed = pathName.replaceAll("\\\\", "/").toLowerCase();
|
||||
result.add(forwardSlashed);
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,205 +0,0 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.framework.main;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.*;
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.options.editor.ButtonPanelFactory;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
|
||||
/**
|
||||
* Helper class that restricts the width of the textField to the size of the
|
||||
* scrolled paths list; also provides the listener for the textfield if user
|
||||
* presses Enter or Tab in a textfield.
|
||||
*
|
||||
*/
|
||||
class BrowsePathPanel extends JPanel {
|
||||
|
||||
private boolean changed;
|
||||
private GhidraFileChooser fileChooser;
|
||||
private JTextField pathTextField;
|
||||
private EditPluginPathDialog dialog;
|
||||
private JButton browseButton;
|
||||
|
||||
/**
|
||||
* Construct a new BrowsePathPanel.
|
||||
* @param editDialog parent dialog
|
||||
* @param sizeComp component to use for size in creating text field
|
||||
* @param button browse button
|
||||
* @param dirOnly
|
||||
* @param textFieldLabel
|
||||
* @param fieldName name of text field component
|
||||
*/
|
||||
BrowsePathPanel(EditPluginPathDialog editDialog, ActionListener buttonListener, String fieldName) {
|
||||
|
||||
super();
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
dialog = editDialog;
|
||||
create(fieldName);
|
||||
addListeners(buttonListener);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the components
|
||||
* @param sizeComp component to use when creating the text field to get the
|
||||
* size
|
||||
* @param textFieldLabel label for the field
|
||||
*/
|
||||
private void create(String fieldName) {
|
||||
pathTextField = new JTextField();
|
||||
pathTextField.setName(fieldName);
|
||||
pathTextField.setEditable(false);
|
||||
pathTextField.setBackground(getBackground());
|
||||
|
||||
browseButton = ButtonPanelFactory.createButton(ButtonPanelFactory.BROWSE_TYPE);
|
||||
browseButton.setToolTipText("Choose Directory");
|
||||
|
||||
// construct the panel with text field and browse button
|
||||
JPanel browsePathPanel = new JPanel(new BorderLayout(5, 5));
|
||||
browsePathPanel.add(pathTextField, BorderLayout.CENTER);
|
||||
browsePathPanel.add(browseButton, BorderLayout.EAST);
|
||||
add(browsePathPanel);
|
||||
|
||||
}
|
||||
|
||||
private void createFileChooser() {
|
||||
// create the fileChooser this panel will use based on its input criteria
|
||||
fileChooser = new GhidraFileChooser(dialog.getComponent());
|
||||
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
|
||||
fileChooser.setFileSelectionMode(GhidraFileChooser.DIRECTORIES_ONLY);
|
||||
fileChooser.setApproveButtonToolTipText("Choose Directory With Plugin JAR Files");
|
||||
fileChooser.setApproveButtonText("Choose JAR Directory");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add listeners.
|
||||
* @param listener listener for the browse button
|
||||
*/
|
||||
private void addListeners(ActionListener listener) {
|
||||
browseButton.addActionListener(listener);
|
||||
|
||||
pathTextField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
int keyCode = e.getKeyCode();
|
||||
|
||||
// when Esc or Ctrl-C is pressed, reset the plugin
|
||||
// jar directory to what is saved in preferences
|
||||
if (keyCode == KeyEvent.VK_ESCAPE ||
|
||||
(DockingUtils.isControlModifier(e) && keyCode == KeyEvent.VK_C)) {
|
||||
dialog.initJarDirectory();
|
||||
}
|
||||
else {
|
||||
dialog.setApplyEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
return pathTextField.getText().trim();
|
||||
}
|
||||
|
||||
boolean isChanged() {
|
||||
return changed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFocus() {
|
||||
return pathTextField.hasFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
pathTextField.requestFocus();
|
||||
pathTextField.selectAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop up the file chooser.
|
||||
*/
|
||||
void showFileChooser() {
|
||||
if (fileChooser == null) {
|
||||
createFileChooser();
|
||||
}
|
||||
// reset the status message
|
||||
dialog.setStatusMessage(EditPluginPathDialog.EMPTY_STATUS);
|
||||
|
||||
File pluginFile = fileChooser.getSelectedFile();
|
||||
if (pluginFile != null) {
|
||||
setPath(pluginFile);
|
||||
}
|
||||
else {
|
||||
pathTextField.requestFocus();
|
||||
pathTextField.selectAll();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether something has changed.
|
||||
* @param changed true if something changed
|
||||
*/
|
||||
void setChanged(boolean changed) {
|
||||
this.changed = changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path field.
|
||||
* @param path filename for the path field
|
||||
* @return boolean true if the path is valid
|
||||
*/
|
||||
private boolean setPath(File path) {
|
||||
boolean pathOK = false;
|
||||
dialog.setStatusMessage(EditPluginPathDialog.EMPTY_STATUS);
|
||||
|
||||
if (!path.canRead()) {
|
||||
pathTextField.selectAll();
|
||||
dialog.setStatusMessage("Cannot read path: " + path.toString());
|
||||
}
|
||||
else {
|
||||
pathTextField.setText(path.getAbsolutePath());
|
||||
pathOK = (pathTextField.getText().trim().length() > 0);
|
||||
}
|
||||
|
||||
if (pathOK) {
|
||||
dialog.setStatusMessage("Press Apply or OK to set JAR directory.");
|
||||
}
|
||||
|
||||
changed = changed || pathOK;
|
||||
|
||||
dialog.enableApply();
|
||||
|
||||
return pathOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the text of the text field of the panel without
|
||||
* any error checking
|
||||
*/
|
||||
void setText(String text) {
|
||||
pathTextField.setText(text);
|
||||
}
|
||||
|
||||
}
|
@ -18,8 +18,6 @@
|
||||
package ghidra.framework.main;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@ -34,6 +32,7 @@ import javax.swing.event.ListSelectionListener;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.options.editor.ButtonPanelFactory;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.list.GListCellRenderer;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@ -45,20 +44,18 @@ import ghidra.util.filechooser.GhidraFileFilter;
|
||||
|
||||
/**
|
||||
* Dialog for editing the Plugin path and Jar directory path preferences.
|
||||
*
|
||||
* <p>The Plugin Path and Jar directory path are locations where Ghidra searches
|
||||
* for plugins to load. The Plugin Path is specified exactly as a Java Classpath
|
||||
* is specified. The Jar directory is searched only for Jar files containing
|
||||
* Plugins. When changes are made to these fields in the dialog, the
|
||||
* is specified. When changes are made to these fields in the dialog, the
|
||||
* preferences file is updated and written to disk. The preferences file is
|
||||
* located in the .ghidra directory in the user's home directory.
|
||||
* </P>
|
||||
* <p> The preferences file also contains the last project that was opened,
|
||||
* and the positions of the Ghidra Project Window and other tools that were
|
||||
* running when the user last exited Ghidra.
|
||||
* </P>
|
||||
*
|
||||
*/
|
||||
class EditPluginPathDialog extends DialogComponentProvider {
|
||||
|
||||
static final String ADD_DIR_BUTTON_TEXT = "Add Dir ...";
|
||||
static final String ADD_JAR_BUTTON_TEXT = "Add Jar ...";
|
||||
private final static int SIDE_MARGIN = 5;
|
||||
private final static Color INVALID_PATH_COLOR = Color.red.brighter();
|
||||
private final static Color INVALID_SELECTED_PATH_COLOR = Color.pink;
|
||||
@ -80,7 +77,6 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
// gui members needed for dis/enabling and other state-dependent actions
|
||||
private JScrollPane scrollPane; // need for preferred size when resizing
|
||||
private JList<String> pluginPathsList;
|
||||
private BrowsePathPanel jarPathPanel;
|
||||
private GhidraFileChooser fileChooser;
|
||||
private JButton upButton;
|
||||
private JButton downButton;
|
||||
@ -94,7 +90,6 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
* Creates a non-modal dialog with OK, Apply, Cancel buttons.
|
||||
* The OK and Apply buttons will be enabled when user makes unapplied
|
||||
* changes to the UserPluginPath or UserPluginJarDirectory property values.
|
||||
* @param parent parent to this dialog
|
||||
*/
|
||||
EditPluginPathDialog() {
|
||||
super("Edit Plugin Path", true, false, true, false);
|
||||
@ -134,8 +129,6 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
// subsequent panels
|
||||
mainPanel.add(buildPluginPathsPanel());
|
||||
mainPanel.add(Box.createVerticalStrut(10));
|
||||
mainPanel.add(buildJarDirectoryPanel());
|
||||
mainPanel.add(Box.createVerticalStrut(10));
|
||||
mainPanel.add(Box.createVerticalGlue());
|
||||
mainPanel.add(statusMessagePanel);
|
||||
mainPanel.invalidate();
|
||||
@ -147,33 +140,15 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when the user selects Apply
|
||||
*/
|
||||
@Override
|
||||
protected void applyCallback() {
|
||||
// validate the jar path before applying changes, since the user
|
||||
// is pressing the Apply button to save this setting
|
||||
String jarPathname = jarPathPanel.getPath();
|
||||
if (jarPathname.length() > 0) {
|
||||
File jarPath = new File(jarPathname);
|
||||
if (!jarPath.isDirectory() || !jarPath.canRead()) {
|
||||
setStatusMessage("Bad Jar Directory: " + jarPathname);
|
||||
jarPathPanel.requestFocus();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// do the things we need to do to handle the applied changes
|
||||
handleApply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when the user selects Cancel
|
||||
*/
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
close();
|
||||
|
||||
// reset original state of dialog for next display of dialog
|
||||
enableButtons(false);
|
||||
setStatusMessage(EMPTY_STATUS);
|
||||
@ -181,17 +156,6 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
errorMsg = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* if the jar directory field has focus, don't let the base dialog
|
||||
* handle it.
|
||||
*/
|
||||
@Override
|
||||
protected void escapeCallback() {
|
||||
if (!jarPathPanel.hasFocus()) {
|
||||
super.escapeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets called when the user selects Ok
|
||||
*/
|
||||
@ -206,30 +170,21 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* re-set the list of paths each time the dialog is shown
|
||||
* Reset the list of paths each time the dialog is shown
|
||||
* @param tool the tool
|
||||
*/
|
||||
public void show(PluginTool tool) {
|
||||
setPluginPathsListData(Preferences.getPluginPaths());
|
||||
initJarDirectory();
|
||||
setApplyEnabled(pluginPathsChanged);
|
||||
setStatusMessage(EMPTY_STATUS);
|
||||
|
||||
// setting the path enables the apply, but we know we haven't
|
||||
// made any changes yet, so disable
|
||||
setApplyEnabled(false);
|
||||
tool.showDialog(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method enableApply.
|
||||
*/
|
||||
void enableApply() {
|
||||
setApplyEnabled(pluginPathsChanged || jarPathPanel.isChanged());
|
||||
}
|
||||
|
||||
void initJarDirectory() {
|
||||
setApplyEnabled(pluginPathsChanged);
|
||||
setStatusMessage(EMPTY_STATUS);
|
||||
}
|
||||
|
||||
void setStatusMessage(String msg) {
|
||||
private void setStatusMessage(String msg) {
|
||||
if (msg == null || msg.length() == 0) {
|
||||
msg = EMPTY_STATUS;
|
||||
}
|
||||
@ -237,15 +192,7 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
statusMessage.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.util.bean.GhidraDialog#setApplyEnabled(boolean)
|
||||
*/
|
||||
@Override
|
||||
protected void setApplyEnabled(boolean state) {
|
||||
super.setApplyEnabled(state);
|
||||
}
|
||||
|
||||
void addJarCallback() {
|
||||
private void addJarCallback() {
|
||||
|
||||
setStatusMessage(EditPluginPathDialog.EMPTY_STATUS);
|
||||
|
||||
@ -253,7 +200,7 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
fileChooser = new GhidraFileChooser(getComponent());
|
||||
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
|
||||
}
|
||||
fileChooser.setFileSelectionMode(GhidraFileChooser.FILES_ONLY);
|
||||
fileChooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
|
||||
fileChooser.setFileFilter(JAR_FILTER);
|
||||
fileChooser.setApproveButtonToolTipText("Choose Plugin Jar File");
|
||||
fileChooser.setApproveButtonText("Add Jar File");
|
||||
@ -277,7 +224,7 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
void addDirCallback() {
|
||||
private void addDirCallback() {
|
||||
|
||||
setStatusMessage(EditPluginPathDialog.EMPTY_STATUS);
|
||||
|
||||
@ -285,7 +232,7 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
fileChooser = new GhidraFileChooser(getComponent());
|
||||
fileChooser.setCurrentDirectory(new File(System.getProperty("user.home")));
|
||||
}
|
||||
fileChooser.setFileSelectionMode(GhidraFileChooser.DIRECTORIES_ONLY);
|
||||
fileChooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
|
||||
fileChooser.setFileFilter(GhidraFileFilter.ALL);
|
||||
fileChooser.setApproveButtonToolTipText("Choose Directory with Plugin class Files");
|
||||
fileChooser.setApproveButtonText("Add Directory");
|
||||
@ -310,63 +257,31 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of pathnames where plugins can be found; used by custom
|
||||
* class loader when searching for plugins.
|
||||
*/
|
||||
private String[] getUserPluginPaths() {
|
||||
String[] pluginsArray = new String[listModel.size()];
|
||||
listModel.copyInto(pluginsArray);
|
||||
return pluginsArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* construct the plugin paths button panel
|
||||
*/
|
||||
private JPanel buildPluginPathsPanel() {
|
||||
// create the UP and DOWN arrows panel
|
||||
upButton = ButtonPanelFactory.createButton(ButtonPanelFactory.ARROW_UP_TYPE);
|
||||
upButton.setName("UpArrow");
|
||||
upButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleSelection(UP);
|
||||
}
|
||||
});
|
||||
upButton.addActionListener(e -> handleSelection(UP));
|
||||
downButton = ButtonPanelFactory.createButton(ButtonPanelFactory.ARROW_DOWN_TYPE);
|
||||
downButton.setName("DownArrow");
|
||||
downButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleSelection(DOWN);
|
||||
}
|
||||
});
|
||||
downButton.addActionListener(e -> handleSelection(DOWN));
|
||||
JPanel arrowButtonsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10));
|
||||
arrowButtonsPanel.add(upButton);
|
||||
arrowButtonsPanel.add(downButton);
|
||||
|
||||
// create the Add and Remove panel
|
||||
JButton addJarButton = ButtonPanelFactory.createButton("Add Jar...");
|
||||
addJarButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
addJarCallback();
|
||||
}
|
||||
});
|
||||
JButton addDirButton = ButtonPanelFactory.createButton("Add Dir...");
|
||||
addDirButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
addDirCallback();
|
||||
}
|
||||
});
|
||||
JButton addJarButton = ButtonPanelFactory.createButton(ADD_JAR_BUTTON_TEXT);
|
||||
addJarButton.addActionListener(e -> addJarCallback());
|
||||
JButton addDirButton = ButtonPanelFactory.createButton(ADD_DIR_BUTTON_TEXT);
|
||||
addDirButton.addActionListener(e -> addDirCallback());
|
||||
removeButton = ButtonPanelFactory.createButton("Remove");
|
||||
removeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
handleSelection(REMOVE);
|
||||
}
|
||||
});
|
||||
removeButton.addActionListener(e -> handleSelection(REMOVE));
|
||||
Dimension d = addJarButton.getPreferredSize();
|
||||
addDirButton.setPreferredSize(d);
|
||||
removeButton.setPreferredSize(d);
|
||||
@ -415,26 +330,6 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
return pluginPathListPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* construct the jar directory panel
|
||||
*/
|
||||
private JPanel buildJarDirectoryPanel() {
|
||||
|
||||
ActionListener listener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
jarPathPanel.showFileChooser();
|
||||
enableApply();
|
||||
}
|
||||
};
|
||||
|
||||
jarPathPanel = new BrowsePathPanel(this, listener, "UserPluginJarDirectory");
|
||||
jarPathPanel.setText(Preferences.getProperty(Preferences.USER_PLUGIN_JAR_DIRECTORY));
|
||||
jarPathPanel.setBorder(new TitledBorder("User Plugin Jar Directory"));
|
||||
|
||||
return jarPathPanel;
|
||||
}
|
||||
|
||||
private void enableButtons(boolean enabled) {
|
||||
upButton.setEnabled(enabled);
|
||||
downButton.setEnabled(enabled);
|
||||
@ -452,22 +347,12 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
// update Ghidra Preferences with new paths
|
||||
Preferences.setPluginPaths(userPluginPaths);
|
||||
|
||||
// Get user Jar directory
|
||||
String jarDirectoryName = jarPathPanel.getPath();
|
||||
if (jarDirectoryName.trim().length() == 0) {
|
||||
jarDirectoryName = null;
|
||||
}
|
||||
|
||||
// update Ghidra Preferences with new Jar path
|
||||
Preferences.setProperty(Preferences.USER_PLUGIN_JAR_DIRECTORY, jarDirectoryName);
|
||||
|
||||
errorMsg = null;
|
||||
// save the new values
|
||||
if (Preferences.store()) {
|
||||
setStatusMessage("Saved plugin paths successfully!");
|
||||
// indicate to user all changes have been applied
|
||||
setApplyEnabled(false);
|
||||
jarPathPanel.setChanged(false);
|
||||
|
||||
Msg.showInfo(getClass(), rootPanel, "Restart Ghidra",
|
||||
"You must restart Ghidra in order\n" + "for path changes to take effect.");
|
||||
@ -479,10 +364,6 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dispatched method for handling button actions on the
|
||||
* dialog
|
||||
*/
|
||||
private void handleSelection(byte whichAction) {
|
||||
// if nothing selected, nothing to do
|
||||
if (selectedInList == null) {
|
||||
@ -574,8 +455,8 @@ class EditPluginPathDialog extends DialogComponentProvider {
|
||||
|
||||
private void setPluginPathsListData(String[] pluginPathNames) {
|
||||
listModel.clear();
|
||||
for (int p = 0; p < pluginPathNames.length; p++) {
|
||||
listModel.addElement(pluginPathNames[p]);
|
||||
for (String pluginPathName : pluginPathNames) {
|
||||
listModel.addElement(pluginPathName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,9 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
||||
// Extensions
|
||||
extensionInstallationDir = findExtensionInstallationDirectory();
|
||||
extensionArchiveDir = findExtensionArchiveDirectory();
|
||||
|
||||
// Patch directory
|
||||
patchDir = findPatchDirectory();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +145,7 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
||||
// Find standard module root directories from within the application root directories
|
||||
Collection<ResourceFile> moduleRootDirectories =
|
||||
ModuleUtilities.findModuleRootDirectories(applicationRootDirs, new ArrayList<>());
|
||||
|
||||
|
||||
// Examine the classpath to look for modules outside of the application root directories.
|
||||
// These might exist if Ghidra was launched from an Eclipse project that resides
|
||||
// external to the Ghidra installation.
|
||||
@ -156,8 +159,9 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
||||
|
||||
// Skip classpath entries that live in an application root directory...we've already
|
||||
// found those.
|
||||
if (applicationRootDirs.stream().anyMatch(dir -> FileUtilities.isPathContainedWithin(
|
||||
dir.getFile(false), classpathEntry.getFile(false)))) {
|
||||
if (applicationRootDirs.stream()
|
||||
.anyMatch(dir -> FileUtilities.isPathContainedWithin(
|
||||
dir.getFile(false), classpathEntry.getFile(false)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -173,6 +177,24 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
||||
return ModuleUtilities.findModules(applicationRootDirs, moduleRootDirectories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory that allows users to add jar and class files to override existing
|
||||
* distribution files
|
||||
* @return the patch dir; null if not in a distribution
|
||||
*/
|
||||
protected ResourceFile findPatchDirectory() {
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (applicationInstallationDir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ResourceFile(applicationInstallationDir, "Ghidra/patch");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory where all Ghidra extension archives are stored.
|
||||
* This should be at the following location:<br>
|
||||
|
@ -31,7 +31,7 @@ import ghidra.util.Msg;
|
||||
*
|
||||
*/
|
||||
public class GhidraClassLoader extends URLClassLoader {
|
||||
|
||||
|
||||
private static final String CP = "java.class.path";
|
||||
|
||||
/**
|
||||
@ -45,7 +45,7 @@ public class GhidraClassLoader extends URLClassLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
public void addURL(URL url) {
|
||||
super.addURL(url);
|
||||
try {
|
||||
System.setProperty(CP,
|
||||
@ -56,23 +56,6 @@ public class GhidraClassLoader extends URLClassLoader {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Places the given path first in the classpath
|
||||
* @param path the path
|
||||
*/
|
||||
void prependPath(String path) {
|
||||
|
||||
try {
|
||||
URL url = new File(path).toURI().toURL();
|
||||
super.addURL(url);
|
||||
File file = new File(url.toURI());
|
||||
System.setProperty(CP, file + File.pathSeparator + System.getProperty(CP));
|
||||
}
|
||||
catch (MalformedURLException | URISyntaxException e) {
|
||||
Msg.debug(this, "Invalid path: " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified path to a {@link URL} and adds it to the classpath.
|
||||
*
|
||||
|
@ -53,7 +53,7 @@ public class GhidraLauncher {
|
||||
GhidraClassLoader loader = (GhidraClassLoader) ClassLoader.getSystemClassLoader();
|
||||
|
||||
// Build the classpath
|
||||
List<String> classpathList = new ArrayList<String>();
|
||||
List<String> classpathList = new ArrayList<>();
|
||||
Map<String, GModule> modules = getOrderedModules(layout);
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
@ -61,7 +61,7 @@ public class GhidraLauncher {
|
||||
addExternalJarPaths(classpathList, layout.getApplicationRootDirs());
|
||||
}
|
||||
else {
|
||||
addPatchJarPaths(loader, layout.getApplicationInstallationDir());
|
||||
addPatchPaths(classpathList, layout.getPatchDir());
|
||||
addModuleJarPaths(classpathList, modules);
|
||||
}
|
||||
classpathList = orderClasspath(classpathList, modules);
|
||||
@ -83,28 +83,24 @@ public class GhidraLauncher {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add patch dir and jars to the given path list. This should be done first so they take
|
||||
* precedence in the classpath.
|
||||
* Add patch jars to the given path list. This should be done first so they take precedence in
|
||||
* the classpath.
|
||||
*
|
||||
* @param loader The loader to which paths will be added.
|
||||
* @param installDir The application installation directory.
|
||||
* @param pathList The list of paths to add to
|
||||
* @param patchDir The application installation directory
|
||||
*/
|
||||
private static void addPatchJarPaths(GhidraClassLoader loader, ResourceFile installDir) {
|
||||
ResourceFile patchDir = new ResourceFile(installDir, "Ghidra/patch");
|
||||
private static void addPatchPaths(List<String> pathList, ResourceFile patchDir) {
|
||||
if (!patchDir.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> patchJars = findJarsInDir(patchDir);
|
||||
Collections.sort(patchJars);
|
||||
// this will allow for unbundled class files
|
||||
pathList.add(patchDir.getAbsolutePath());
|
||||
|
||||
// add in reverse order, since we are prepending
|
||||
for (int i = patchJars.size() - 1; i >= 0; i--) {
|
||||
loader.prependPath(patchJars.get(i));
|
||||
}
|
||||
|
||||
// put last; paths are prepended in list order
|
||||
loader.prependPath(patchDir.getAbsolutePath());
|
||||
// this is each jar file, sorted for loading consistency
|
||||
List<String> jars = findJarsInDir(patchDir);
|
||||
Collections.sort(jars);
|
||||
pathList.addAll(jars);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,7 +272,7 @@ public class GhidraLauncher {
|
||||
.flatMap(m -> m.getFatJars().stream())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<String> orderedList = new ArrayList<String>(pathList);
|
||||
List<String> orderedList = new ArrayList<>(pathList);
|
||||
|
||||
for (String path : pathList) {
|
||||
if (fatJars.contains(new File(path).getName())) {
|
||||
|
@ -55,4 +55,10 @@ public class GhidraTestApplicationLayout extends GhidraApplicationLayout {
|
||||
File installDir = new File(getUserTempDir(), "ExtensionInstallDir");
|
||||
return new ResourceFile(installDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceFile findPatchDirectory() {
|
||||
File dir = new File(getUserTempDir(), "patch");
|
||||
return new ResourceFile(dir);
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ public abstract class ApplicationLayout {
|
||||
protected File userTempDir;
|
||||
protected File userCacheDir;
|
||||
protected File userSettingsDir;
|
||||
protected ResourceFile patchDir;
|
||||
protected ResourceFile extensionArchiveDir;
|
||||
protected ResourceFile extensionInstallationDir;
|
||||
|
||||
@ -109,10 +110,10 @@ public abstract class ApplicationLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory where archived Ghidra Extensions are stored.
|
||||
* Returns the directory where archived application Extensions are stored.
|
||||
*
|
||||
* @return The Ghidra Extensions archive directory. Could be null if the
|
||||
* {@link ApplicationLayout} does not support Ghidra Extensions.
|
||||
* @return the application Extensions archive directory. Could be null if the
|
||||
* {@link ApplicationLayout} does not support application Extensions.
|
||||
*
|
||||
*/
|
||||
public final ResourceFile getExtensionArchiveDir() {
|
||||
@ -120,15 +121,24 @@ public abstract class ApplicationLayout {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Ghidra Extensions installation folder.
|
||||
* Returns the application Extensions installation folder.
|
||||
*
|
||||
* @return The Ghidra Extensions installation directory. Could be null if the
|
||||
* {@link ApplicationLayout} does not support Ghidra Extensions.
|
||||
* @return the application Extensions installation directory. Could be null if the
|
||||
* {@link ApplicationLayout} does not support application Extensions.
|
||||
*/
|
||||
public final ResourceFile getExtensionInstallationDir() {
|
||||
return extensionInstallationDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the location of the application patch directory. The patch directory can be
|
||||
* used to modify existing code within a distribution.
|
||||
* @return the patch directory; may be null
|
||||
*/
|
||||
public final ResourceFile getPatchDir() {
|
||||
return patchDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the application's user directories (or ensures they already exist).
|
||||
*
|
||||
|
@ -255,7 +255,6 @@ public class FrontEndPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
@Test
|
||||
public void testEditPluginPath() {
|
||||
Preferences.setProperty(Preferences.USER_PLUGIN_JAR_DIRECTORY, "/MyPlugins");
|
||||
Preferences.setPluginPaths(new String[] { "/myJar.jar", "/MyPlugins/classes" });
|
||||
performAction("Edit Plugin Path", "FrontEndPlugin", false);
|
||||
DialogComponentProvider dialog = getDialog();
|
||||
|
@ -1,6 +1,7 @@
|
||||
Into this directory may be added compiled Java class files, either inside of a jar file or
|
||||
in a directory structure. This directory and the contained jar files will be prepended to
|
||||
the classpath, allowing them to override any existing classes in any module.
|
||||
Compiled Java class files, either inside of a jar file or in a directory structure may be inserted
|
||||
into this directory. This directory and the contained jar files will be prepended to
|
||||
the classpath, allowing them to override any existing classes in any module (except those from
|
||||
the Utility module).
|
||||
|
||||
The jar files will be sorted by name before being added to the classpath in order to present
|
||||
predictable class loading between Ghidra runs. This directory will be prepended on the classpath
|
||||
|
Loading…
Reference in New Issue
Block a user