mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 13:16:48 +08:00
style fixes for review
This commit is contained in:
@@ -45,7 +45,7 @@
|
||||
|
||||
<h2><a name="source_bundles"/>Source bundles</h2>
|
||||
<p>When a directory is added to the Bundle Manager, it is treated as a
|
||||
<em>source bundle</em> and of its Java contents will be compiled to <br>
|
||||
<em>source bundle</em> and its Java contents will be compiled to <br>
|
||||
<code><user home>/.ghidra/.ghidra-<version>/osgi/compiled-bundles/<hash></code>, <br>
|
||||
where <code><hash></code> is a hash of the source bundle location.</p>
|
||||
<h3>exploded bundles</h3>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.app.plugin.core.osgi;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
|
||||
/**
|
||||
* An error produced during {@link GhidraBundle#build()} with a time stamp
|
||||
*/
|
||||
public class BuildError {
|
||||
// the lastModified time of the source causing this error
|
||||
private final long lastModified;
|
||||
|
||||
private final StringBuilder message = new StringBuilder();
|
||||
|
||||
/**
|
||||
* Construct an object to record error message produced for {@code sourceFile}
|
||||
* @param sourceFile the file causing this error
|
||||
*/
|
||||
public BuildError(ResourceFile sourceFile) {
|
||||
lastModified = sourceFile.lastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append {@code str} to the current error message
|
||||
*
|
||||
* @param str the string to append
|
||||
*/
|
||||
public void append(String str) {
|
||||
message.append(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the error message
|
||||
*/
|
||||
String getMessage() {
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last modified time of the source for this build error
|
||||
*/
|
||||
public long getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,34 +19,38 @@ import generic.jar.ResourceFile;
|
||||
import generic.util.Path;
|
||||
|
||||
/**
|
||||
* The BundleStatus class represents the runtime state and user preferences for OSGi bundles in Ghidra.
|
||||
* The BundleStatus class represents the runtime state and user preferences for bundles.
|
||||
*/
|
||||
public class BundleStatus implements Comparable<BundleStatus> {
|
||||
final Path path;
|
||||
final GhidraBundle.Type type;
|
||||
final String bundleLocation;
|
||||
private final GhidraBundle.Type type;
|
||||
private final String location;
|
||||
|
||||
boolean active = false;
|
||||
boolean busy = false;
|
||||
private final ResourceFile file;
|
||||
private final boolean readOnly;
|
||||
private boolean enabled;
|
||||
private boolean active = false;
|
||||
private boolean busy = false;
|
||||
|
||||
String summary;
|
||||
private String summary;
|
||||
|
||||
BundleStatus(ResourceFile path, boolean enabled, boolean readonly, String bundleLoc) {
|
||||
this.path = new Path(path, enabled, false, readonly);
|
||||
type = GhidraBundle.getType(getPath());
|
||||
this.bundleLocation = bundleLoc;
|
||||
BundleStatus(ResourceFile bundleFile, boolean enabled, boolean readOnly, String bundleLoc) {
|
||||
this.file = bundleFile;
|
||||
type = GhidraBundle.getType(getFile());
|
||||
this.location = bundleLoc;
|
||||
this.enabled = enabled;
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(BundleStatus o) {
|
||||
return path.compareTo(o != null ? o.path : null);
|
||||
return Path.toPathString(file).compareTo(o != null ? Path.toPathString(o.file) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the bundle is enabled
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return path.isEnabled();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,14 +59,14 @@ public class BundleStatus implements Comparable<BundleStatus> {
|
||||
* @param isEnabled true to set status to enabled
|
||||
*/
|
||||
public void setEnabled(boolean isEnabled) {
|
||||
path.setEnabled(isEnabled);
|
||||
this.enabled = isEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the bundle is read only
|
||||
*/
|
||||
public boolean isReadOnly() {
|
||||
return path.isReadOnly();
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,31 +111,31 @@ public class BundleStatus implements Comparable<BundleStatus> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bundle's path
|
||||
* @return the bundle file
|
||||
*/
|
||||
public ResourceFile getPath() {
|
||||
return path.getPath();
|
||||
public ResourceFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the bundle's path exists
|
||||
* @return true if the bundle file exists
|
||||
*/
|
||||
public boolean pathExists() {
|
||||
return path.exists();
|
||||
public boolean fileExists() {
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bundle's path as a string, using $USER and $GHIDRA_HOME when appropriate
|
||||
* @return the bundle file path, using $USER and $GHIDRA_HOME when appropriate
|
||||
*/
|
||||
public String getPathAsString() {
|
||||
return path.getPathAsString();
|
||||
return Path.toPathString(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the bundle's location identifier
|
||||
*/
|
||||
public String getBundleLocation() {
|
||||
return bundleLocation;
|
||||
public String getLocationIdentifier() {
|
||||
return location;
|
||||
}
|
||||
|
||||
void setBusy(boolean isBusy) {
|
||||
|
||||
+218
-229
File diff suppressed because it is too large
Load Diff
+158
-139
@@ -16,46 +16,28 @@
|
||||
package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
|
||||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
import docking.widgets.table.TableSortingContext;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
* Model for {@link BundleStatus} objects.
|
||||
*/
|
||||
public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatus> {
|
||||
List<Column> columns = new ArrayList<>();
|
||||
|
||||
class Column {
|
||||
final Class<?> clazz;
|
||||
final int index;
|
||||
final String name;
|
||||
|
||||
Column(String name, Class<?> clazz) {
|
||||
this.name = name;
|
||||
this.index = columns.size();
|
||||
columns.add(this);
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
boolean editable(BundleStatus status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object getValue(BundleStatus status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
void setValue(BundleStatus status, Object aValue) {
|
||||
throw new RuntimeException(name + " is not editable!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column enabledColumn = new Column("Enabled", Boolean.class) {
|
||||
@Override
|
||||
boolean editable(BundleStatus status) {
|
||||
return status.pathExists();
|
||||
return status.fileExists();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,7 +53,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
Column activeColumn = new Column("Active", Boolean.class) {
|
||||
@Override
|
||||
boolean editable(BundleStatus status) {
|
||||
return status.pathExists() && status.isEnabled();
|
||||
return status.fileExists() && status.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,7 +76,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
Column pathColumn = new Column("Path", ResourceFile.class) {
|
||||
@Override
|
||||
Object getValue(BundleStatus status) {
|
||||
return status.getPath();
|
||||
return status.getFile();
|
||||
}
|
||||
};
|
||||
Column summaryColumn = new Column("Summary", String.class) {
|
||||
@@ -115,77 +97,10 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
private Map<String, BundleStatus> bundleLocToStatusMap = new HashMap<>();
|
||||
private BundleHostListener bundleHostListener;
|
||||
|
||||
private ArrayList<BundleStatusChangeRequestListener> bundleStatusListeners = new ArrayList<>();
|
||||
private List<BundleStatusChangeRequestListener> bundleStatusListeners =
|
||||
new CopyOnWriteArrayList<>();
|
||||
private List<BundleStatus> statuses;
|
||||
|
||||
protected class MyBundleHostListener implements BundleHostListener {
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
status.setSummary(summary);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleActivationChange(GhidraBundle bundle, boolean newActivation) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
int row = getRowIndex(status);
|
||||
status.setBusy(false);
|
||||
if (newActivation) {
|
||||
status.setActive(true);
|
||||
}
|
||||
else {
|
||||
status.setActive(false);
|
||||
}
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle bundle) {
|
||||
addNewStatus(bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||
int index = statuses.size();
|
||||
for (GhidraBundle bundle : bundles) {
|
||||
addNewStatusNoFire(bundle);
|
||||
}
|
||||
fireTableRowsInserted(index, bundles.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleRemoved(GhidraBundle bundle) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
removeStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||
List<BundleStatus> toRemove = bundles.stream()
|
||||
.map(BundleStatusTableModel.this::getStatus)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
removeStatuses(toRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablement) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
status.setEnabled(newEnablement);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleException(GhidraBundleException exception) {
|
||||
BundleStatus status = getStatusFromLoc(exception.getBundleLocation());
|
||||
status.setSummary(exception.getMessage());
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
}
|
||||
|
||||
BundleStatusTableModel(BundleStatusComponentProvider provider, BundleHost bundleHost) {
|
||||
super();
|
||||
this.provider = provider;
|
||||
@@ -195,7 +110,8 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
addNewStatus(bundle);
|
||||
}
|
||||
|
||||
bundleHost.addListener(bundleHostListener = new MyBundleHostListener());
|
||||
bundleHostListener = new MyBundleHostListener();
|
||||
bundleHost.addListener(bundleHostListener);
|
||||
}
|
||||
|
||||
Column getColumn(int i) {
|
||||
@@ -206,7 +122,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
}
|
||||
|
||||
BundleStatus getStatus(GhidraBundle bundle) {
|
||||
return getStatusFromLoc(bundle.getBundleLocation());
|
||||
return getStatusFromLoc(bundle.getLocationIdentifier());
|
||||
}
|
||||
|
||||
BundleStatus getStatusFromLoc(String bundleLoc) {
|
||||
@@ -224,15 +140,25 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
bundleHost.removeListener(bundleHostListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireTableChanged(TableModelEvent e) {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
super.fireTableChanged(e);
|
||||
return;
|
||||
}
|
||||
final TableModelEvent e1 = e;
|
||||
SwingUtilities.invokeLater(() -> BundleStatusTableModel.super.fireTableChanged(e1));
|
||||
}
|
||||
|
||||
private void addNewStatusNoFire(GhidraBundle bundle) {
|
||||
BundleStatus status = new BundleStatus(bundle.getPath(), bundle.isEnabled(),
|
||||
bundle.isSystemBundle(), bundle.getBundleLocation());
|
||||
BundleStatus status = new BundleStatus(bundle.getFile(), bundle.isEnabled(),
|
||||
bundle.isSystemBundle(), bundle.getLocationIdentifier());
|
||||
if (statuses.contains(status)) {
|
||||
throw new RuntimeException(
|
||||
"Bundle status manager already contains " + bundle.getPath().toString());
|
||||
"Bundle status manager already contains " + bundle.getFile().toString());
|
||||
}
|
||||
status.setActive(bundle.isActive());
|
||||
bundleLocToStatusMap.put(status.getBundleLocation(), status);
|
||||
bundleLocToStatusMap.put(status.getLocationIdentifier(), status);
|
||||
statuses.add(status);
|
||||
}
|
||||
|
||||
@@ -247,10 +173,10 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
|
||||
private int removeStatusNoFire(BundleStatus status) {
|
||||
if (!status.isReadOnly()) {
|
||||
int i = statuses.indexOf(status);
|
||||
statuses.remove(i);
|
||||
bundleLocToStatusMap.remove(status.getBundleLocation());
|
||||
return i;
|
||||
int statusIndex = statuses.indexOf(status);
|
||||
statuses.remove(statusIndex);
|
||||
bundleLocToStatusMap.remove(status.getLocationIdentifier());
|
||||
return statusIndex;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -289,7 +215,7 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.lang.Class<?> getColumnClass(int columnIndex) {
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
return getColumn(columnIndex).clazz;
|
||||
}
|
||||
|
||||
@@ -341,15 +267,13 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
/**
|
||||
* Add a change request listener.
|
||||
*
|
||||
* When the user requests a change to the status of a bundle, each listener is called.
|
||||
* <p>When the user requests a change to the status of a bundle, each listener is called.
|
||||
*
|
||||
* @param listener the listener to add
|
||||
*/
|
||||
public void addListener(BundleStatusChangeRequestListener listener) {
|
||||
synchronized (bundleStatusListeners) {
|
||||
if (!bundleStatusListeners.contains(listener)) {
|
||||
bundleStatusListeners.add(listener);
|
||||
}
|
||||
if (!bundleStatusListeners.contains(listener)) {
|
||||
bundleStatusListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,24 +283,18 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
* @param listener the listener to remove
|
||||
*/
|
||||
public void removeListener(BundleStatusChangeRequestListener listener) {
|
||||
synchronized (bundleStatusListeners) {
|
||||
bundleStatusListeners.remove(listener);
|
||||
bundleStatusListeners.remove(listener);
|
||||
}
|
||||
|
||||
void fireBundleEnablementChangeRequested(BundleStatus status, boolean newValue) {
|
||||
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
|
||||
listener.bundleEnablementChangeRequest(status, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
void fireBundleEnablementChangeRequested(BundleStatus path, boolean newValue) {
|
||||
synchronized (bundleStatusListeners) {
|
||||
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
|
||||
listener.bundleEnablementChangeRequest(path, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fireBundleActivationChangeRequested(BundleStatus path, boolean newValue) {
|
||||
synchronized (bundleStatusListeners) {
|
||||
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
|
||||
listener.bundleActivationChangeRequest(path, newValue);
|
||||
}
|
||||
void fireBundleActivationChangeRequested(BundleStatus status, boolean newValue) {
|
||||
for (BundleStatusChangeRequestListener listener : bundleStatusListeners) {
|
||||
listener.bundleActivationChangeRequest(status, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +317,8 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
*/
|
||||
@Override
|
||||
protected void sort(List<BundleStatus> data, TableSortingContext<BundleStatus> sortingContext) {
|
||||
// notify accesses listeners, must be done on swing thread
|
||||
SystemUtilities.assertThisIsTheSwingThread("Must be called on the Swing thread");
|
||||
|
||||
if (sortingContext.isUnsorted()) {
|
||||
// this is the 'no sort' state
|
||||
@@ -409,22 +329,23 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
|
||||
hasEverSorted = true; // signal that we have sorted at least one time
|
||||
|
||||
boolean[] change = { false };
|
||||
Comparator<BundleStatus> proxy = new Comparator<BundleStatus>() {
|
||||
Comparator<BundleStatus> p = sortingContext.getComparator();
|
||||
// wrap the assigned comparator to detect if the order changes
|
||||
boolean[] changed = { false };
|
||||
Comparator<BundleStatus> wrapper = new Comparator<BundleStatus>() {
|
||||
Comparator<BundleStatus> comparator = sortingContext.getComparator();
|
||||
|
||||
@Override
|
||||
public int compare(BundleStatus o1, BundleStatus o2) {
|
||||
int v = p.compare(o1, o2);
|
||||
if (v < 0) {
|
||||
change[0] = true;
|
||||
int result = comparator.compare(o1, o2);
|
||||
if (result < 0) {
|
||||
changed[0] = true;
|
||||
}
|
||||
return v;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
Collections.sort(data, proxy);
|
||||
Collections.sort(data, wrapper);
|
||||
sortCompleted(sortingContext);
|
||||
if (change[0]) {
|
||||
if (changed[0]) {
|
||||
notifyModelSorted(false);
|
||||
}
|
||||
}
|
||||
@@ -435,8 +356,106 @@ public class BundleStatusTableModel extends AbstractSortedTableModel<BundleStatu
|
||||
private void computeCache() {
|
||||
bundleLocToStatusMap.clear();
|
||||
for (BundleStatus status : statuses) {
|
||||
bundleLocToStatusMap.put(status.getBundleLocation(), status);
|
||||
bundleLocToStatusMap.put(status.getLocationIdentifier(), status);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* when bundles are added or removed, update the table.
|
||||
* when bundles change enablement or activation, update rows.
|
||||
*/
|
||||
protected class MyBundleHostListener implements BundleHostListener {
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
status.setSummary(summary);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleActivationChange(GhidraBundle bundle, boolean newActivation) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
int row = getRowIndex(status);
|
||||
status.setBusy(false);
|
||||
if (newActivation) {
|
||||
status.setActive(true);
|
||||
}
|
||||
else {
|
||||
status.setActive(false);
|
||||
}
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle bundle) {
|
||||
addNewStatus(bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||
int index = statuses.size();
|
||||
for (GhidraBundle bundle : bundles) {
|
||||
addNewStatusNoFire(bundle);
|
||||
}
|
||||
fireTableRowsInserted(index, bundles.size() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleRemoved(GhidraBundle bundle) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
removeStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||
List<BundleStatus> toRemove = bundles.stream()
|
||||
.map(BundleStatusTableModel.this::getStatus)
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
removeStatuses(toRemove);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablement) {
|
||||
BundleStatus status = getStatus(bundle);
|
||||
status.setEnabled(newEnablement);
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleException(GhidraBundleException exception) {
|
||||
BundleStatus status = getStatusFromLoc(exception.getBundleLocation());
|
||||
status.setSummary(exception.getMessage());
|
||||
int row = getRowIndex(status);
|
||||
fireTableRowsUpdated(row, row);
|
||||
}
|
||||
}
|
||||
|
||||
class Column {
|
||||
final Class<?> clazz;
|
||||
final int index;
|
||||
final String name;
|
||||
|
||||
Column(String name, Class<?> clazz) {
|
||||
this.name = name;
|
||||
this.index = columns.size();
|
||||
columns.add(this);
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
boolean editable(BundleStatus status) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object getValue(BundleStatus status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
void setValue(BundleStatus status, Object aValue) {
|
||||
throw new RuntimeException(name + " is not editable!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,15 +29,16 @@ import generic.jar.ResourceFile;
|
||||
*/
|
||||
public abstract class GhidraBundle {
|
||||
|
||||
protected final ResourceFile path;
|
||||
protected final ResourceFile file;
|
||||
|
||||
protected final BundleHost bundleHost;
|
||||
protected boolean enabled;
|
||||
protected boolean systemBundle;
|
||||
|
||||
GhidraBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean enabled,
|
||||
GhidraBundle(BundleHost bundleHost, ResourceFile bundleFile, boolean enabled,
|
||||
boolean systemBundle) {
|
||||
this.bundleHost = bundleHost;
|
||||
this.path = bundlePath;
|
||||
this.file = bundleFile;
|
||||
this.enabled = enabled;
|
||||
this.systemBundle = systemBundle;
|
||||
}
|
||||
@@ -59,29 +60,36 @@ public abstract class GhidraBundle {
|
||||
public abstract boolean build(PrintWriter writer) throws Exception;
|
||||
|
||||
/**
|
||||
* same as {@link #build(PrintWriter)} with writer = {@link System.err}.
|
||||
* same as {@link #build(PrintWriter)} with writer = {@link System#err}.
|
||||
*
|
||||
* @return true if build happened, false if already built
|
||||
* @throws Exception if the build cannot complete
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public boolean build() throws Exception {
|
||||
return build(new PrintWriter(System.err));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location identifier of the bundle that this GhidraBundle represents.
|
||||
* The location identifier is passed to {@link org.osgi.framework.BundleContext#installBundle} when this
|
||||
* bundle is installed.
|
||||
*
|
||||
* <p>The location identifier is used by the framework, e.g. it is passed to
|
||||
* {@link org.osgi.framework.BundleContext#installBundle} when the bundle is
|
||||
* first installed.
|
||||
*
|
||||
* <p>Although the bundle location is a URI, outside of interactions with the framework,
|
||||
* the bundle location should remain opaque.
|
||||
*
|
||||
* @return location identifier of this bundle
|
||||
*/
|
||||
public abstract String getBundleLocation();
|
||||
public abstract String getLocationIdentifier();
|
||||
|
||||
abstract List<BundleRequirement> getAllRequirements();
|
||||
|
||||
/**
|
||||
* @return this bundle's path
|
||||
* @return the file where this bundle is loaded from
|
||||
*/
|
||||
public ResourceFile getPath() {
|
||||
return path;
|
||||
public ResourceFile getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +102,7 @@ public abstract class GhidraBundle {
|
||||
/**
|
||||
* set the enablement flag for this bundle.
|
||||
*
|
||||
* If a bundle is enabled its contents will be scanned, e.g. for scripts.
|
||||
* <p>If a bundle is enabled its contents will be scanned, e.g. for scripts.
|
||||
*
|
||||
* @param enabled new state
|
||||
*/
|
||||
@@ -112,81 +120,74 @@ public abstract class GhidraBundle {
|
||||
}
|
||||
|
||||
/**
|
||||
* A GhidraBundle can be
|
||||
* <ul>
|
||||
* <li>a Bndtools .bnd script</li>
|
||||
* <li>an OSGi bundle .jar file</li>
|
||||
* <li>a directory of Java source</li>
|
||||
* </u>
|
||||
*
|
||||
*/
|
||||
enum Type {
|
||||
BndScript, Jar, SourceDir, INVALID
|
||||
}
|
||||
|
||||
/**
|
||||
* a string error with a time stamp
|
||||
*/
|
||||
public static class BuildFailure {
|
||||
long when = -1;
|
||||
StringBuilder message = new StringBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of a GhidraBundle from its path.
|
||||
* Get the type of a GhidraBundle from its file.
|
||||
*
|
||||
* @param path a resource path
|
||||
* @param file a bundle file
|
||||
* @return the type
|
||||
*/
|
||||
static GhidraBundle.Type getType(ResourceFile path) {
|
||||
if (path.isDirectory()) {
|
||||
return GhidraBundle.Type.SourceDir;
|
||||
static GhidraBundle.Type getType(ResourceFile file) {
|
||||
if (file.isDirectory()) {
|
||||
return GhidraBundle.Type.SOURCE_DIR;
|
||||
}
|
||||
String n = path.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BndScript;
|
||||
String fileName = file.getName().toLowerCase();
|
||||
if (fileName.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BND_SCRIPT;
|
||||
}
|
||||
if (n.endsWith(".jar")) {
|
||||
return GhidraBundle.Type.Jar;
|
||||
if (fileName.endsWith(".jar")) {
|
||||
return GhidraBundle.Type.JAR;
|
||||
}
|
||||
return GhidraBundle.Type.INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of a GhidraBundle from its path.
|
||||
* Get the type of a GhidraBundle from its file.
|
||||
*
|
||||
* @param path a file system path
|
||||
* @param file a bundle file
|
||||
* @return the type
|
||||
*/
|
||||
public static GhidraBundle.Type getType(File path) {
|
||||
if (path.isDirectory()) {
|
||||
return GhidraBundle.Type.SourceDir;
|
||||
public static GhidraBundle.Type getType(File file) {
|
||||
if (file.isDirectory()) {
|
||||
return GhidraBundle.Type.SOURCE_DIR;
|
||||
}
|
||||
String n = path.getName().toLowerCase();
|
||||
if (n.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BndScript;
|
||||
String fileName = file.getName().toLowerCase();
|
||||
if (fileName.endsWith(".bnd")) {
|
||||
return GhidraBundle.Type.BND_SCRIPT;
|
||||
}
|
||||
if (n.endsWith(".jar")) {
|
||||
return GhidraBundle.Type.Jar;
|
||||
if (fileName.endsWith(".jar")) {
|
||||
return GhidraBundle.Type.JAR;
|
||||
}
|
||||
return GhidraBundle.Type.INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OSGi bundle respresented by this GhidraBundle or null
|
||||
* Get the OSGi bundle represented by this GhidraBundle or null if it isn't in
|
||||
* the "installed" state.
|
||||
*
|
||||
* @return a Bundle or null
|
||||
*/
|
||||
public Bundle getOSGiBundle() {
|
||||
return bundleHost.getOSGiBundle(getBundleLocation());
|
||||
return bundleHost.getOSGiBundle(getLocationIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this bundle is active
|
||||
*/
|
||||
public boolean isActive() {
|
||||
Bundle b = getOSGiBundle();
|
||||
return (b != null) && b.getState() == Bundle.ACTIVE;
|
||||
Bundle bundle = getOSGiBundle();
|
||||
return (bundle != null) && bundle.getState() == Bundle.ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A GhidraBundle can be
|
||||
* <ul>
|
||||
* <li>a Bndtools .bnd script</li>
|
||||
* <li>an OSGi bundle .jar file</li>
|
||||
* <li>a directory of Java source</li>
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
enum Type {
|
||||
BND_SCRIPT, JAR, SOURCE_DIR, INVALID
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+26
-49
@@ -20,12 +20,15 @@ import java.util.stream.Collectors;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleException;
|
||||
|
||||
/**
|
||||
* {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
|
||||
*/
|
||||
public class GhidraBundleException extends OSGiException {
|
||||
private final Bundle bundle;
|
||||
private final String bundleLocation;
|
||||
|
||||
/**
|
||||
* {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
|
||||
* Construct a new exception originating with {@code bundle}.
|
||||
*
|
||||
* @param bundle the bundle (if available)
|
||||
* @param msg a contextual message
|
||||
@@ -38,7 +41,7 @@ public class GhidraBundleException extends OSGiException {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link GhidraBundleException}s store the context associated with exceptions thrown during bundle operations.
|
||||
* Construct a new exception originating with the bundle having location identifier {@code bundleLocation}.
|
||||
*
|
||||
* @param bundleLocation the bundle location identifier (since no bundle is available)
|
||||
* @param msg a contextual message
|
||||
@@ -71,73 +74,56 @@ public class GhidraBundleException extends OSGiException {
|
||||
return "";
|
||||
}
|
||||
if (e instanceof BundleException) {
|
||||
BundleException be = (BundleException) e;
|
||||
switch (be.getType()) {
|
||||
BundleException bundleException = (BundleException) e;
|
||||
switch (bundleException.getType()) {
|
||||
default:
|
||||
return "No exception type";
|
||||
case BundleException.UNSPECIFIED:
|
||||
return "UNSPECIFIED";
|
||||
/**
|
||||
* The operation was unsupported. This type can be used anywhere a
|
||||
* BundleException can be thrown.
|
||||
*/
|
||||
// The operation was unsupported. This type can be used anywhere a BundleException can be thrown.
|
||||
case BundleException.UNSUPPORTED_OPERATION:
|
||||
return "UNSUPPORTED_OPERATION";
|
||||
|
||||
/**
|
||||
* The operation was invalid.
|
||||
*/
|
||||
// The operation was invalid.
|
||||
case BundleException.INVALID_OPERATION:
|
||||
return "INVALID_OPERATION";
|
||||
|
||||
/**
|
||||
* The bundle manifest was in error.
|
||||
*/
|
||||
// The bundle manifest was in error.
|
||||
case BundleException.MANIFEST_ERROR:
|
||||
return "MANIFEST_ERROR";
|
||||
|
||||
/**
|
||||
* The bundle was not resolved.
|
||||
*/
|
||||
// The bundle was not resolved.
|
||||
case BundleException.RESOLVE_ERROR: {
|
||||
String message = be.getMessage();
|
||||
String message = bundleException.getMessage();
|
||||
if (message.startsWith("Unable to acquire global lock")) {
|
||||
return message;
|
||||
}
|
||||
// parse the package constraints from filters in the BundleRequirement string
|
||||
String packages = OSGiUtils.extractPackageNamesFromFailedResolution(be.getMessage())
|
||||
.stream()
|
||||
.distinct()
|
||||
.collect(Collectors.joining("\n"));
|
||||
String packages =
|
||||
OSGiUtils.extractPackageNamesFromFailedResolution(bundleException.getMessage())
|
||||
.stream()
|
||||
.distinct()
|
||||
.collect(Collectors.joining("\n"));
|
||||
return "RESOLVE_ERROR with reference to packages:\n" + packages;
|
||||
}
|
||||
|
||||
/**
|
||||
* The bundle activator was in error.
|
||||
*/
|
||||
// The bundle activator was in error.
|
||||
case BundleException.ACTIVATOR_ERROR:
|
||||
return "ACTIVATOR_ERROR";
|
||||
|
||||
/**
|
||||
* The operation failed due to insufficient permissions.
|
||||
*/
|
||||
// The operation failed due to insufficient permissions.
|
||||
case BundleException.SECURITY_ERROR:
|
||||
return "SECURITY_ERROR";
|
||||
|
||||
/**
|
||||
* The operation failed to complete the requested lifecycle state change.
|
||||
*/
|
||||
// The operation failed to complete the requested lifecycle state change.
|
||||
case BundleException.STATECHANGE_ERROR:
|
||||
return "STATECHANGE_ERROR";
|
||||
|
||||
/**
|
||||
* The bundle could not be resolved due to an error with the
|
||||
* Bundle-NativeCode header.
|
||||
*/
|
||||
// The bundle could not be resolved due to an error with the Bundle-NativeCode header.
|
||||
case BundleException.NATIVECODE_ERROR:
|
||||
return "NATIVECODE_ERROR";
|
||||
|
||||
/**
|
||||
/*
|
||||
* The install or update operation failed because another already installed
|
||||
* bundle has the same symbolic name and version. This exception type will
|
||||
* only occur if the framework is configured to only allow a single bundle
|
||||
@@ -148,25 +134,16 @@ public class GhidraBundleException extends OSGiException {
|
||||
case BundleException.DUPLICATE_BUNDLE_ERROR:
|
||||
return "DUPLICATE_BUNDLE_ERROR";
|
||||
|
||||
/**
|
||||
* The start transient operation failed because the start level of the
|
||||
* bundle is greater than the current framework start level
|
||||
*/
|
||||
// The start transient operation failed because the start level of the bundle
|
||||
// is greater than the current framework start level
|
||||
case BundleException.START_TRANSIENT_ERROR:
|
||||
return "START_TRANSIENT_ERROR";
|
||||
|
||||
/**
|
||||
* The framework received an error while reading the input stream for a
|
||||
* bundle.
|
||||
*/
|
||||
// The framework received an error while reading the input stream for a bundle.
|
||||
case BundleException.READ_ERROR:
|
||||
return "READ_ERROR";
|
||||
|
||||
/**
|
||||
* A framework hook rejected the operation.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
// A framework hook rejected the operation.
|
||||
case BundleException.REJECTED_BY_HOOK:
|
||||
return "REJECTED_BY_HOOK";
|
||||
}
|
||||
|
||||
+10
-12
@@ -36,14 +36,14 @@ public class GhidraJarBundle extends GhidraBundle {
|
||||
* {@link GhidraJarBundle} wraps an ordinary OSGi bundle .jar.
|
||||
*
|
||||
* @param bundleHost the {@link BundleHost} instance this bundle will belong to
|
||||
* @param path the jar file's path
|
||||
* @param file the jar file
|
||||
* @param enabled true to start enabled
|
||||
* @param systemBundle true if this is a Ghidra system bundle
|
||||
*/
|
||||
public GhidraJarBundle(BundleHost bundleHost, ResourceFile path, boolean enabled,
|
||||
public GhidraJarBundle(BundleHost bundleHost, ResourceFile file, boolean enabled,
|
||||
boolean systemBundle) {
|
||||
super(bundleHost, path, enabled, systemBundle);
|
||||
this.bundleLocation = "file://" + path.getAbsolutePath().toString();
|
||||
super(bundleHost, file, enabled, systemBundle);
|
||||
this.bundleLocation = "file://" + file.getAbsolutePath().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,19 +57,17 @@ public class GhidraJarBundle extends GhidraBundle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBundleLocation() {
|
||||
public String getLocationIdentifier() {
|
||||
return bundleLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BundleRequirement> getAllRequirements() {
|
||||
Jar jar;
|
||||
try {
|
||||
jar = new Jar(path.getFile(true));
|
||||
Manifest m = jar.getManifest();
|
||||
String imps = m.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
|
||||
if (imps != null) {
|
||||
return OSGiUtils.parseImports(imps);
|
||||
try (Jar jar = new Jar(file.getFile(true))) {
|
||||
Manifest manifest = jar.getManifest();
|
||||
String importPackageString = manifest.getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
|
||||
if (importPackageString != null) {
|
||||
return OSGiUtils.parseImportPackage(importPackageString);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
+4
-4
@@ -28,9 +28,9 @@ import generic.jar.ResourceFile;
|
||||
*/
|
||||
public class GhidraPlaceholderBundle extends GhidraBundle {
|
||||
|
||||
GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile bundlePath, boolean isEnabled,
|
||||
GhidraPlaceholderBundle(BundleHost bundleHost, ResourceFile bundleFile, boolean isEnabled,
|
||||
boolean isSystemBundle) {
|
||||
super(bundleHost, bundlePath, isEnabled, isSystemBundle);
|
||||
super(bundleHost, bundleFile, isEnabled, isSystemBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,8 +44,8 @@ public class GhidraPlaceholderBundle extends GhidraBundle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBundleLocation() {
|
||||
return "invalid://" + getPath();
|
||||
public String getLocationIdentifier() {
|
||||
return "invalid://" + getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+447
-295
File diff suppressed because it is too large
Load Diff
+5
-2
@@ -22,8 +22,11 @@ import javax.swing.table.TableModel;
|
||||
import docking.widgets.table.*;
|
||||
|
||||
/**
|
||||
* RowObjectSelectionManager attempts to repair selections in a filtered table
|
||||
* before and after filter events. The additional selection events, however, cause focus changes we don't want.
|
||||
* {@link RowObjectSelectionManager} attempts to repair selections in a filtered table
|
||||
* before and after filter events. The additional selection events, however, cause focus changes we don't want.
|
||||
*
|
||||
* <p>if the behavior is a bug in RowObjectSelectionManager, and it's fixed, this
|
||||
* class can go away and it's use in {@link BundleStatusComponentProvider} replaced with GTable.
|
||||
*/
|
||||
class LessFreneticGTable extends GTable {
|
||||
boolean chilled = false;
|
||||
|
||||
@@ -17,9 +17,12 @@ package ghidra.app.plugin.core.osgi;
|
||||
|
||||
import ghidra.util.exception.UsrException;
|
||||
|
||||
/**
|
||||
* Wrapper for exceptions originating with an OSGi operation.
|
||||
*/
|
||||
public class OSGiException extends UsrException {
|
||||
/**
|
||||
* Wrapper for exceptions originating with an OSGi operation.
|
||||
* Create an exception with given {@code message} and {@code cause}.
|
||||
*
|
||||
* @param message a contextual message
|
||||
* @param cause the original exception
|
||||
@@ -29,7 +32,7 @@ public class OSGiException extends UsrException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for exceptions originating with an OSGi operation.
|
||||
* Create an exception with given {@code message}.
|
||||
*
|
||||
* @param message a contextual message
|
||||
*/
|
||||
|
||||
@@ -77,17 +77,17 @@ public class OSGiUtils {
|
||||
/**
|
||||
* parse Import-Package string from a bundle manifest
|
||||
*
|
||||
* @param imports Import-Package value
|
||||
* @param importPackageString Import-Package value
|
||||
* @return deduced requirements or null if there was an error
|
||||
* @throws BundleException on parse failure
|
||||
*/
|
||||
static List<BundleRequirement> parseImports(String imports) throws BundleException {
|
||||
static List<BundleRequirement> parseImportPackage(String importPackageString)
|
||||
throws BundleException {
|
||||
// parse it with Felix's ManifestParser to a list of BundleRequirement objects
|
||||
Map<String, Object> headerMap = new HashMap<>();
|
||||
headerMap.put(Constants.IMPORT_PACKAGE, imports);
|
||||
ManifestParser mp;
|
||||
mp = new ManifestParser(null, null, null, headerMap);
|
||||
return mp.getRequirements();
|
||||
headerMap.put(Constants.IMPORT_PACKAGE, importPackageString);
|
||||
ManifestParser manifestParser = new ManifestParser(null, null, null, headerMap);
|
||||
return manifestParser.getRequirements();
|
||||
}
|
||||
|
||||
// from https://dzone.com/articles/locate-jar-classpath-given
|
||||
@@ -102,10 +102,10 @@ public class OSGiUtils {
|
||||
location = loader.getResource(classLocation);
|
||||
}
|
||||
if (location != null) {
|
||||
Pattern p = Pattern.compile("^.*:(.*)!.*$");
|
||||
Matcher m = p.matcher(location.toString());
|
||||
if (m.find()) {
|
||||
return m.group(1);
|
||||
Pattern pattern = Pattern.compile("^.*:(.*)!.*$");
|
||||
Matcher matcher = pattern.matcher(location.toString());
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null; // not loaded from jar?
|
||||
}
|
||||
@@ -132,13 +132,14 @@ public class OSGiUtils {
|
||||
.map(Path::normalize);
|
||||
}
|
||||
|
||||
static void collectPackagesFromDirectory(Path dirPath, Set<String> s) {
|
||||
static void collectPackagesFromDirectory(Path dirPath, Set<String> packages) {
|
||||
try {
|
||||
Files.walk(dirPath).filter(p -> p.toString().endsWith(".class")).forEach(p -> {
|
||||
String n = dirPath.relativize(p).toString();
|
||||
int lastSlash = n.lastIndexOf(File.separatorChar);
|
||||
s.add(lastSlash > 0 ? n.substring(0, lastSlash).replace(File.separatorChar, '.')
|
||||
: "");
|
||||
Files.walk(dirPath).filter(p -> p.toString().endsWith(".class")).forEach(path -> {
|
||||
String relativePath = dirPath.relativize(path).toString();
|
||||
int lastSlash = relativePath.lastIndexOf(File.separatorChar);
|
||||
packages
|
||||
.add(lastSlash > 0 ? relativePath.substring(0, lastSlash).replace(File.separatorChar, '.')
|
||||
: "");
|
||||
});
|
||||
|
||||
}
|
||||
@@ -147,13 +148,13 @@ public class OSGiUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void collectPackagesFromJar(Path jarPath, Set<String> s) {
|
||||
static void collectPackagesFromJar(Path jarPath, Set<String> packages) {
|
||||
try {
|
||||
try (JarFile j = new JarFile(jarPath.toFile())) {
|
||||
j.stream().filter(je -> je.getName().endsWith(".class")).forEach(je -> {
|
||||
String n = je.getName();
|
||||
int lastSlash = n.lastIndexOf('/');
|
||||
s.add(lastSlash > 0 ? n.substring(0, lastSlash).replace('/', '.') : "");
|
||||
j.stream().filter(entry -> entry.getName().endsWith(".class")).forEach(jarEntry -> {
|
||||
String entryName = jarEntry.getName();
|
||||
int lastSlash = entryName.lastIndexOf('/');
|
||||
packages.add(lastSlash > 0 ? entryName.substring(0, lastSlash).replace('/', '.') : "");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+64
-88
@@ -21,6 +21,7 @@ import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -30,6 +31,7 @@ import javax.swing.KeyStroke;
|
||||
import docking.ActionContext;
|
||||
import docking.DockingUtils;
|
||||
import docking.action.*;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.table.GTable;
|
||||
@@ -61,7 +63,6 @@ class GhidraScriptActionManager {
|
||||
private DockingAction globalRunLastAction;
|
||||
private DockingAction renameAction;
|
||||
private DockingAction keyBindingAction;
|
||||
private DockingAction helpAction;
|
||||
private Map<ResourceFile, ScriptAction> actionMap = new HashMap<>();
|
||||
|
||||
GhidraScriptActionManager(GhidraScriptComponentProvider provider, GhidraScriptMgrPlugin plugin,
|
||||
@@ -78,7 +79,7 @@ class GhidraScriptActionManager {
|
||||
}
|
||||
|
||||
void restoreUserDefinedKeybindings(SaveState saveState) {
|
||||
Collection<ResourceFile> dirs = provider.getBundleHost().getBundlePaths();
|
||||
Collection<ResourceFile> dirs = provider.getBundleHost().getBundleFiles();
|
||||
String[] names = saveState.getNames();
|
||||
|
||||
for (String name : names) {
|
||||
@@ -172,54 +173,32 @@ class GhidraScriptActionManager {
|
||||
globalRunLastAction.firePropertyChanged(DockingActionIf.DESCRIPTION_PROPERTY, "", newDesc);
|
||||
}
|
||||
|
||||
private DockingAction createScriptAction(String name, String menuEntry,
|
||||
String actionDescription, Icon icon, String toolBarGroup, Runnable runnable) {
|
||||
DockingAction action = new DockingAction(name, plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
return contextObject instanceof ResourceFile;
|
||||
}
|
||||
};
|
||||
action.setPopupMenuData(new MenuData(new String[] { menuEntry }, icon));
|
||||
action.setToolBarData(new ToolBarData(icon, toolBarGroup));
|
||||
|
||||
action.setDescription(actionDescription);
|
||||
action.setEnabled(false);
|
||||
|
||||
plugin.getTool().addLocalAction(provider, action);
|
||||
|
||||
return action;
|
||||
private DockingAction createScriptAction(String name, String menuEntry, String description,
|
||||
Icon icon, String toolBarGroup, Runnable runnable) {
|
||||
return new ActionBuilder(name, plugin.getName()).popupMenuPath(menuEntry)
|
||||
.popupMenuIcon(icon)
|
||||
.toolBarIcon(icon)
|
||||
.toolBarGroup(toolBarGroup)
|
||||
.description(description)
|
||||
.enabled(false)
|
||||
.enabledWhen(context -> context.getContextObject() instanceof ResourceFile)
|
||||
.onAction(context -> runnable.run())
|
||||
.buildAndInstallLocal(provider);
|
||||
}
|
||||
|
||||
private DockingAction createScriptTableAction(String name, String actionDescription, Icon icon,
|
||||
private DockingAction createScriptTableAction(String name, String description, Icon icon,
|
||||
Runnable runnable) {
|
||||
DockingAction action = new DockingAction(name, plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
return new ActionBuilder(name, plugin.getName()).popupMenuPath(name)
|
||||
.popupMenuIcon(icon)
|
||||
.toolBarIcon(icon)
|
||||
.toolBarGroup(null)
|
||||
.description(description)
|
||||
.enabledWhen(context -> {
|
||||
Object contextObject = context.getContextObject();
|
||||
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
|
||||
}
|
||||
};
|
||||
action.setPopupMenuData(new MenuData(new String[] { name }, icon));
|
||||
action.setToolBarData(new ToolBarData(icon, null));
|
||||
|
||||
action.setDescription(actionDescription);
|
||||
action.setEnabled(true);
|
||||
|
||||
plugin.getTool().addLocalAction(provider, action);
|
||||
|
||||
return action;
|
||||
})
|
||||
.onAction(context -> runnable.run())
|
||||
.buildAndInstallLocal(provider);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
@@ -253,60 +232,57 @@ class GhidraScriptActionManager {
|
||||
newAction = createScriptTableAction("New", "Create New Script",
|
||||
ResourceManager.loadImage("images/script_add.png"), provider::newScript);
|
||||
|
||||
createScriptTableAction("Refresh", "Refresh Script List",
|
||||
Icons.REFRESH_ICON, provider::refresh);
|
||||
createScriptTableAction("Refresh", "Refresh Script List", Icons.REFRESH_ICON,
|
||||
provider::refresh);
|
||||
|
||||
showBundleStatusAction = createScriptTableAction("Script Directories",
|
||||
"Manage Script Directories", ResourceManager.loadImage("images/text_list_bullets.png"),
|
||||
provider::showBundleStatusComponent);
|
||||
|
||||
helpAction = new DockingAction("Ghidra API Help", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
showGhidraScriptJavadoc();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledForContext(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAddToPopup(ActionContext context) {
|
||||
Object contextObject = context.getContextObject();
|
||||
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
|
||||
}
|
||||
Icon icon = ResourceManager.loadImage("images/red-cross.png");
|
||||
Predicate<ActionContext> test = context -> {
|
||||
Object contextObject = context.getContextObject();
|
||||
return (contextObject instanceof GTable) || (contextObject instanceof ResourceFile);
|
||||
};
|
||||
|
||||
helpAction.setPopupMenuData(new MenuData(new String[] { "Ghidra API Help" },
|
||||
ResourceManager.loadImage("images/red-cross.png"), null));
|
||||
helpAction.setToolBarData(
|
||||
new ToolBarData(ResourceManager.loadImage("images/red-cross.png"), null));
|
||||
new ActionBuilder("Ghidra API Help", plugin.getName()).popupMenuPath("Ghidra API Help")
|
||||
.popupMenuIcon(icon)
|
||||
.popupWhen(test)
|
||||
.toolBarIcon(icon)
|
||||
.toolBarGroup(null)
|
||||
.description("Help")
|
||||
.helpLocation(new HelpLocation(plugin.getName(), "Help"))
|
||||
.enabledWhen(test)
|
||||
.onAction(context -> showGhidraScriptJavadoc())
|
||||
.buildAndInstallLocal(provider);
|
||||
|
||||
helpAction.setDescription("Help");
|
||||
helpAction.setEnabled(true);
|
||||
helpAction.setHelpLocation(new HelpLocation(plugin.getName(), "Help"));
|
||||
plugin.getTool().addLocalAction(provider, helpAction);
|
||||
|
||||
DockingAction globalHelpAction = new DockingAction("Ghidra API Help", plugin.getName()) {
|
||||
// XXX In order to override a method of the new DockingAction and use the builder, we
|
||||
// need to override the build method of the ActionBuilder. When the ActionBuilder is
|
||||
// updated, this code can be cleaned up.
|
||||
new ActionBuilder("Ghidra API Help", plugin.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
showGhidraScriptJavadoc();
|
||||
}
|
||||
public DockingAction build() {
|
||||
validate();
|
||||
DockingAction action = new DockingAction(name, owner, keyBindingType) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
actionCallback.accept(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldAddToWindow(boolean isMainWindow, Set<Class<?>> contextTypes) {
|
||||
return true;
|
||||
@Override
|
||||
public boolean shouldAddToWindow(boolean isMainWindow,
|
||||
Set<Class<?>> contextTypes) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
decorateAction(action);
|
||||
return action;
|
||||
}
|
||||
};
|
||||
globalHelpAction.setEnabled(true);
|
||||
globalHelpAction.setHelpLocation(new HelpLocation("Misc", "Welcome_to_Ghidra_Help"));
|
||||
globalHelpAction.setMenuBarData(
|
||||
new MenuData(new String[] { ToolConstants.MENU_HELP, "Ghidra API Help" }, null,
|
||||
ToolConstants.HELP_CONTENTS_MENU_GROUP));
|
||||
plugin.getTool().addAction(globalHelpAction);
|
||||
|
||||
}.menuGroup(ToolConstants.HELP_CONTENTS_MENU_GROUP)
|
||||
.menuPath(ToolConstants.MENU_HELP, "Ghidra API Help")
|
||||
.helpLocation(new HelpLocation("Misc", "Welcome_to_Ghidra_Help"))
|
||||
.onAction(context -> showGhidraScriptJavadoc())
|
||||
.buildAndInstall(plugin.getTool());
|
||||
}
|
||||
|
||||
private void showGhidraScriptJavadoc() {
|
||||
|
||||
+69
-70
@@ -458,7 +458,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
.stream()
|
||||
.filter(GhidraSourceBundle.class::isInstance)
|
||||
.filter(GhidraBundle::isEnabled)
|
||||
.map(GhidraBundle::getPath)
|
||||
.map(GhidraBundle::getFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -471,7 +471,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
.filter(GhidraSourceBundle.class::isInstance)
|
||||
.filter(Predicate.not(GhidraBundle::isSystemBundle))
|
||||
.filter(GhidraBundle::isEnabled)
|
||||
.map(GhidraBundle::getPath)
|
||||
.map(GhidraBundle::getFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@@ -530,7 +530,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
void enableScriptDirectory(ResourceFile scriptDir) {
|
||||
bundleHost.enablePath(scriptDir);
|
||||
bundleHost.enable(scriptDir);
|
||||
Msg.showInfo(this, getComponent(), "Script Path Added/Enabled",
|
||||
"The directory has been automatically enabled for use:\n" +
|
||||
scriptDir.getAbsolutePath());
|
||||
@@ -586,7 +586,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
|
||||
void runScript(String scriptName, TaskListener listener) {
|
||||
for (ResourceFile dir : bundleHost.getBundlePaths()) {
|
||||
for (ResourceFile dir : bundleHost.getBundleFiles()) {
|
||||
if (dir.isDirectory()) {
|
||||
ResourceFile scriptSource = new ResourceFile(dir, scriptName);
|
||||
if (scriptSource.exists()) {
|
||||
@@ -711,55 +711,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
bundleStatusComponentProvider.setVisible(true);
|
||||
}
|
||||
|
||||
class RefreshingBundleHostListener implements BundleHostListener {
|
||||
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
if (bundle instanceof GhidraSourceBundle) {
|
||||
GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
|
||||
for (ResourceFile sourceFile : sourceBundle.getNewSources()) {
|
||||
if (infoManager.containsMetadata(sourceFile)) {
|
||||
ScriptInfo scriptInfo = infoManager.getExistingScriptInfo(sourceFile);
|
||||
GhidraBundle.BuildFailure e = sourceBundle.getErrors(sourceFile);
|
||||
scriptInfo.setCompileErrors(e != null);
|
||||
}
|
||||
}
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablment) {
|
||||
if (bundle instanceof GhidraSourceBundle) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle bundle) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleRemoved(GhidraBundle bundle) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
hasBeenRefreshed = true;
|
||||
|
||||
@@ -778,8 +729,8 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
private void updateAvailableScriptFilesForAllPaths() {
|
||||
List<ResourceFile> scriptsToRemove = tableModel.getScripts();
|
||||
List<ResourceFile> scriptAccumulator = new ArrayList<>();
|
||||
for (ResourceFile bundlePath : getScriptDirectories()) {
|
||||
updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator, bundlePath);
|
||||
for (ResourceFile bundleFile : getScriptDirectories()) {
|
||||
updateAvailableScriptFilesForDirectory(scriptsToRemove, scriptAccumulator, bundleFile);
|
||||
}
|
||||
|
||||
// note: do this after the loop to prevent a flurry of table model update events
|
||||
@@ -1135,21 +1086,6 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
return taskListener;
|
||||
}
|
||||
|
||||
private class ScriptTaskListener implements TaskListener {
|
||||
@Override
|
||||
public void taskCancelled(Task task) {
|
||||
taskCompleted(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskCompleted(Task task) {
|
||||
Rectangle visibleRect = scriptTable.getVisibleRect();
|
||||
scriptTable.repaint(visibleRect);
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
|
||||
@Override
|
||||
public void componentShown() {
|
||||
if (!hasBeenRefreshed) {
|
||||
@@ -1199,6 +1135,69 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
/** passed to runScript, repaints scriptTable when a script completes */
|
||||
private class ScriptTaskListener implements TaskListener {
|
||||
@Override
|
||||
public void taskCancelled(Task task) {
|
||||
taskCompleted(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskCompleted(Task task) {
|
||||
Rectangle visibleRect = scriptTable.getVisibleRect();
|
||||
scriptTable.repaint(visibleRect);
|
||||
}
|
||||
}
|
||||
|
||||
class RefreshingBundleHostListener implements BundleHostListener {
|
||||
|
||||
@Override
|
||||
public void bundleBuilt(GhidraBundle bundle, String summary) {
|
||||
if (bundle instanceof GhidraSourceBundle) {
|
||||
GhidraSourceBundle sourceBundle = (GhidraSourceBundle) bundle;
|
||||
for (ResourceFile sourceFile : sourceBundle.getNewSources()) {
|
||||
if (infoManager.containsMetadata(sourceFile)) {
|
||||
ScriptInfo scriptInfo = infoManager.getExistingScriptInfo(sourceFile);
|
||||
BuildError e = sourceBundle.getErrors(sourceFile);
|
||||
scriptInfo.setCompileErrors(e != null);
|
||||
}
|
||||
}
|
||||
tableModel.fireTableDataChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleEnablementChange(GhidraBundle bundle, boolean newEnablment) {
|
||||
if (bundle instanceof GhidraSourceBundle) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleAdded(GhidraBundle bundle) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesAdded(Collection<GhidraBundle> bundles) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleRemoved(GhidraBundle bundle) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundlesRemoved(Collection<GhidraBundle> bundles) {
|
||||
plugin.getTool().setConfigChanged(true);
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/** Table filter that uses the state of the tree to further filter */
|
||||
private class ScriptTableSecondaryFilter implements TableFilter<ResourceFile> {
|
||||
|
||||
|
||||
+2
-2
@@ -133,8 +133,8 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isReadOnly(ResourceFile scriptSourceFile1) {
|
||||
return GhidraScriptUtil.isSystemScriptPath(scriptSourceFile1);
|
||||
private static boolean isReadOnly(ResourceFile scriptSourceFile) {
|
||||
return GhidraScriptUtil.isSystemScript(scriptSourceFile);
|
||||
}
|
||||
|
||||
// private boolean isSystemScript() {
|
||||
|
||||
+8
-5
@@ -48,7 +48,8 @@ import ghidra.util.task.TaskListener;
|
||||
)
|
||||
//@formatter:on
|
||||
public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScriptService {
|
||||
private static int loaded = 0;
|
||||
/** number of GhidraScriptMgrPlugin references to the BundleHost owned by {@link GhidraScriptUtil} */
|
||||
private static int referenceCount = 0;
|
||||
|
||||
private final GhidraScriptComponentProvider provider;
|
||||
|
||||
@@ -61,14 +62,16 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
|
||||
*/
|
||||
public GhidraScriptMgrPlugin(PluginTool tool) {
|
||||
super(tool, true, true, true);
|
||||
if (loaded == 0) {
|
||||
// Each tool starts a new script manager plugin, but we only ever want one bundle host.
|
||||
// We store the one BundleHost in GhidraScriptUtil and keep a count of references to it.
|
||||
if (referenceCount == 0) {
|
||||
bundleHost = new BundleHost();
|
||||
GhidraScriptUtil.initialize(bundleHost, null);
|
||||
}
|
||||
else {
|
||||
bundleHost = GhidraScriptUtil.getBundleHost();
|
||||
}
|
||||
loaded += 1;
|
||||
referenceCount += 1;
|
||||
|
||||
provider = new GhidraScriptComponentProvider(this, bundleHost);
|
||||
}
|
||||
@@ -77,8 +80,8 @@ public class GhidraScriptMgrPlugin extends ProgramPlugin implements GhidraScript
|
||||
protected void dispose() {
|
||||
super.dispose();
|
||||
provider.dispose();
|
||||
loaded -= 1;
|
||||
if (loaded == 0) {
|
||||
referenceCount -= 1;
|
||||
if (referenceCount == 0) {
|
||||
GhidraScriptUtil.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ import ghidra.util.task.TaskMonitor;
|
||||
* </pre>
|
||||
* <h3>Ghidra Script State</h3>
|
||||
* <blockquote>
|
||||
* <p>
|
||||
* All scripts, when run, will be handed the current state in the form of class
|
||||
* instance variable. These variables are:
|
||||
*
|
||||
* <p>All scripts, when run, will be handed the current state in the form of class instance
|
||||
* variable. These variables are:
|
||||
* <ol>
|
||||
* <li><code>currentProgram</code>: the active program</li>
|
||||
* <li><code>currentAddress</code>: the address of the current cursor location in the tool</li>
|
||||
|
||||
@@ -79,7 +79,7 @@ public class GhidraScriptInfoManager {
|
||||
* Returns the script info object for the specified script file,
|
||||
* construct a new one if necessary.
|
||||
*
|
||||
* Only call this method if you expect to be creating ScriptInfo objects.
|
||||
* <p>Only call this method if you expect to be creating ScriptInfo objects.
|
||||
* Prefer getExistingScriptInfo instead.
|
||||
*
|
||||
* @param scriptFile the script file
|
||||
@@ -122,10 +122,9 @@ public class GhidraScriptInfoManager {
|
||||
public ScriptInfo getExistingScriptInfo(ResourceFile script) {
|
||||
ScriptInfo info = scriptFileToInfoMap.get(script);
|
||||
if (info == null) {
|
||||
String s = (script.exists() ? "" : "non") + "existing script" + script.toString() +
|
||||
String error = (script.exists() ? "" : "non") + "existing script" + script.toString() +
|
||||
" is missing expected ScriptInfo";
|
||||
System.err.println(s);
|
||||
Msg.showError(GhidraScriptInfoManager.class, null, "ScriptInfo lookup", s);
|
||||
Msg.showError(GhidraScriptInfoManager.class, null, "ScriptInfo lookup", error);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import ghidra.util.Msg;
|
||||
* Handles processing for .properties files associated with a GhidraScript (.properties file and
|
||||
* script should share the same basename).
|
||||
*
|
||||
* This should only be called/used by the GhidraScript class.
|
||||
* <p>This should only be called/used by the GhidraScript class.
|
||||
*/
|
||||
public class GhidraScriptProperties {
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.osgi.BundleHost;
|
||||
import ghidra.app.plugin.core.osgi.OSGiException;
|
||||
import ghidra.app.plugin.core.script.GhidraScriptMgrPlugin;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
@@ -37,6 +38,9 @@ public class GhidraScriptUtil {
|
||||
*/
|
||||
public static String USER_SCRIPTS_DIR = buildUserScriptsDirectory();
|
||||
|
||||
/**
|
||||
* this instance is Ghidra's singleton, a reference is held here and in {@link GhidraScriptMgrPlugin}
|
||||
*/
|
||||
private static BundleHost bundleHost;
|
||||
|
||||
private static final String SCRIPTS_SUBDIR_NAME = "ghidra_scripts";
|
||||
@@ -87,7 +91,7 @@ public class GhidraScriptUtil {
|
||||
}
|
||||
|
||||
bundleHost.add(getUserScriptDirectory(), true, false);
|
||||
bundleHost.add(getSystemScriptPaths(), true, true);
|
||||
bundleHost.add(getSystemScriptDirectories(), true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,13 +110,19 @@ public class GhidraScriptUtil {
|
||||
* @return a list of the current script directories
|
||||
*/
|
||||
public static List<ResourceFile> getScriptSourceDirectories() {
|
||||
return bundleHost.getBundlePaths()
|
||||
return bundleHost.getBundleFiles()
|
||||
.stream()
|
||||
.filter(ResourceFile::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static ResourceFile getSourceDirectoryContaining(ResourceFile sourceFile) {
|
||||
/**
|
||||
* Search the currently managed source directories for the given script file.
|
||||
*
|
||||
* @param sourceFile the source file
|
||||
* @return the source directory if found, or null if not
|
||||
*/
|
||||
public static ResourceFile findSourceDirectoryContaining(ResourceFile sourceFile) {
|
||||
String sourcePath = sourceFile.getAbsolutePath();
|
||||
for (ResourceFile sourceDir : getScriptSourceDirectories()) {
|
||||
if (sourcePath.startsWith(sourceDir.getAbsolutePath() + File.separatorChar)) {
|
||||
@@ -122,6 +132,12 @@ public class GhidraScriptUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the currently managed scripts for one with the given name.
|
||||
*
|
||||
* @param scriptName the name
|
||||
* @return the first file found or null if none are found
|
||||
*/
|
||||
public static ResourceFile findScriptByName(String scriptName) {
|
||||
return findScriptFileInPaths(getScriptSourceDirectories(), scriptName);
|
||||
}
|
||||
@@ -148,22 +164,22 @@ public class GhidraScriptUtil {
|
||||
* Returns a list of the default script directories.
|
||||
* @return a list of the default script directories
|
||||
*/
|
||||
public static List<ResourceFile> getSystemScriptPaths() {
|
||||
List<ResourceFile> pathsList = new ArrayList<>();
|
||||
public static List<ResourceFile> getSystemScriptDirectories() {
|
||||
List<ResourceFile> dirList = new ArrayList<>();
|
||||
|
||||
addScriptPaths(pathsList, SCRIPTS_SUBDIR_NAME);
|
||||
addScriptPaths(pathsList, DEV_SCRIPTS_SUBDIR_NAME);
|
||||
addScriptDirectories(dirList, SCRIPTS_SUBDIR_NAME);
|
||||
addScriptDirectories(dirList, DEV_SCRIPTS_SUBDIR_NAME);
|
||||
|
||||
Collections.sort(pathsList);
|
||||
return pathsList;
|
||||
Collections.sort(dirList);
|
||||
return dirList;
|
||||
}
|
||||
|
||||
public static ResourceFile getUserScriptDirectory() {
|
||||
return new ResourceFile(USER_SCRIPTS_DIR);
|
||||
}
|
||||
|
||||
private static void addScriptPaths(List<ResourceFile> pathsList, String directoryName) {
|
||||
pathsList.addAll(Application.findModuleSubDirectories(directoryName));
|
||||
private static void addScriptDirectories(List<ResourceFile> dirList, String directoryName) {
|
||||
dirList.addAll(Application.findModuleSubDirectories(directoryName));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +187,7 @@ public class GhidraScriptUtil {
|
||||
* @param file script file or directory
|
||||
* @return true if file contained within Ghidra installation area
|
||||
*/
|
||||
public static boolean isSystemScriptPath(ResourceFile file) {
|
||||
public static boolean isSystemScript(ResourceFile file) {
|
||||
return isSystemFile(file);
|
||||
}
|
||||
|
||||
@@ -333,8 +349,8 @@ public class GhidraScriptUtil {
|
||||
|
||||
/**
|
||||
* Fixup name issues, such as package parts in the name and inner class names.
|
||||
* <p>
|
||||
* This method can handle names with or without '.java' at the end; names with
|
||||
*
|
||||
* <p>This method can handle names with or without '.java' at the end; names with
|
||||
* '$' (inner classes) and names with '.' characters for package separators
|
||||
*
|
||||
* @param name the name of the script
|
||||
|
||||
@@ -40,7 +40,7 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
* @return the bundle
|
||||
*/
|
||||
public GhidraSourceBundle getBundleForSource(ResourceFile sourceFile) {
|
||||
ResourceFile sourceDir = GhidraScriptUtil.getSourceDirectoryContaining(sourceFile);
|
||||
ResourceFile sourceDir = GhidraScriptUtil.findSourceDirectoryContaining(sourceFile);
|
||||
if (sourceDir == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -111,15 +111,15 @@ public class JavaScriptProvider extends GhidraScriptProvider {
|
||||
* @throws Exception if build, activation, or class loading fail
|
||||
*/
|
||||
public Class<?> loadClass(ResourceFile sourceFile, PrintWriter writer) throws Exception {
|
||||
GhidraSourceBundle gb = getBundleForSource(sourceFile);
|
||||
gb.build(writer);
|
||||
GhidraSourceBundle bundle = getBundleForSource(sourceFile);
|
||||
bundle.build(writer);
|
||||
|
||||
Bundle b = bundleHost.install(gb);
|
||||
Bundle osgiBundle = bundleHost.install(bundle);
|
||||
|
||||
bundleHost.activateSynchronously(b);
|
||||
bundleHost.activateSynchronously(osgiBundle);
|
||||
|
||||
String classname = gb.classNameForScript(sourceFile);
|
||||
Class<?> clazz = b.loadClass(classname); // throws ClassNotFoundException
|
||||
String classname = bundle.classNameForScript(sourceFile);
|
||||
Class<?> clazz = osgiBundle.loadClass(classname); // throws ClassNotFoundException
|
||||
return clazz;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -26,9 +26,9 @@ import generic.jar.ResourceFile;
|
||||
import ghidra.util.exception.AssertException;
|
||||
|
||||
/**
|
||||
* A {@link JavaFileManager} that works with Ghidra's {@link ResourceFile}'s.
|
||||
* <p>
|
||||
* This class is used to dynamically compile Ghidra scripts.
|
||||
* A {@link JavaFileManager} that works with Ghidra's {@link ResourceFile}s.
|
||||
*
|
||||
* <p>This class is used to dynamically compile Ghidra scripts.
|
||||
*/
|
||||
public class ResourceFileJavaFileManager implements JavaFileManager {
|
||||
|
||||
|
||||
+2
-2
@@ -27,8 +27,8 @@ import generic.jar.ResourceFile;
|
||||
|
||||
/**
|
||||
* A {@link JavaFileObject} that works with Ghidra's {@link ResourceFileJavaFileManager}.
|
||||
* <p>
|
||||
* This class is used to dynamically compile Ghidra scripts.
|
||||
*
|
||||
* <p>This class is used to dynamically compile Ghidra scripts.
|
||||
*/
|
||||
public class ResourceFileJavaFileObject implements JavaFileObject {
|
||||
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
package ghidra.app.script;
|
||||
|
||||
public interface StringTransformer<T> {
|
||||
T apply(String s);
|
||||
public T apply(String s);
|
||||
}
|
||||
|
||||
@@ -550,33 +550,33 @@ public class TestEnv {
|
||||
});
|
||||
}
|
||||
|
||||
public ScriptTaskListener runScript(File script) throws PluginException {
|
||||
GhidraScriptMgrPlugin sm = getPlugin(GhidraScriptMgrPlugin.class);
|
||||
if (sm == null) {
|
||||
public ScriptTaskListener runScript(File scriptFile) throws PluginException {
|
||||
GhidraScriptMgrPlugin scriptManagerPlugin = getPlugin(GhidraScriptMgrPlugin.class);
|
||||
if (scriptManagerPlugin == null) {
|
||||
lazyTool().addPlugin(GhidraScriptMgrPlugin.class.getName());
|
||||
sm = getPlugin(GhidraScriptMgrPlugin.class);
|
||||
scriptManagerPlugin = getPlugin(GhidraScriptMgrPlugin.class);
|
||||
}
|
||||
|
||||
JavaScriptProvider scriptProvider = new JavaScriptProvider();
|
||||
PrintWriter writer = new PrintWriter(System.out);
|
||||
ResourceFile resourceFile = new ResourceFile(script);
|
||||
GhidraScript scr=null;
|
||||
ResourceFile resourceFile = new ResourceFile(scriptFile);
|
||||
GhidraScript script=null;
|
||||
try {
|
||||
scr=scriptProvider.getScriptInstance(resourceFile, writer);
|
||||
script=scriptProvider.getScriptInstance(resourceFile, writer);
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
}
|
||||
if (scr==null) {
|
||||
if (script==null) {
|
||||
writer.flush();
|
||||
throw new RuntimeException("Failed to compile script " + script.getAbsolutePath());
|
||||
throw new RuntimeException("Failed to compile script " + scriptFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
String scriptName = script.getName();
|
||||
String scriptName = scriptFile.getName();
|
||||
ScriptTaskListener listener = new ScriptTaskListener(scriptName);
|
||||
sm.runScript(scriptName, listener);
|
||||
scriptManagerPlugin.runScript(scriptName, listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
|
||||
+16
-16
@@ -261,20 +261,20 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
protected ResourceFile finishNewScriptDialog(String newScriptName) {
|
||||
|
||||
SaveDialog sd = waitForDialogComponent(SaveDialog.class);
|
||||
SaveDialog saveDialog = waitForDialogComponent(SaveDialog.class);
|
||||
if (newScriptName != null) {
|
||||
setNewScriptName(sd, newScriptName);
|
||||
setNewScriptName(saveDialog, newScriptName);
|
||||
}
|
||||
|
||||
pressButtonByText(sd, "OK");
|
||||
pressButtonByText(saveDialog, "OK");
|
||||
waitForSwing();
|
||||
|
||||
ResourceFile newFile = (ResourceFile) invokeInstanceMethod("getFile", sd);
|
||||
ResourceFile newFile = (ResourceFile) invokeInstanceMethod("getFile", saveDialog);
|
||||
assertNotNull(newFile);
|
||||
|
||||
JTextField textField = (JTextField) getInstanceField("nameField", sd);
|
||||
assertTrue("New script dialog did not close. Message: " + sd.getStatusText() +
|
||||
" - text: " + textField.getText(), !sd.isShowing());
|
||||
JTextField textField = (JTextField) getInstanceField("nameField", saveDialog);
|
||||
assertTrue("New script dialog did not close. Message: " + saveDialog.getStatusText() +
|
||||
" - text: " + textField.getText(), !saveDialog.isShowing());
|
||||
|
||||
return newFile;
|
||||
}
|
||||
@@ -334,8 +334,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
GDynamicColumnTableModel<?, ?> model =
|
||||
(GDynamicColumnTableModel<?, ?>) RowObjectTableModel.unwrap(tableModel);
|
||||
|
||||
int n = model.getColumnCount();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int columnCount = model.getColumnCount();
|
||||
for (int i = 0; i < columnCount; i++) {
|
||||
String name = model.getColumnName(i);
|
||||
if (columnName.equals(name)) {
|
||||
return i;
|
||||
@@ -405,8 +405,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
chooseJavaProvider();
|
||||
|
||||
SaveDialog sd = waitForDialogComponent(SaveDialog.class);
|
||||
pressButtonByText(sd, "OK");
|
||||
SaveDialog saveDialog = waitForDialogComponent(SaveDialog.class);
|
||||
pressButtonByText(saveDialog, "OK");
|
||||
waitForSwing();
|
||||
|
||||
// initialize our editor variable to the newly opened editor
|
||||
@@ -415,7 +415,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
waitForSwing();
|
||||
|
||||
return sd.getFile();
|
||||
return saveDialog.getFile();
|
||||
}
|
||||
|
||||
protected void assertCannotCreateNewScriptByName(final String name) throws Exception {
|
||||
@@ -847,8 +847,8 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
protected void assertEditorContentsSame(ResourceFile file, String expectedText) {
|
||||
|
||||
Map<ResourceFile, GhidraScriptEditorComponentProvider> map = provider.getEditorMap();
|
||||
GhidraScriptEditorComponentProvider fileEditor = map.get(file);
|
||||
Map<ResourceFile, GhidraScriptEditorComponentProvider> editorMap = provider.getEditorMap();
|
||||
GhidraScriptEditorComponentProvider fileEditor = editorMap.get(file);
|
||||
final JTextArea textArea =
|
||||
(JTextArea) findComponentByName(fileEditor.getComponent(), GhidraScriptEditorComponentProvider.EDITOR_COMPONENT_NAME);
|
||||
assertNotNull(textArea);
|
||||
@@ -985,7 +985,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
// destroy any NewScriptxxx files...and Temp ones too
|
||||
List<ResourceFile> paths = provider.getBundleHost()
|
||||
.getBundlePaths()
|
||||
.getBundleFiles()
|
||||
.stream()
|
||||
.filter(ResourceFile::isDirectory)
|
||||
.collect(Collectors.toList());
|
||||
@@ -1139,7 +1139,7 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
||||
|
||||
private boolean isReadOnly(ResourceFile script) {
|
||||
assertNotNull(script);
|
||||
return GhidraScriptUtil.isSystemScriptPath(script);
|
||||
return GhidraScriptUtil.isSystemScript(script);
|
||||
}
|
||||
|
||||
protected void assertSaveButtonDisabled() {
|
||||
|
||||
+44
-42
@@ -32,7 +32,15 @@ import ghidra.app.plugin.core.osgi.*;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
|
||||
public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
static protected void wipe(Path path) throws IOException {
|
||||
BundleHost bundleHost;
|
||||
CapturingBundleHostListener capturingBundleHostListener;
|
||||
|
||||
Set<Path> tempDirs = new HashSet<>();
|
||||
LinkedList<GhidraBundle> bundleStack = new LinkedList<>();
|
||||
GhidraBundle currentBundle;
|
||||
|
||||
|
||||
protected static void wipe(Path path) throws IOException {
|
||||
if (Files.exists(path)) {
|
||||
for (Path p : (Iterable<Path>) Files.walk(path).sorted(
|
||||
Comparator.reverseOrder())::iterator) {
|
||||
@@ -41,23 +49,17 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
}
|
||||
}
|
||||
|
||||
BundleHost bundleHost;
|
||||
CapturingBundleHostListener bhl;
|
||||
|
||||
Set<Path> tmpdirs = new HashSet<>();
|
||||
LinkedList<GhidraBundle> gbstack = new LinkedList<>();
|
||||
GhidraBundle current_gb;
|
||||
|
||||
protected GhidraBundle pushNewBundle() throws IOException {
|
||||
String dir = String.format("sourcebundle%03d", tmpdirs.size());
|
||||
String dir = String.format("sourcebundle%03d", tempDirs.size());
|
||||
Path tmpDir = new File(getTestDirectoryPath(), dir).toPath();
|
||||
Files.createDirectories(tmpDir);
|
||||
tmpdirs.add(tmpDir);
|
||||
tempDirs.add(tmpDir);
|
||||
|
||||
ResourceFile rp = new ResourceFile(tmpDir.toFile());
|
||||
current_gb = bundleHost.add(rp, true, false);
|
||||
gbstack.push(current_gb);
|
||||
return current_gb;
|
||||
ResourceFile sourceDirectory = new ResourceFile(tmpDir.toFile());
|
||||
currentBundle = bundleHost.add(sourceDirectory, true, false);
|
||||
bundleStack.push(currentBundle);
|
||||
return currentBundle;
|
||||
}
|
||||
|
||||
static class CapturingBundleHostListener implements BundleHostListener {
|
||||
@@ -75,8 +77,8 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
bundleHost = new BundleHost();
|
||||
bundleHost.startFramework();
|
||||
bhl = new CapturingBundleHostListener();
|
||||
bundleHost.addListener(bhl);
|
||||
capturingBundleHostListener = new CapturingBundleHostListener();
|
||||
bundleHost.addListener(capturingBundleHostListener);
|
||||
|
||||
pushNewBundle();
|
||||
}
|
||||
@@ -84,29 +86,29 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
bundleHost.dispose();
|
||||
bhl = null;
|
||||
capturingBundleHostListener = null;
|
||||
bundleHost = null;
|
||||
|
||||
for (Path tmpdir : tmpdirs) {
|
||||
for (Path tmpdir : tempDirs) {
|
||||
wipe(tmpdir);
|
||||
}
|
||||
}
|
||||
|
||||
protected void buildWithExpectations(String expectedCompilerOutput, String expectedSummary)
|
||||
throws Exception {
|
||||
StringWriter sw = new StringWriter();
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
|
||||
current_gb.build(new PrintWriter(sw));
|
||||
sw.flush();
|
||||
currentBundle.build(new PrintWriter(stringWriter));
|
||||
stringWriter.flush();
|
||||
|
||||
assertEquals("unexpected output during build", expectedCompilerOutput,
|
||||
sw.getBuffer().toString());
|
||||
stringWriter.getBuffer().toString());
|
||||
|
||||
assertEquals("wrong summary", expectedSummary, bhl.lastBuildSummary);
|
||||
assertEquals("wrong summary", expectedSummary, capturingBundleHostListener.lastBuildSummary);
|
||||
}
|
||||
|
||||
protected void activate() throws Exception {
|
||||
Bundle bundle = bundleHost.install(current_gb);
|
||||
Bundle bundle = bundleHost.install(currentBundle);
|
||||
assertNotNull("failed to install bundle", bundle);
|
||||
bundle.start();
|
||||
}
|
||||
@@ -117,7 +119,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
}
|
||||
|
||||
protected Class<?> loadClass(String classname) throws ClassNotFoundException {
|
||||
Class<?> clazz = current_gb.getOSGiBundle().loadClass(classname);
|
||||
Class<?> clazz = currentBundle.getOSGiBundle().loadClass(classname);
|
||||
assertNotNull("failed to load class", clazz);
|
||||
return clazz;
|
||||
}
|
||||
@@ -133,19 +135,19 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
protected void addClass(String meta, String imports, String fullclassname, String body)
|
||||
throws IOException {
|
||||
String simplename;
|
||||
Path tmpsource = current_gb.getPath().getFile(false).toPath();
|
||||
Path tmpsource = currentBundle.getFile().getFile(false).toPath();
|
||||
|
||||
if (fullclassname.contains(".")) {
|
||||
String packagename;
|
||||
|
||||
Pattern classpat = Pattern.compile("^(.*)\\.([^.]*)$");
|
||||
Matcher m = classpat.matcher(fullclassname);
|
||||
if (!m.matches()) {
|
||||
Pattern pattern = Pattern.compile("^(.*)\\.([^.]*)$");
|
||||
Matcher matcher = pattern.matcher(fullclassname);
|
||||
if (!matcher.matches()) {
|
||||
throw new IllegalArgumentException(
|
||||
"fullclassname must be of the form \"xxxx.xxxx.Xxxx\"");
|
||||
}
|
||||
packagename = m.group(1);
|
||||
simplename = m.group(2);
|
||||
packagename = matcher.group(1);
|
||||
simplename = matcher.group(2);
|
||||
|
||||
for (String n : packagename.split("\\.")) {
|
||||
tmpsource = tmpsource.resolve(n);
|
||||
@@ -169,9 +171,9 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
protected Object getInstance(String classname) throws Exception {
|
||||
Class<?> clazz = loadClass(classname);
|
||||
Object o = clazz.getDeclaredConstructor().newInstance();
|
||||
assertNotNull("failed to create instance", o);
|
||||
return o;
|
||||
Object object = clazz.getDeclaredConstructor().newInstance();
|
||||
assertNotNull("failed to create instance", object);
|
||||
return object;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
@@ -221,9 +223,9 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
"BClass.java:7: error: ';' expected\n" +
|
||||
" failing java goes here\n" +
|
||||
" ^\n" +
|
||||
String.format("skipping %s/apackage/BClass.java\n", current_gb.getPath().toString())
|
||||
String.format("skipping %s/apackage/BClass.java\n", currentBundle.getFile().toString())
|
||||
,
|
||||
"1 failing source files"
|
||||
"1 source file with errors"
|
||||
);
|
||||
// @formatter:on
|
||||
|
||||
@@ -290,9 +292,9 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
" ^\n" +
|
||||
" symbol: variable Library\n" +
|
||||
" location: class apackage.AClass\n" +
|
||||
String.format("skipping %s/apackage/AClass.java\n", current_gb.getPath().toString())
|
||||
String.format("skipping %s/apackage/AClass.java\n", currentBundle.getFile().toString())
|
||||
,
|
||||
"1 failing source files"
|
||||
"1 source file with errors"
|
||||
);
|
||||
// @formatter:on
|
||||
}
|
||||
@@ -313,7 +315,7 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
|
||||
|
||||
pushNewBundle();
|
||||
// @importpackages tag is only parsed from classes in default package
|
||||
// @importpackage tag is only parsed from classes in default package
|
||||
addClass(
|
||||
"//@importpackage lib\n"
|
||||
,
|
||||
@@ -353,10 +355,10 @@ public class BundleHostTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
"}\n"
|
||||
);
|
||||
|
||||
Path p = current_gb.getPath().getFile(false).toPath();
|
||||
p=p.resolve("META-INF");
|
||||
Files.createDirectories(p);
|
||||
Path manifest=p.resolve("MANIFEST.MF");
|
||||
Path path = currentBundle.getFile().getFile(false).toPath();
|
||||
path=path.resolve("META-INF");
|
||||
Files.createDirectories(path);
|
||||
Path manifest=path.resolve("MANIFEST.MF");
|
||||
|
||||
Files.writeString(manifest,
|
||||
"Manifest-Version: 1.0\n" +
|
||||
|
||||
+3
-3
@@ -216,7 +216,7 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
||||
tempScriptDir.mkdir();
|
||||
|
||||
ResourceFile scriptDir = new ResourceFile(tempScriptDir);
|
||||
provider.getBundleHost().enablePath(scriptDir);
|
||||
provider.getBundleHost().enable(scriptDir);
|
||||
|
||||
try {
|
||||
// create a script file in that directory
|
||||
@@ -257,8 +257,8 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
||||
|
||||
chooseJavaProvider();
|
||||
|
||||
SaveDialog sd = AbstractDockingTest.waitForDialogComponent(SaveDialog.class);
|
||||
pressButtonByText(sd, "OK");
|
||||
SaveDialog saveDialog = AbstractDockingTest.waitForDialogComponent(SaveDialog.class);
|
||||
pressButtonByText(saveDialog, "OK");
|
||||
|
||||
refreshProvider();
|
||||
|
||||
|
||||
+9
-9
@@ -148,11 +148,11 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
||||
public void testRefreshFindsNewScript() throws Exception {
|
||||
int rowCount = getRowCount();
|
||||
|
||||
JavaScriptProvider jsp = new JavaScriptProvider();
|
||||
JavaScriptProvider javaScriptProvider = new JavaScriptProvider();
|
||||
|
||||
ResourceFile newScript = GhidraScriptUtil.createNewScript(jsp,
|
||||
ResourceFile newScript = GhidraScriptUtil.createNewScript(javaScriptProvider,
|
||||
new ResourceFile(GhidraScriptUtil.USER_SCRIPTS_DIR), provider.getScriptDirectories());
|
||||
jsp.createNewScript(newScript, null);
|
||||
javaScriptProvider.createNewScript(newScript, null);
|
||||
|
||||
refreshScriptManager();
|
||||
|
||||
@@ -283,16 +283,16 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
||||
final ResourceFile dir = new ResourceFile(getTestDirectoryPath() + "/test_scripts");
|
||||
dir.getFile(false).mkdirs();
|
||||
|
||||
provider.getBundleHost().enablePath(dir);
|
||||
provider.getBundleHost().enable(dir);
|
||||
waitForSwing();
|
||||
|
||||
pressNewButton();
|
||||
|
||||
chooseJavaProvider();
|
||||
|
||||
SaveDialog sd = waitForDialogComponent(SaveDialog.class);
|
||||
SaveDialog saveDialog = waitForDialogComponent(SaveDialog.class);
|
||||
|
||||
final ListPanel listPanel = (ListPanel) findComponentByName(sd.getComponent(), "PATH_LIST");
|
||||
final ListPanel listPanel = (ListPanel) findComponentByName(saveDialog.getComponent(), "PATH_LIST");
|
||||
assertNotNull(listPanel);
|
||||
assertTrue(listPanel.isVisible());
|
||||
assertEquals(2, listPanel.getListModel().getSize());
|
||||
@@ -315,11 +315,11 @@ public class GhidraScriptMgrPlugin3Test extends AbstractGhidraScriptMgrPluginTes
|
||||
}, false);
|
||||
waitForSwing();
|
||||
|
||||
pressButtonByText(sd, "OK");
|
||||
assertTrue(!sd.isShowing());
|
||||
pressButtonByText(saveDialog, "OK");
|
||||
assertTrue(!saveDialog.isShowing());
|
||||
waitForTasks();
|
||||
|
||||
ResourceFile newScript = sd.getFile();
|
||||
ResourceFile newScript = saveDialog.getFile();
|
||||
assertTrue(newScript.exists());
|
||||
|
||||
assertNotNull(newScript);
|
||||
|
||||
@@ -137,16 +137,16 @@ public class ShowConstantUse extends GhidraScript {
|
||||
tableDialog.setMessage("Finished!");
|
||||
}
|
||||
|
||||
private Function getReferencedFunction(Address faddr) {
|
||||
Function f = currentProgram.getFunctionManager().getFunctionAt(faddr);
|
||||
private Function getReferencedFunction(Address functionAddress) {
|
||||
Function f = currentProgram.getFunctionManager().getFunctionAt(functionAddress);
|
||||
// couldn't find the function, see if there is an external ref there.
|
||||
if (f == null) {
|
||||
Reference[] referencesFrom =
|
||||
currentProgram.getReferenceManager().getReferencesFrom(faddr);
|
||||
for (Reference element : referencesFrom) {
|
||||
if (element.isExternalReference()) {
|
||||
faddr = element.getToAddress();
|
||||
f = currentProgram.getFunctionManager().getFunctionAt(faddr);
|
||||
currentProgram.getReferenceManager().getReferencesFrom(functionAddress);
|
||||
for (Reference reference : referencesFrom) {
|
||||
if (reference.isExternalReference()) {
|
||||
functionAddress = reference.getToAddress();
|
||||
f = currentProgram.getFunctionManager().getFunctionAt(functionAddress);
|
||||
if (f != null) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ import ghidra.framework.OperatingSystem;
|
||||
public class Path implements Comparable<Path> {
|
||||
public static final String GHIDRA_HOME = "$GHIDRA_HOME";
|
||||
public static final String USER_HOME = "$USER_HOME";
|
||||
private ResourceFile path;
|
||||
private ResourceFile file;
|
||||
private boolean isEnabled;
|
||||
private boolean isEditable;
|
||||
private boolean isReadOnly;
|
||||
@@ -39,10 +39,10 @@ public class Path implements Comparable<Path> {
|
||||
* <li>isEditable = true</li>
|
||||
* <li>isReadOnly = false</li>
|
||||
* </ul>
|
||||
* @param path absolute directory path
|
||||
* @param file absolute directory path
|
||||
*/
|
||||
public Path(File path) {
|
||||
this(new ResourceFile(path), true, true, false);
|
||||
public Path(File file) {
|
||||
this(new ResourceFile(file), true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,21 +52,21 @@ public class Path implements Comparable<Path> {
|
||||
* <li>isEditable = true</li>
|
||||
* <li>isReadOnly = false</li>
|
||||
* </ul>
|
||||
* @param path absolute directory path
|
||||
* @param file absolute directory path
|
||||
*/
|
||||
public Path(ResourceFile path) {
|
||||
this(path, true, true, false);
|
||||
public Path(ResourceFile file) {
|
||||
this(file, true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies an absolute directory path with the specified attributes.
|
||||
* @param path absolute directory path
|
||||
* @param file absolute directory path
|
||||
* @param isEnabled directory path will be searched if true
|
||||
* @param isEditable if true files contained within directory are considered editable
|
||||
* @param isReadOnly if true files contained within directory are considered read-only
|
||||
*/
|
||||
public Path(ResourceFile path, boolean isEnabled, boolean isEditable, boolean isReadOnly) {
|
||||
this.path = path;
|
||||
public Path(ResourceFile file, boolean isEnabled, boolean isEditable, boolean isReadOnly) {
|
||||
this.file = file;
|
||||
this.isEnabled = isEnabled;
|
||||
this.isEditable = isEditable;
|
||||
this.isReadOnly = isReadOnly;
|
||||
@@ -106,7 +106,7 @@ public class Path implements Comparable<Path> {
|
||||
* @param isReadOnly if true files contained within directory are considered read-only
|
||||
*/
|
||||
public Path(String path, boolean isEnabled, boolean isEditable, boolean isReadOnly) {
|
||||
this.path = fromPathString(path);
|
||||
this.file = fromPathString(path);
|
||||
|
||||
this.isEnabled = isEnabled;
|
||||
this.isEditable = isEditable;
|
||||
@@ -132,12 +132,12 @@ public class Path implements Comparable<Path> {
|
||||
return false;
|
||||
}
|
||||
Path that = (Path) obj;
|
||||
return this.path.equals(that.path);
|
||||
return this.file.equals(that.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return path.hashCode();
|
||||
return file.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +171,7 @@ public class Path implements Comparable<Path> {
|
||||
}
|
||||
|
||||
public ResourceFile getPath() {
|
||||
return path;
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,36 +182,36 @@ public class Path implements Comparable<Path> {
|
||||
* @return the path as a ResourceFile.
|
||||
*/
|
||||
public static ResourceFile fromPathString(String path) {
|
||||
ResourceFile rf = null;
|
||||
ResourceFile resourceFile = null;
|
||||
if (path.startsWith(GHIDRA_HOME)) {
|
||||
rf = resolveGhidraHome(path);
|
||||
resourceFile = resolveGhidraHome(path);
|
||||
}
|
||||
else if (path.startsWith(USER_HOME)) {
|
||||
String userHome = System.getProperty("user.home");
|
||||
int length = USER_HOME.length();
|
||||
String relativePath = path.substring(length);
|
||||
rf = new ResourceFile(new File(userHome + relativePath));
|
||||
resourceFile = new ResourceFile(new File(userHome + relativePath));
|
||||
}
|
||||
else {
|
||||
rf = new ResourceFile(path);
|
||||
resourceFile = new ResourceFile(path);
|
||||
}
|
||||
if (OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS) {
|
||||
rf = rf.getCanonicalFile();
|
||||
resourceFile = resourceFile.getCanonicalFile();
|
||||
}
|
||||
|
||||
return rf;
|
||||
return resourceFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path as a string <b>with path element placeholders</b>, such as
|
||||
* {@link #GHIDRA_HOME}.
|
||||
* @param path the path
|
||||
*
|
||||
* @param file the file to translate
|
||||
* @return the path as a string .
|
||||
*/
|
||||
static public String toPathString(ResourceFile path) {
|
||||
static public String toPathString(ResourceFile file) {
|
||||
String userHome = System.getProperty("user.home");
|
||||
String absolutePath = path.getAbsolutePath();
|
||||
String absolutePath = file.getAbsolutePath();
|
||||
for (ResourceFile appRoot : Application.getApplicationRootDirectories()) {
|
||||
String ghidraHome = appRoot.getAbsolutePath();
|
||||
if (absolutePath.startsWith(ghidraHome)) {
|
||||
@@ -239,7 +239,7 @@ public class Path implements Comparable<Path> {
|
||||
* @return the path as a string .
|
||||
*/
|
||||
public String getPathAsString() {
|
||||
return toPathString(path);
|
||||
return toPathString(file);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,24 +253,24 @@ public class Path implements Comparable<Path> {
|
||||
|
||||
public void setPath(String path) {
|
||||
if (isEditable) {
|
||||
this.path = new ResourceFile(path);
|
||||
this.file = new ResourceFile(path);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Path is not editable - " + path);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPath(ResourceFile path) {
|
||||
public void setPath(ResourceFile file) {
|
||||
if (isEditable) {
|
||||
this.path = path;
|
||||
this.file = file;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Path is not editable - " + path);
|
||||
throw new IllegalStateException("Path is not editable - " + file);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return path.exists();
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-5
@@ -111,15 +111,15 @@ public class GhidraScriptMgrPluginScreenShots extends GhidraScreenShotGenerator
|
||||
|
||||
@Test
|
||||
public void testScript_Dirs() throws Exception {
|
||||
List<ResourceFile> paths = new ArrayList<>();
|
||||
paths.add(Path.fromPathString("$USER_HOME/ghidra_scripts"));
|
||||
paths.add(Path.fromPathString("$GHIDRA_HOME/Features/Base/ghidra_scripts"));
|
||||
paths.add(Path.fromPathString("/User/defined/invalid/directory"));
|
||||
List<ResourceFile> bundleFiles = new ArrayList<>();
|
||||
bundleFiles.add(Path.fromPathString("$USER_HOME/ghidra_scripts"));
|
||||
bundleFiles.add(Path.fromPathString("$GHIDRA_HOME/Features/Base/ghidra_scripts"));
|
||||
bundleFiles.add(Path.fromPathString("/User/defined/invalid/directory"));
|
||||
|
||||
BundleStatusComponentProvider bundleStatusComponentProvider =
|
||||
showProvider(BundleStatusComponentProvider.class);
|
||||
|
||||
bundleStatusComponentProvider.setPathsForTesting(paths);
|
||||
bundleStatusComponentProvider.setBundleFilesForTesting(bundleFiles);
|
||||
|
||||
waitForComponentProvider(BundleStatusComponentProvider.class);
|
||||
captureComponent(bundleStatusComponentProvider.getComponent());
|
||||
|
||||
Reference in New Issue
Block a user