mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 07:57:42 +08:00
GP-2509 GP-2644 Improved Ghidra URL support. Added support for Ghidra
URL linked files and folders within project.
This commit is contained in:
+3
-1
@@ -27,6 +27,7 @@ import ghidra.framework.data.ProjectFileManager;
|
|||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.options.SaveState;
|
import ghidra.framework.options.SaveState;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
|
import ghidra.framework.store.LockException;
|
||||||
import ghidra.trace.database.DBTraceContentHandler;
|
import ghidra.trace.database.DBTraceContentHandler;
|
||||||
import ghidra.trace.model.Lifespan;
|
import ghidra.trace.model.Lifespan;
|
||||||
import ghidra.trace.model.Trace;
|
import ghidra.trace.model.Trace;
|
||||||
@@ -643,6 +644,7 @@ public class DebuggerCoordinates {
|
|||||||
ProjectData projData = tool.getProject().getProjectData(projLoc);
|
ProjectData projData = tool.getProject().getProjectData(projLoc);
|
||||||
if (projData == null) {
|
if (projData == null) {
|
||||||
try {
|
try {
|
||||||
|
// FIXME! orphaned instance - transient in nature
|
||||||
projData = new ProjectFileManager(projLoc, false, false);
|
projData = new ProjectFileManager(projLoc, false, false);
|
||||||
}
|
}
|
||||||
catch (NotOwnerException e) {
|
catch (NotOwnerException e) {
|
||||||
@@ -650,7 +652,7 @@ public class DebuggerCoordinates {
|
|||||||
"Not project owner: " + projLoc + "(" + pathname + ")");
|
"Not project owner: " + projLoc + "(" + pathname + ")");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException | LockException e) {
|
||||||
Msg.showError(DebuggerCoordinates.class, tool.getToolFrame(), "Trace Open Failed",
|
Msg.showError(DebuggerCoordinates.class, tool.getToolFrame(), "Trace Open Failed",
|
||||||
"Project error: " + e.getMessage());
|
"Project error: " + e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+12
-1
@@ -368,7 +368,18 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
|||||||
if (traceChooserDialog != null) {
|
if (traceChooserDialog != null) {
|
||||||
return traceChooserDialog;
|
return traceChooserDialog;
|
||||||
}
|
}
|
||||||
DomainFileFilter filter = df -> Trace.class.isAssignableFrom(df.getDomainObjectClass());
|
DomainFileFilter filter = new DomainFileFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(DomainFile df) {
|
||||||
|
return Trace.class.isAssignableFrom(df.getDomainObjectClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean followLinkedFolders() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// TODO regarding the hack note below, I believe this issue ahs been fixed, but not sure how to test
|
// TODO regarding the hack note below, I believe this issue ahs been fixed, but not sure how to test
|
||||||
return traceChooserDialog =
|
return traceChooserDialog =
|
||||||
|
|||||||
+1
-1
@@ -176,7 +176,7 @@ public class ProjectExperimentsTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
|
|
||||||
assertNotNull(proj2 = pm.openProject(loc2, false, false));
|
assertNotNull(proj2 = pm.openProject(loc2, false, false));
|
||||||
|
|
||||||
ProjectData data1 = proj2.addProjectView(loc1.getURL());
|
ProjectData data1 = proj2.addProjectView(loc1.getURL(), true);
|
||||||
assertNotNull(data1);
|
assertNotNull(data1);
|
||||||
|
|
||||||
// It's a cryin' shame. I don't get *any* callbacks. _ANY!_
|
// It's a cryin' shame. I don't get *any* callbacks. _ANY!_
|
||||||
|
|||||||
+23
-9
@@ -18,11 +18,13 @@ package ghidra.trace.database;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
import db.DBHandle;
|
import db.DBHandle;
|
||||||
import db.buffers.BufferFile;
|
import db.buffers.BufferFile;
|
||||||
import db.buffers.ManagedBufferFile;
|
import db.buffers.ManagedBufferFile;
|
||||||
import ghidra.framework.data.*;
|
import ghidra.framework.data.DBWithUserDataContentHandler;
|
||||||
|
import ghidra.framework.data.DomainObjectMergeManager;
|
||||||
import ghidra.framework.model.ChangeSet;
|
import ghidra.framework.model.ChangeSet;
|
||||||
import ghidra.framework.model.DomainObject;
|
import ghidra.framework.model.DomainObject;
|
||||||
import ghidra.framework.store.*;
|
import ghidra.framework.store.*;
|
||||||
@@ -34,9 +36,16 @@ import ghidra.util.exception.CancelledException;
|
|||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class DBTraceContentHandler extends DBContentHandler {
|
public class DBTraceContentHandler extends DBWithUserDataContentHandler<DBTrace> {
|
||||||
public static final String TRACE_CONTENT_TYPE = "Trace";
|
public static final String TRACE_CONTENT_TYPE = "Trace";
|
||||||
|
|
||||||
|
public static ImageIcon TRACE_ICON = Trace.TRACE_ICON;
|
||||||
|
|
||||||
|
static final Class<DBTrace> TRACE_DOMAIN_OBJECT_CLASS = DBTrace.class;
|
||||||
|
static final String TRACE_CONTENT_DEFAULT_TOOL = "Debugger";
|
||||||
|
|
||||||
|
private static final DBTraceLinkContentHandler linkHandler = new DBTraceLinkContentHandler();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long createFile(FileSystem fs, FileSystem userfs, String path, String name,
|
public long createFile(FileSystem fs, FileSystem userfs, String path, String name,
|
||||||
DomainObject obj, TaskMonitor monitor)
|
DomainObject obj, TaskMonitor monitor)
|
||||||
@@ -48,7 +57,7 @@ public class DBTraceContentHandler extends DBContentHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainObjectAdapter getImmutableObject(FolderItem item, Object consumer, int version,
|
public DBTrace getImmutableObject(FolderItem item, Object consumer, int version,
|
||||||
int minChangeVersion, TaskMonitor monitor)
|
int minChangeVersion, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException, VersionException {
|
throws IOException, CancelledException, VersionException {
|
||||||
String contentType = item.getContentType();
|
String contentType = item.getContentType();
|
||||||
@@ -96,7 +105,7 @@ public class DBTraceContentHandler extends DBContentHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainObjectAdapter getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade,
|
public DBTrace getReadOnlyObject(FolderItem item, int version, boolean okToUpgrade,
|
||||||
Object consumer, TaskMonitor monitor)
|
Object consumer, TaskMonitor monitor)
|
||||||
throws IOException, VersionException, CancelledException {
|
throws IOException, VersionException, CancelledException {
|
||||||
String contentType = item.getContentType();
|
String contentType = item.getContentType();
|
||||||
@@ -146,7 +155,7 @@ public class DBTraceContentHandler extends DBContentHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DomainObjectAdapter getDomainObject(FolderItem item, FileSystem userfs, long checkoutId,
|
public DBTrace getDomainObject(FolderItem item, FileSystem userfs, long checkoutId,
|
||||||
boolean okToUpgrade, boolean recover, Object consumer, TaskMonitor monitor)
|
boolean okToUpgrade, boolean recover, Object consumer, TaskMonitor monitor)
|
||||||
throws IOException, CancelledException, VersionException {
|
throws IOException, CancelledException, VersionException {
|
||||||
String contentType = item.getContentType();
|
String contentType = item.getContentType();
|
||||||
@@ -296,8 +305,8 @@ public class DBTraceContentHandler extends DBContentHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends DomainObject> getDomainObjectClass() {
|
public Class<DBTrace> getDomainObjectClass() {
|
||||||
return DBTrace.class;
|
return TRACE_DOMAIN_OBJECT_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -312,12 +321,12 @@ public class DBTraceContentHandler extends DBContentHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getDefaultToolName() {
|
public String getDefaultToolName() {
|
||||||
return "Debugger";
|
return TRACE_CONTENT_DEFAULT_TOOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return Trace.TRACE_ICON;
|
return TRACE_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -331,4 +340,9 @@ public class DBTraceContentHandler extends DBContentHandler {
|
|||||||
// TODO:
|
// TODO:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DBTraceLinkContentHandler getLinkHandler() {
|
||||||
|
return linkHandler;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+71
@@ -0,0 +1,71 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.trace.database;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
import ghidra.framework.data.LinkHandler;
|
||||||
|
import ghidra.framework.data.URLLinkObject;
|
||||||
|
import ghidra.framework.model.DomainObject;
|
||||||
|
import ghidra.framework.store.FileSystem;
|
||||||
|
import ghidra.util.InvalidNameException;
|
||||||
|
import ghidra.util.exception.CancelledException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
public class DBTraceLinkContentHandler extends LinkHandler<DBTrace> {
|
||||||
|
|
||||||
|
public static final String TRACE_LINK_CONTENT_TYPE = "TraceLink";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long createFile(FileSystem fs, FileSystem userfs, String path, String name,
|
||||||
|
DomainObject obj, TaskMonitor monitor)
|
||||||
|
throws IOException, InvalidNameException, CancelledException {
|
||||||
|
if (!(obj instanceof URLLinkObject)) {
|
||||||
|
throw new IOException("Unsupported domain object: " + obj.getClass().getName());
|
||||||
|
}
|
||||||
|
return createFile((URLLinkObject) obj, TRACE_LINK_CONTENT_TYPE, fs, path, name,
|
||||||
|
monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return TRACE_LINK_CONTENT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentTypeDisplayString() {
|
||||||
|
return TRACE_LINK_CONTENT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<DBTrace> getDomainObjectClass() {
|
||||||
|
// return linked content class
|
||||||
|
return DBTraceContentHandler.TRACE_DOMAIN_OBJECT_CLASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Icon getIcon() {
|
||||||
|
return DBTraceContentHandler.TRACE_ICON;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDefaultToolName() {
|
||||||
|
return DBTraceContentHandler.TRACE_CONTENT_DEFAULT_TOOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -351,6 +351,7 @@ src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png||GHIDRA||||END
|
|||||||
src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessPanel.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessPanel.png||GHIDRA||||END|
|
||||||
|
src/main/help/help/topics/FrontEndPlugin/images/LinkOtherProject.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/MemoryUsage.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/MemoryUsage.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png||GHIDRA||||END|
|
||||||
src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png||GHIDRA||||END|
|
src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png||GHIDRA||||END|
|
||||||
@@ -1089,7 +1090,6 @@ src/main/resources/images/layout_add.png||FAMFAMFAM Icons - CC 2.5|||famfamfam s
|
|||||||
src/main/resources/images/ledgreen.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
src/main/resources/images/ledgreen.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||||
src/main/resources/images/ledred.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
src/main/resources/images/ledred.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||||
src/main/resources/images/ledyellow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
src/main/resources/images/ledyellow.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
||||||
src/main/resources/images/link.png||Nuvola Icons - LGPL 2.1|||Nuvola icon set|END|
|
|
||||||
src/main/resources/images/lock.gif||GHIDRA||||END|
|
src/main/resources/images/lock.gif||GHIDRA||||END|
|
||||||
src/main/resources/images/magnifier.png||FAMFAMFAM Icons - CC 2.5||||END|
|
src/main/resources/images/magnifier.png||FAMFAMFAM Icons - CC 2.5||||END|
|
||||||
src/main/resources/images/media-flash.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
src/main/resources/images/media-flash.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
// NOTE: Script will only process unversioned and checked-out files.
|
// NOTE: Script will only process unversioned and checked-out files.
|
||||||
//@category Examples
|
//@category Examples
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.app.script.GhidraState;
|
import ghidra.app.script.GhidraState;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
@@ -25,8 +27,6 @@ import ghidra.program.model.listing.Program;
|
|||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class CallAnotherScriptForAllPrograms extends GhidraScript {
|
public class CallAnotherScriptForAllPrograms extends GhidraScript {
|
||||||
|
|
||||||
// The script referenced in the following line should be replaced with the script to be called
|
// The script referenced in the following line should be replaced with the script to be called
|
||||||
@@ -59,6 +59,10 @@ public class CallAnotherScriptForAllPrograms extends GhidraScript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processDomainFile(DomainFile domainFile) throws CancelledException, IOException {
|
private void processDomainFile(DomainFile domainFile) throws CancelledException, IOException {
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domainFile.getContentType())) {
|
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domainFile.getContentType())) {
|
||||||
return; // skip non-Program files
|
return; // skip non-Program files
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ public class RepositoryFileUpgradeScript extends GhidraScript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean performProgramUpgrade(DomainFile df) throws IOException, CancelledException {
|
private boolean performProgramUpgrade(DomainFile df) throws IOException, CancelledException {
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this.
|
||||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(df.getContentType())) {
|
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(df.getContentType())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,10 @@ public class VersionControl_AddAll extends GhidraScript {
|
|||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used. It may also be appropriate to handle other content types.
|
||||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(file.getContentType()) ||
|
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(file.getContentType()) ||
|
||||||
file.isVersioned()) {
|
file.isVersioned()) {
|
||||||
continue;// skip
|
continue;// skip
|
||||||
|
|||||||
@@ -22,13 +22,15 @@
|
|||||||
hyperlink.</P>
|
hyperlink.</P>
|
||||||
<!-- Annotation Example -->
|
<!-- Annotation Example -->
|
||||||
|
|
||||||
<P>The following text shows the syntax of the URL annotation:</P>
|
<P>The following text shows the syntax of a sample URL annotation:</P>
|
||||||
<pre><font size="4"><br>
|
<pre><font size="4"><br>
|
||||||
<b>{@<i>url</i></b> "http://www.google.com"<b>}</b><br>
|
<b>{@<i>url</i></b> "<i>http://www.google.com</i>"</b> "Search Web"<b>}</b><br>
|
||||||
</font><br></pre>
|
</font><br></pre>
|
||||||
|
|
||||||
<P>The bold text is required for all annotations. The italicized text is required but is
|
<P>The bold text is required for all annotations. The italicized text is required but is
|
||||||
specific to the annotation being used (see the table below).</P>
|
specific to the annotation being used (see the table below). The optional rendered display text
|
||||||
|
"Search Web" will be displayed in listing. If the optional display test is omitted, the URL
|
||||||
|
will be displayed. Quotes around display text are optional.</P>
|
||||||
|
|
||||||
<H2>Examples</H2>
|
<H2>Examples</H2>
|
||||||
|
|
||||||
@@ -47,10 +49,21 @@
|
|||||||
<I>Rendered URL Annotation Example</I></P>
|
<I>Rendered URL Annotation Example</I></P>
|
||||||
|
|
||||||
<P>When the URL text (e.g., "http://www.google.com") in the above image is clicked from within
|
<P>When the URL text (e.g., "http://www.google.com") in the above image is clicked from within
|
||||||
Ghidra, a web browser is launched and attempts to load the corresponding web page. If the URL text
|
Ghidra, a web browser is launched and attempts to load the corresponding web page. </P>
|
||||||
is of the form <i>ghidra://<host>[:<port>]/<repository-name>/<program-path>[#<address-or-symbol-ref>]</i>
|
|
||||||
an attempt will be made to open the corresponding program from the referenced Ghidra Server (e.g.,
|
<P>If the URL text corresponds to a Ghidra URL and attempt will be made to open the referenced
|
||||||
<i>ghidra://myserver/Repo/notepad.exe#entry</i>).</P>
|
Program file within the Code Browser. Such a URL may refer to a Program file from a
|
||||||
|
local project or Ghidra Server. The Ghidra URL forms supported include:</P>
|
||||||
|
|
||||||
|
<P><U>Remote Ghidra Server file</U><BR>
|
||||||
|
<i>ghidra://<host>[:<port>]/<repository-name>/<program-path>[#<address-or-symbol-ref>]</i><BR>
|
||||||
|
Example: <i>ghidra://hostname/Repo/notepad.exe#entry</i>
|
||||||
|
</P>
|
||||||
|
|
||||||
|
<P><U>Local Ghidra project file</U><BR>
|
||||||
|
<i>ghidra:/[<project-path>/]<project-name>?/<program-path>[#<address-or-symbol-ref>]</i><BR>
|
||||||
|
Example: <i>ghidra:/share/MyProject?/notepad.exe#entry</i>
|
||||||
|
</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2>Valid Annotations</H2>
|
<H2>Valid Annotations</H2>
|
||||||
@@ -174,9 +187,10 @@
|
|||||||
<TD valign="top" width="15%">Displays the given URL has a hyperlink. This annotation
|
<TD valign="top" width="15%">Displays the given URL has a hyperlink. This annotation
|
||||||
optionally takes display text so that the hyperlink may be displayed with text other
|
optionally takes display text so that the hyperlink may be displayed with text other
|
||||||
than that of the URL.<BR><BR>
|
than that of the URL.<BR><BR>
|
||||||
References to <i>ghidra://</i>, which refer to a program within a Ghidra Server repository,
|
References to a program file on a Ghidra Server (<i>ghidra://<host...</i>) or
|
||||||
will be opened within the Listing display, while all other URL protocols (e.g., <i>http://, https://,
|
local project (<i>ghidra:/<project-...</i>) will be opened within the Listing display,
|
||||||
file://,</i> etc.) will be launched via an external web browser (see
|
while all other URL forms (e.g., <i>http://, https://, file://,</i> etc.) will be
|
||||||
|
launched via an external web browser (see
|
||||||
<a href="help/topics/ShowInstructionInfoPlugin/ShowInstructionInfo.htm#Show_Processor_Manual">command configuration
|
<a href="help/topics/ShowInstructionInfoPlugin/ShowInstructionInfo.htm#Show_Processor_Manual">command configuration
|
||||||
for Processor Manuals</a>).
|
for Processor Manuals</a>).
|
||||||
</TD>
|
</TD>
|
||||||
@@ -216,6 +230,12 @@
|
|||||||
<LI>{@url "ghidra://myserver/Repo/notepad.exe#entry"}</LI>
|
<LI>{@url "ghidra://myserver/Repo/notepad.exe#entry"}</LI>
|
||||||
|
|
||||||
<LI>{@url "ghidra://myserver/Repo/notepad.exe" "see notepad.exe"}</LI>
|
<LI>{@url "ghidra://myserver/Repo/notepad.exe" "see notepad.exe"}</LI>
|
||||||
|
|
||||||
|
<LI>{@url "ghidra:/share/MyProject?/notepad.exe#entry"}</LI>
|
||||||
|
|
||||||
|
<LI>{@url "ghidra:/share/MyProject?/notepad.exe" "see notepad.exe"}</LI>
|
||||||
|
|
||||||
|
|
||||||
</UL>
|
</UL>
|
||||||
</TD>
|
</TD>
|
||||||
</TR>
|
</TR>
|
||||||
@@ -224,8 +244,9 @@
|
|||||||
<TD valign="top" width="5%">Program<BR>
|
<TD valign="top" width="5%">Program<BR>
|
||||||
</TD>
|
</TD>
|
||||||
|
|
||||||
<TD valign="top" width="15%">Displays a hyperlink to the given Ghidra program name that
|
<TD valign="top" width="15%">Displays a hyperlink to the given Ghidra program pathname
|
||||||
will open that program in a new Listing tab when clicked.<BR>
|
with the current project. Referenced program
|
||||||
|
will open in a new Listing tab when clicked.<BR>
|
||||||
You may optionally provide an address or symbol to be displayed when the program is
|
You may optionally provide an address or symbol to be displayed when the program is
|
||||||
opened by appending to the program name an '@' character, followed by an address or
|
opened by appending to the program name an '@' character, followed by an address or
|
||||||
symbol name.</TD>
|
symbol name.</TD>
|
||||||
|
|||||||
@@ -688,6 +688,54 @@
|
|||||||
|
|
||||||
<P>The tabbed pane for read-only Project data is removed from the Project Window.</P>
|
<P>The tabbed pane for read-only Project data is removed from the Project Window.</P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<H3><A name="Create_File_Links"></A>Create Linked Folder or File</H3>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>This feature allows you to create a folder or file link in your active project to a
|
||||||
|
corresponding folder or file within a read-only viewed project.
|
||||||
|
This is done using a Ghidra URL which references the
|
||||||
|
file in its local or remote storage location. If the viewed project corresponds to a
|
||||||
|
viewed repository a remote URL will be used, while other cases will refer to the
|
||||||
|
locally viewed project. It is possible for links to become broken if the referenced
|
||||||
|
repository, local project or file location are changed.</P>
|
||||||
|
<ol>
|
||||||
|
<li>Select a single folder or file from a viewed READ-ONLY Project Data tree.</li>
|
||||||
|
<li>Right mouse click on the selected tree node and choose the <I>Copy</I> option.</li>
|
||||||
|
<li>Select a destination folder in the active project tree.</li>
|
||||||
|
<li>Right mouse click on the folder and choose the <I>Paste as Link</I> option.
|
||||||
|
<P><IMG src="../../shared/note.png" border="0">Currently, linked-file types are
|
||||||
|
currently limited to <I>Program</I> and <I>Data Type Archive</I> files
|
||||||
|
only. The <I>Past as Link</I> menu item will be disabled for
|
||||||
|
unsupported file content types or for other unsupported situations such as internal
|
||||||
|
linking within the same project.</P>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<P>A linked-file may be opened in a tool via the project window in the same fashion that
|
||||||
|
a normal file is opened (e.g., double-left-mouse-click or drag-n-drop onto a tool box icon).
|
||||||
|
Such a project file may also be opened within a Tool using its <B>File->Open...</B> action
|
||||||
|
and selected from the resulting project file selection dilaog.
|
||||||
|
Clicking on a linked-folder in the active project window will open that location in a
|
||||||
|
<B>READ-ONLY Project Data</B> tree. The user may be prompted for a shared repository
|
||||||
|
connection password when accessing a linked folder or file.</P>
|
||||||
|
<P>Within a project file chooser dialog a linked-folder may be expanded in a similar fashion
|
||||||
|
to local folders provided any neccessary repository connection can be completed.</P>
|
||||||
|
<P><IMG src="../../shared/note.png" border="0"><B>Add to Version Control...</B> is supported
|
||||||
|
for repository folder and file links only and will be disabled for links to a
|
||||||
|
local project.</P>
|
||||||
|
<P><IMG src="../../shared/note.png" border="0">Currently, linked-files only provide access
|
||||||
|
to the latest file version and do not facilitate access to older file versions.</P>
|
||||||
|
<P>The project window below shows a Program file-link "Program1" which is linked to the
|
||||||
|
same file in the viewed project. Hovering the mouse over a linked-file will show the URL
|
||||||
|
of the linked file or folder. The chain-link icon decoration indicates such a linked
|
||||||
|
file or folder.</P>
|
||||||
|
|
||||||
|
<CENTER>
|
||||||
|
<IMG src= "images/LinkOtherProject.png" border="0">
|
||||||
|
</CENTER>
|
||||||
|
|
||||||
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H2><A name="Workspace"></A>Workspaces</H2>
|
<H2><A name="Workspace"></A>Workspaces</H2>
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 36 KiB |
+7
-2
@@ -1266,19 +1266,24 @@ public class CallTreeProvider extends ComponentProviderAdapter implements Domain
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void expandNode(GTreeNode node, TaskMonitor monitor) throws CancelledException {
|
protected void expandNode(GTreeNode node, boolean force, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
TreePath treePath = node.getTreePath();
|
TreePath treePath = node.getTreePath();
|
||||||
Object[] path = treePath.getPath();
|
Object[] path = treePath.getPath();
|
||||||
if (path.length > maxDepth) {
|
if (path.length > maxDepth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!force && !node.isAutoExpandPermitted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CallNode callNode = (CallNode) node;
|
CallNode callNode = (CallNode) node;
|
||||||
if (callNode.functionIsInPath()) {
|
if (callNode.functionIsInPath()) {
|
||||||
return; // this path hit a function that is already in the path
|
return; // this path hit a function that is already in the path
|
||||||
}
|
}
|
||||||
|
|
||||||
super.expandNode(node, monitor);
|
super.expandNode(node, false, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-1
@@ -106,9 +106,33 @@ public class ClearFlowAndRepairCmd extends BackgroundCommand {
|
|||||||
CodeUnit cu = cuIter.next();
|
CodeUnit cu = cuIter.next();
|
||||||
if (cu instanceof Instruction) {
|
if (cu instanceof Instruction) {
|
||||||
Instruction instr = (Instruction) cu;
|
Instruction instr = (Instruction) cu;
|
||||||
|
|
||||||
|
// check for function on delay slot
|
||||||
|
if (listing.getFunctionAt(instr.getMinAddress()) != null) {
|
||||||
|
continue; // skip since it will be picked-up by flow if appropriate
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for fallthrough to instruction
|
||||||
Address ffAddr = instr.getFallFrom();
|
Address ffAddr = instr.getFallFrom();
|
||||||
if (ffAddr != null && startAddrs.contains(ffAddr)) {
|
if (ffAddr != null && startAddrs.contains(ffAddr)) {
|
||||||
continue; // skip since it will be picked-up by flow
|
continue; // skip since it will be picked-up by flow if appropriate
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for flow into delay slot
|
||||||
|
if (instr.isInDelaySlot()) {
|
||||||
|
boolean skip = false;
|
||||||
|
ReferenceIterator refToIter = instr.getReferenceIteratorTo();
|
||||||
|
while (refToIter.hasNext()) {
|
||||||
|
Reference ref = refToIter.next();
|
||||||
|
RefType refType = ref.getReferenceType();
|
||||||
|
if (refType.isJump() || refType.isCall()) {
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip) {
|
||||||
|
continue; // skip since it will be picked-up by flow if appropriate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+5
-4
@@ -240,10 +240,11 @@ public class CommentsDialog extends DialogComponentProvider implements KeyListen
|
|||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private AnnotationAdapterWrapper[] getAnnotationAdapterWrappers() {
|
private AnnotationAdapterWrapper[] getAnnotationAdapterWrappers() {
|
||||||
AnnotatedStringHandler[] annotations = Annotation.getAnnotatedStringHandlers();
|
List<AnnotatedStringHandler> annotations = Annotation.getAnnotatedStringHandlers();
|
||||||
AnnotationAdapterWrapper[] retVal = new AnnotationAdapterWrapper[annotations.length];
|
int count = annotations.size();
|
||||||
for (int i = 0; i < annotations.length; i++) {
|
AnnotationAdapterWrapper[] retVal = new AnnotationAdapterWrapper[count];
|
||||||
retVal[i] = new AnnotationAdapterWrapper(annotations[i]);
|
for (int i = 0; i < count; i++) {
|
||||||
|
retVal[i] = new AnnotationAdapterWrapper(annotations.get(i));
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-8
@@ -56,7 +56,6 @@ import ghidra.framework.options.SaveState;
|
|||||||
import ghidra.framework.plugintool.PluginInfo;
|
import ghidra.framework.plugintool.PluginInfo;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.program.database.DataTypeArchiveContentHandler;
|
|
||||||
import ghidra.program.database.data.ProgramDataTypeManager;
|
import ghidra.program.database.data.ProgramDataTypeManager;
|
||||||
import ghidra.program.model.address.AddressSetView;
|
import ghidra.program.model.address.AddressSetView;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
@@ -245,8 +244,7 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
|||||||
Project project = tool.getProjectManager().getActiveProject();
|
Project project = tool.getProjectManager().getActiveProject();
|
||||||
if (project != null && project.getName().equals(projectName)) {
|
if (project != null && project.getName().equals(projectName)) {
|
||||||
DomainFile df = project.getProjectData().getFile(pathname);
|
DomainFile df = project.getProjectData().getFile(pathname);
|
||||||
if (df != null && DataTypeArchiveContentHandler.DATA_TYPE_ARCHIVE_CONTENT_TYPE
|
if (DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass())) {
|
||||||
.equals(df.getContentType())) {
|
|
||||||
return df;
|
return df;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -588,12 +586,9 @@ public class DataTypeManagerPlugin extends ProgramPlugin
|
|||||||
openArchive(domainFile, version);
|
openArchive(domainFile, version);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
DomainFileFilter filter = f -> {
|
|
||||||
Class<?> c = f.getDomainObjectClass();
|
|
||||||
return DataTypeArchive.class.isAssignableFrom(c);
|
|
||||||
};
|
|
||||||
openDialog =
|
openDialog =
|
||||||
new OpenVersionedFileDialog(tool, "Open Project Data Type Archive", filter);
|
new OpenVersionedFileDialog(tool, "Open Project Data Type Archive",
|
||||||
|
df -> DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass()));
|
||||||
openDialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog"));
|
openDialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog"));
|
||||||
openDialog.addOkActionListener(listener);
|
openDialog.addOkActionListener(listener);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-2
@@ -608,8 +608,15 @@ public class DataTypesProvider extends ComponentProviderAdapter {
|
|||||||
ArchiveNode archiveNode = dataTypeNode.getArchiveNode();
|
ArchiveNode archiveNode = dataTypeNode.getArchiveNode();
|
||||||
|
|
||||||
if (archiveNode instanceof ProjectArchiveNode && !archiveNode.isModifiable()) {
|
if (archiveNode instanceof ProjectArchiveNode && !archiveNode.isModifiable()) {
|
||||||
Msg.showInfo(getClass(), archiveGTree, "Archive Not Checked Out",
|
ProjectArchiveNode projectArchive = (ProjectArchiveNode) archiveNode;
|
||||||
"You must checkout this archive before you may edit data types.");
|
if (projectArchive.getDomainFile().isReadOnly()) {
|
||||||
|
Msg.showInfo(getClass(), archiveGTree, "Read-Only Archive",
|
||||||
|
"You may not edit data type within a read-only project archive.");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Msg.showInfo(getClass(), archiveGTree, "Archive Not Checked Out",
|
||||||
|
"You must checkout this archive before you may edit data types.");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-14
@@ -38,6 +38,7 @@ import ghidra.app.plugin.core.datamgr.tree.ArchiveNode;
|
|||||||
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
import ghidra.app.plugin.core.datamgr.tree.DataTypeNode;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask;
|
||||||
import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask.ActionType;
|
import ghidra.app.plugin.core.datamgr.util.DataTypeTreeCopyMoveTask.ActionType;
|
||||||
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
@@ -85,26 +86,21 @@ public class AssociateDataTypeAction extends DockingAction {
|
|||||||
return !nodes.isEmpty();
|
return !nodes.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasSingleModifiableSourceArchive(List<GTreeNode> nodes) {
|
private Archive getSingleDTArchive(List<GTreeNode> nodes) {
|
||||||
|
|
||||||
Archive sourceArchive = null;
|
Archive dtArchive = null;
|
||||||
for (GTreeNode node : nodes) {
|
for (GTreeNode node : nodes) {
|
||||||
Archive archive = findArchive(node);
|
Archive archive = findArchive(node);
|
||||||
if (sourceArchive == null) {
|
if (dtArchive == null) {
|
||||||
sourceArchive = archive;
|
dtArchive = archive;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sourceArchive != archive) {
|
if (dtArchive != archive) {
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return dtArchive;
|
||||||
if (sourceArchive != null && sourceArchive.isModifiable()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Archive findArchive(GTreeNode node) {
|
private static Archive findArchive(GTreeNode node) {
|
||||||
@@ -134,13 +130,20 @@ public class AssociateDataTypeAction extends DockingAction {
|
|||||||
|
|
||||||
List<GTreeNode> nodes = ((DataTypesActionContext) context).getSelectedNodes();
|
List<GTreeNode> nodes = ((DataTypesActionContext) context).getSelectedNodes();
|
||||||
|
|
||||||
if (!hasSingleModifiableSourceArchive(nodes)) {
|
Archive dtArchive = getSingleDTArchive(nodes);
|
||||||
Msg.showInfo(this, getProviderComponent(), "Multiple Source Archives",
|
if (dtArchive == null) {
|
||||||
|
Msg.showInfo(this, getProviderComponent(), "Multiple Data Type Archives",
|
||||||
"The currently selected nodes are from multiple archives.\n" +
|
"The currently selected nodes are from multiple archives.\n" +
|
||||||
"Please select only nodes from a single archvie.");
|
"Please select only nodes from a single archvie.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!dtArchive.isModifiable()) {
|
||||||
|
DataTypeUtils.showUnmodifiableArchiveErrorMessage(context.getSourceComponent(),
|
||||||
|
"Disassociate Failed", dtArchive.getDataTypeManager());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAlreadyAssociated((DataTypesActionContext) context)) {
|
if (isAlreadyAssociated((DataTypesActionContext) context)) {
|
||||||
Msg.showInfo(this, getProviderComponent(), "Already Associated",
|
Msg.showInfo(this, getProviderComponent(), "Already Associated",
|
||||||
"One or more of the currently selected nodes are already associated\n" +
|
"One or more of the currently selected nodes are already associated\n" +
|
||||||
|
|||||||
+16
-8
@@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@@ -76,7 +76,7 @@ public class DataTypeManagerHandler {
|
|||||||
private DataTreeDialog dataTreeSaveDialog;
|
private DataTreeDialog dataTreeSaveDialog;
|
||||||
private CreateDataTypeArchiveDataTreeDialog dataTreeCreateDialog;
|
private CreateDataTypeArchiveDataTreeDialog dataTreeCreateDialog;
|
||||||
private boolean treeDialogCancelled = false;
|
private boolean treeDialogCancelled = false;
|
||||||
private DomainFileFilter domainFileFilter;
|
private DomainFileFilter createArchiveFileFilter;
|
||||||
|
|
||||||
private DataTypeIndexer dataTypeIndexer;
|
private DataTypeIndexer dataTypeIndexer;
|
||||||
private List<ArchiveManagerListener> archiveManagerlisteners = new ArrayList<>();
|
private List<ArchiveManagerListener> archiveManagerlisteners = new ArrayList<>();
|
||||||
@@ -102,9 +102,17 @@ public class DataTypeManagerHandler {
|
|||||||
dataTypeIndexer.addDataTypeManager(builtInDataTypesManager);
|
dataTypeIndexer.addDataTypeManager(builtInDataTypesManager);
|
||||||
openArchives.add(new BuiltInArchive(this, builtInDataTypesManager));
|
openArchives.add(new BuiltInArchive(this, builtInDataTypesManager));
|
||||||
|
|
||||||
domainFileFilter = f -> {
|
createArchiveFileFilter = new DomainFileFilter() {
|
||||||
Class<?> c = f.getDomainObjectClass();
|
|
||||||
return DataTypeArchive.class.isAssignableFrom(c);
|
@Override
|
||||||
|
public boolean accept(DomainFile df) {
|
||||||
|
return DataTypeArchive.class.isAssignableFrom(df.getDomainObjectClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean followLinkedFolders() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
folderListener = new MyFolderListener();
|
folderListener = new MyFolderListener();
|
||||||
@@ -1425,7 +1433,7 @@ public class DataTypeManagerHandler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
dataTreeSaveDialog =
|
dataTreeSaveDialog =
|
||||||
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, domainFileFilter);
|
new DataTreeDialog(null, "Save As", DataTreeDialog.SAVE, createArchiveFileFilter);
|
||||||
|
|
||||||
dataTreeSaveDialog.addOkActionListener(listener);
|
dataTreeSaveDialog.addOkActionListener(listener);
|
||||||
dataTreeSaveDialog
|
dataTreeSaveDialog
|
||||||
@@ -1465,7 +1473,7 @@ public class DataTypeManagerHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
dataTreeCreateDialog = new CreateDataTypeArchiveDataTreeDialog(null, "Create",
|
dataTreeCreateDialog = new CreateDataTypeArchiveDataTreeDialog(null, "Create",
|
||||||
DataTreeDialog.CREATE, domainFileFilter);
|
DataTreeDialog.CREATE, createArchiveFileFilter);
|
||||||
|
|
||||||
dataTreeCreateDialog.addOkActionListener(listener);
|
dataTreeCreateDialog.addOkActionListener(listener);
|
||||||
dataTreeCreateDialog.setHelpLocation(
|
dataTreeCreateDialog.setHelpLocation(
|
||||||
|
|||||||
+12
-2
@@ -24,6 +24,7 @@ import javax.swing.Icon;
|
|||||||
import generic.theme.GColor;
|
import generic.theme.GColor;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
import ghidra.app.services.DataTypeQueryService;
|
import ghidra.app.services.DataTypeQueryService;
|
||||||
|
import ghidra.program.database.data.ProjectDataTypeManager;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
@@ -438,9 +439,18 @@ public class DataTypeUtils {
|
|||||||
msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" +
|
msg = "The archive file is not modifiable!\nYou must open the archive for editing\n" +
|
||||||
"before performing this operation.\n" + dtm.getName();
|
"before performing this operation.\n" + dtm.getName();
|
||||||
}
|
}
|
||||||
|
else if (dtm instanceof ProjectDataTypeManager) {
|
||||||
|
ProjectDataTypeManager projectDtm = (ProjectDataTypeManager) dtm;
|
||||||
|
if (!projectDtm.isUpdatable() && !projectDtm.getDomainFile().canCheckout()) {
|
||||||
|
msg = "The project archive is not modifiable!\n" + dtm.getName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg = "The project archive is not modifiable!\nYou must check out the archive\n" +
|
||||||
|
"before performing this operation.\n" + dtm.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
msg = "The project archive is not modifiable!\nYou must check out the archive\n" +
|
msg = "The Archive is not modifiable!\n";
|
||||||
"before performing this operation.\n" + dtm.getName();
|
|
||||||
}
|
}
|
||||||
Msg.showInfo(DataTypeUtils.class, parent, title, msg);
|
Msg.showInfo(DataTypeUtils.class, parent, title, msg);
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-5
@@ -295,11 +295,15 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
|||||||
return comboBox;
|
return comboBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private List<Exporter> getApplicableExporters() {
|
private List<Exporter> getApplicableExporters() {
|
||||||
List<Exporter> list = new ArrayList<>(ClassSearcher.getInstances(Exporter.class));
|
List<Exporter> list = new ArrayList<>(ClassSearcher.getInstances(Exporter.class));
|
||||||
Class<? extends DomainObject> domainObjectClass = domainFile.getDomainObjectClass();
|
Class<?> domainObjectClass = domainFile.getDomainObjectClass();
|
||||||
list.removeIf(exporter -> !exporter.canExportDomainObject(domainObjectClass));
|
if (DomainObject.class.isAssignableFrom(domainObjectClass)) {
|
||||||
Collections.sort(list, (o1, o2) -> o1.toString().compareTo(o2.toString()));
|
list.removeIf(exporter -> !exporter
|
||||||
|
.canExportDomainObject((Class<? extends DomainObject>) domainObjectClass));
|
||||||
|
Collections.sort(list, (o1, o2) -> o1.toString().compareTo(o2.toString()));
|
||||||
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,8 +426,16 @@ public class ExporterDialog extends DialogComponentProvider implements AddressFa
|
|||||||
|
|
||||||
private void doOpenFile(TaskMonitor monitor) {
|
private void doOpenFile(TaskMonitor monitor) {
|
||||||
try {
|
try {
|
||||||
domainObject = domainFile.getImmutableDomainObject(this, DomainFile.DEFAULT_VERSION,
|
if (domainFile.isLinkFile()) {
|
||||||
TaskMonitor.DUMMY);
|
// Linked files are always subject to upgrade if needed and do not support
|
||||||
|
// getImmutableDomainObject
|
||||||
|
domainObject =
|
||||||
|
domainFile.getReadOnlyDomainObject(this, DomainFile.DEFAULT_VERSION, monitor);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
domainObject =
|
||||||
|
domainFile.getImmutableDomainObject(this, DomainFile.DEFAULT_VERSION, monitor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (VersionException | CancelledException | IOException e) {
|
catch (VersionException | CancelledException | IOException e) {
|
||||||
Msg.showError(this, getComponent(), "Error Opening File",
|
Msg.showError(this, getComponent(), "Error Opening File",
|
||||||
|
|||||||
+2
-1
@@ -147,7 +147,8 @@ public class MyProgramChangesDisplayPlugin extends ProgramPlugin implements Doma
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabledForContext(ActionContext context) {
|
public boolean isEnabledForContext(ActionContext context) {
|
||||||
return currentProgram != null && currentProgram.getDomainFile().canCheckin();
|
return currentProgram != null &&
|
||||||
|
currentProgram.getDomainFile().modifiedSinceCheckout();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+14
-12
@@ -26,8 +26,8 @@ import docking.action.MenuData;
|
|||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import ghidra.app.CorePluginPackage;
|
import ghidra.app.CorePluginPackage;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
import ghidra.framework.main.FrontEndTool;
|
|
||||||
import ghidra.framework.main.ApplicationLevelPlugin;
|
import ghidra.framework.main.ApplicationLevelPlugin;
|
||||||
|
import ghidra.framework.main.FrontEndTool;
|
||||||
import ghidra.framework.main.datatable.ProjectDataContext;
|
import ghidra.framework.main.datatable.ProjectDataContext;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
@@ -316,18 +316,20 @@ public final class LanguageProviderPlugin extends Plugin implements ApplicationL
|
|||||||
try {
|
try {
|
||||||
SwingUtilities.invokeAndWait(() -> {
|
SwingUtilities.invokeAndWait(() -> {
|
||||||
ToolServices toolServices = tool.getToolServices();
|
ToolServices toolServices = tool.getToolServices();
|
||||||
String defaultToolName = toolServices.getDefaultToolTemplate(file).getName();
|
ToolTemplate defaultToolTemplate = toolServices.getDefaultToolTemplate(file);
|
||||||
for (PluginTool t : toolServices.getRunningTools()) {
|
if (defaultToolTemplate != null) {
|
||||||
if (t.getName().equals(defaultToolName)) {
|
String defaultToolName = defaultToolTemplate.getName();
|
||||||
openTool = t;
|
for (PluginTool t : toolServices.getRunningTools()) {
|
||||||
break;
|
if (t.getName().equals(defaultToolName)) {
|
||||||
|
openTool = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (openTool != null) {
|
if (openTool == null ||
|
||||||
openTool.acceptDomainFiles(new DomainFile[] { file });
|
!openTool.acceptDomainFiles(new DomainFile[] { file })) {
|
||||||
}
|
Msg.showError(this, tool.getToolFrame(), "Failed to Open Program",
|
||||||
else {
|
"A suitable default tool could not found!");
|
||||||
openTool = tool.getToolServices().launchDefaultTool(file);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -336,7 +338,7 @@ public final class LanguageProviderPlugin extends Plugin implements ApplicationL
|
|||||||
}
|
}
|
||||||
catch (InvocationTargetException e) {
|
catch (InvocationTargetException e) {
|
||||||
Throwable t = e.getCause();
|
Throwable t = e.getCause();
|
||||||
Msg.showError(this, tool.getToolFrame(), "Tool Launch Failed",
|
Msg.showError(this, tool.getToolFrame(), "Failed to Open Program",
|
||||||
"An error occurred while attempting to launch your default tool!", t);
|
"An error occurred while attempting to launch your default tool!", t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-39
@@ -30,6 +30,7 @@ import ghidra.app.events.*;
|
|||||||
import ghidra.app.nav.Navigatable;
|
import ghidra.app.nav.Navigatable;
|
||||||
import ghidra.app.services.*;
|
import ghidra.app.services.*;
|
||||||
import ghidra.app.util.task.OpenProgramTask;
|
import ghidra.app.util.task.OpenProgramTask;
|
||||||
|
import ghidra.app.util.task.OpenProgramTask.OpenProgramRequest;
|
||||||
import ghidra.framework.data.DomainObjectAdapterDB;
|
import ghidra.framework.data.DomainObjectAdapterDB;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
@@ -42,9 +43,6 @@ import ghidra.util.task.TaskLauncher;
|
|||||||
|
|
||||||
class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
||||||
|
|
||||||
// arbitrary counter for given ProgramInfo objects and ID to use for sorting
|
|
||||||
private static final AtomicInteger nextAvailableId = new AtomicInteger();
|
|
||||||
|
|
||||||
private ProgramManagerPlugin plugin;
|
private ProgramManagerPlugin plugin;
|
||||||
private PluginTool tool;
|
private PluginTool tool;
|
||||||
private ProgramInfo currentInfo;
|
private ProgramInfo currentInfo;
|
||||||
@@ -82,25 +80,31 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void addProgram(Program p, URL ghidraURL, int state) {
|
void addProgram(Program p, DomainFile domainFile, int state) {
|
||||||
|
addProgram(new ProgramInfo(p, domainFile, state != ProgramManager.OPEN_HIDDEN), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addProgram(Program p, URL ghidraUrl, int state) {
|
||||||
|
addProgram(new ProgramInfo(p, ghidraUrl, state != ProgramManager.OPEN_HIDDEN), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addProgram(ProgramInfo programInfo, int state) {
|
||||||
|
Program p = programInfo.program;
|
||||||
ProgramInfo oldInfo = getInfo(p);
|
ProgramInfo oldInfo = getInfo(p);
|
||||||
if (oldInfo == null) {
|
if (oldInfo == null) {
|
||||||
|
oldInfo = programInfo;
|
||||||
p.addConsumer(tool);
|
p.addConsumer(tool);
|
||||||
ProgramInfo info = new ProgramInfo(p, state != ProgramManager.OPEN_HIDDEN);
|
openPrograms.add(oldInfo);
|
||||||
info.ghidraURL = ghidraURL;
|
|
||||||
openPrograms.add(info);
|
|
||||||
openPrograms.sort(Comparator.naturalOrder());
|
openPrograms.sort(Comparator.naturalOrder());
|
||||||
programMap.put(p, info);
|
programMap.put(p, oldInfo);
|
||||||
|
|
||||||
fireOpenEvents(p);
|
fireOpenEvents(p);
|
||||||
|
|
||||||
p.addListener(this);
|
p.addListener(this);
|
||||||
p.addTransactionListener(this);
|
p.addTransactionListener(this);
|
||||||
}
|
}
|
||||||
else {
|
else if (!oldInfo.visible && state != ProgramManager.OPEN_HIDDEN) {
|
||||||
if (!oldInfo.visible && state != ProgramManager.OPEN_HIDDEN) {
|
oldInfo.setVisible(true);
|
||||||
oldInfo.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (state == ProgramManager.OPEN_CURRENT) {
|
if (state == ProgramManager.OPEN_CURRENT) {
|
||||||
saveLocation();
|
saveLocation();
|
||||||
@@ -246,12 +250,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
TransientToolState toolState = null;
|
TransientToolState toolState = null;
|
||||||
if (currentInfo != null) {
|
if (currentInfo != null) {
|
||||||
currentInfo.setVisible(true);
|
currentInfo.setVisible(true);
|
||||||
DomainFile df = currentInfo.program.getDomainFile();
|
tool.setSubTitle(currentInfo.toString());
|
||||||
String title = df.toString();
|
|
||||||
if (df.isReadOnly()) {
|
|
||||||
title = title + " [Read-Only]";
|
|
||||||
}
|
|
||||||
tool.setSubTitle(title);
|
|
||||||
txMonitor.setProgram(currentInfo.program);
|
txMonitor.setProgram(currentInfo.program);
|
||||||
if (currentInfo.lastState != null) {
|
if (currentInfo.lastState != null) {
|
||||||
toolState = currentInfo.lastState;
|
toolState = currentInfo.lastState;
|
||||||
@@ -370,7 +369,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
return (info != null && info.owner != null);
|
return (info != null && info.owner != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProgramInfo getInfo(Program p) {
|
ProgramInfo getInfo(Program p) {
|
||||||
if (p == null) {
|
if (p == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -378,9 +377,6 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Program getOpenProgram(URL ghidraURL) {
|
Program getOpenProgram(URL ghidraURL) {
|
||||||
if (!GhidraURL.isServerRepositoryURL(ghidraURL)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
URL normalizedURL = GhidraURL.getNormalizedURL(ghidraURL);
|
URL normalizedURL = GhidraURL.getNormalizedURL(ghidraURL);
|
||||||
for (ProgramInfo info : programMap.values()) {
|
for (ProgramInfo info : programMap.values()) {
|
||||||
URL url = info.ghidraURL;
|
URL url = info.ghidraURL;
|
||||||
@@ -392,10 +388,10 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Program getOpenProgram(DomainFile domainFile, int version) {
|
Program getOpenProgram(DomainFile domainFile, int version) {
|
||||||
for (Program program : programMap.keySet()) {
|
for (ProgramInfo info : programMap.values()) {
|
||||||
DomainFile programDomainFile = program.getDomainFile();
|
DomainFile df = info.domainFile;
|
||||||
if (filesMatch(domainFile, version, programDomainFile)) {
|
if (df != null && filesMatch(domainFile, version, df)) {
|
||||||
return program;
|
return info.program;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -413,7 +409,7 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
if (!SystemUtilities.isEqual(file1.getProjectLocator(), file2.getProjectLocator())) {
|
if (!SystemUtilities.isEqual(file1.getProjectLocator(), file2.getProjectLocator())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// TODO: version check is questionable - unclear how proxy file would work
|
||||||
int openVersion = file2.isReadOnly() ? file2.getVersion() : -1;
|
int openVersion = file2.isReadOnly() ? file2.getVersion() : -1;
|
||||||
return version == openVersion;
|
return version == openVersion;
|
||||||
}
|
}
|
||||||
@@ -488,28 +484,54 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
OpenProgramTask openTask = new OpenProgramTask(file, -1, this);
|
OpenProgramTask openTask = new OpenProgramTask(file, -1, this);
|
||||||
openTask.setSilent();
|
openTask.setSilent();
|
||||||
new TaskLauncher(openTask, tool.getToolFrame());
|
new TaskLauncher(openTask, tool.getToolFrame());
|
||||||
Program openProgram = openTask.getOpenProgram();
|
OpenProgramRequest openProgramReq = openTask.getOpenProgram();
|
||||||
plugin.openProgram(openProgram,
|
if (openProgramReq != null) {
|
||||||
dataState != null ? ProgramManager.OPEN_CURRENT : ProgramManager.OPEN_VISIBLE);
|
plugin.openProgram(openProgramReq.getProgram(),
|
||||||
openProgram.release(this);
|
dataState != null ? ProgramManager.OPEN_CURRENT : ProgramManager.OPEN_VISIBLE);
|
||||||
removeProgram((Program) oldObject);
|
openProgramReq.release();
|
||||||
if (dataState != null) {
|
removeProgram((Program) oldObject);
|
||||||
tool.restoreDataStateFromXml(dataState);
|
if (dataState != null) {
|
||||||
|
tool.restoreDataStateFromXml(dataState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProgramInfo implements Comparable<ProgramInfo> {
|
class ProgramInfo implements Comparable<ProgramInfo> {
|
||||||
|
|
||||||
|
// arbitrary counter for given ProgramInfo objects and ID to use for sorting
|
||||||
|
private static final AtomicInteger nextAvailableId = new AtomicInteger();
|
||||||
|
|
||||||
|
public final Program program;
|
||||||
|
|
||||||
|
// NOTE: domainFile and ghidraURL use are mutually exclusive and reflect how program was
|
||||||
|
// opened. Supported cases include:
|
||||||
|
// 1. Opened via Program file
|
||||||
|
// 2. Opened via ProgramLink file
|
||||||
|
// 3. Opened via Program URL
|
||||||
|
|
||||||
|
public final DomainFile domainFile; // may be link file
|
||||||
|
public final URL ghidraURL;
|
||||||
|
|
||||||
private Program program;
|
|
||||||
private URL ghidraURL;
|
|
||||||
private TransientToolState lastState;
|
private TransientToolState lastState;
|
||||||
private int instance;
|
private int instance;
|
||||||
private boolean visible;
|
private boolean visible = false;
|
||||||
private Object owner;
|
private Object owner;
|
||||||
|
|
||||||
ProgramInfo(Program p, boolean visible) {
|
private String str; // cached toString
|
||||||
|
|
||||||
|
ProgramInfo(Program p, DomainFile domainFile, boolean visible) {
|
||||||
this.program = p;
|
this.program = p;
|
||||||
|
this.domainFile = domainFile;
|
||||||
|
this.ghidraURL = null;
|
||||||
|
this.visible = visible;
|
||||||
|
instance = nextAvailableId.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramInfo(Program p, URL ghidraURL, boolean visible) {
|
||||||
|
this.program = p;
|
||||||
|
this.domainFile = null;
|
||||||
|
this.ghidraURL = ghidraURL;
|
||||||
this.visible = visible;
|
this.visible = visible;
|
||||||
instance = nextAvailableId.incrementAndGet();
|
instance = nextAvailableId.incrementAndGet();
|
||||||
}
|
}
|
||||||
@@ -523,5 +545,24 @@ class MultiProgramManager implements DomainObjectListener, TransactionListener {
|
|||||||
public int compareTo(ProgramInfo info) {
|
public int compareTo(ProgramInfo info) {
|
||||||
return instance - info.instance;
|
return instance - info.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (str != null) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
StringBuilder buf = new StringBuilder();
|
||||||
|
DomainFile df = program.getDomainFile();
|
||||||
|
if (domainFile != null && domainFile.isLinkFile()) {
|
||||||
|
buf.append(domainFile.getName());
|
||||||
|
buf.append("->");
|
||||||
|
}
|
||||||
|
buf.append(df.toString());
|
||||||
|
if (df.isReadOnly()) {
|
||||||
|
buf.append(" [Read-Only]");
|
||||||
|
}
|
||||||
|
str = buf.toString();
|
||||||
|
return str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+162
-146
@@ -18,7 +18,6 @@ package ghidra.app.plugin.core.progmgr;
|
|||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.beans.PropertyEditor;
|
import java.beans.PropertyEditor;
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -32,26 +31,26 @@ import ghidra.app.CorePluginPackage;
|
|||||||
import ghidra.app.context.ProgramActionContext;
|
import ghidra.app.context.ProgramActionContext;
|
||||||
import ghidra.app.events.*;
|
import ghidra.app.events.*;
|
||||||
import ghidra.app.plugin.PluginCategoryNames;
|
import ghidra.app.plugin.PluginCategoryNames;
|
||||||
|
import ghidra.app.plugin.core.progmgr.MultiProgramManager.ProgramInfo;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
import ghidra.app.util.HelpTopics;
|
import ghidra.app.util.HelpTopics;
|
||||||
import ghidra.app.util.NamespaceUtils;
|
import ghidra.app.util.NamespaceUtils;
|
||||||
import ghidra.app.util.task.OpenProgramTask;
|
import ghidra.app.util.task.OpenProgramTask;
|
||||||
|
import ghidra.app.util.task.OpenProgramTask.OpenProgramRequest;
|
||||||
import ghidra.framework.client.ClientUtil;
|
import ghidra.framework.client.ClientUtil;
|
||||||
import ghidra.framework.data.ProjectFileManager;
|
|
||||||
import ghidra.framework.main.OpenVersionedFileDialog;
|
import ghidra.framework.main.OpenVersionedFileDialog;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
import ghidra.framework.options.*;
|
import ghidra.framework.options.*;
|
||||||
import ghidra.framework.plugintool.*;
|
import ghidra.framework.plugintool.*;
|
||||||
import ghidra.framework.plugintool.util.PluginStatus;
|
import ghidra.framework.plugintool.util.PluginStatus;
|
||||||
import ghidra.framework.protocol.ghidra.*;
|
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||||
import ghidra.program.database.ProgramContentHandler;
|
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.program.model.symbol.Symbol;
|
import ghidra.program.model.symbol.Symbol;
|
||||||
import ghidra.program.model.symbol.SymbolType;
|
import ghidra.program.model.symbol.SymbolType;
|
||||||
import ghidra.program.util.*;
|
import ghidra.program.util.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.NotFoundException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.TaskLauncher;
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
@@ -121,16 +120,21 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
if (domainFile == null) {
|
if (domainFile == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!(Program.class.isAssignableFrom(domainFile.getDomainObjectClass()))) {
|
Class<? extends DomainObject> domainObjectClass = domainFile.getDomainObjectClass();
|
||||||
continue;
|
if (Program.class.isAssignableFrom(domainObjectClass)) {
|
||||||
|
filesToOpen.add(domainFile);
|
||||||
}
|
}
|
||||||
filesToOpen.add(domainFile);
|
|
||||||
}
|
}
|
||||||
openPrograms(filesToOpen);
|
openPrograms(filesToOpen);
|
||||||
|
|
||||||
return !filesToOpen.isEmpty();
|
return !filesToOpen.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(URL url) {
|
||||||
|
return openProgram(url, OPEN_CURRENT) != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<?>[] getSupportedDataTypes() {
|
public Class<?>[] getSupportedDataTypes() {
|
||||||
return new Class[] { Program.class };
|
return new Class[] { Program.class };
|
||||||
@@ -144,89 +148,53 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Swing.runNow(() -> doOpenProgram(ghidraURL, state));
|
// Check for URL already open and re-use
|
||||||
}
|
URL url = GhidraURL.getNormalizedURL(ghidraURL);
|
||||||
|
Program p = programMgr.getOpenProgram(url);
|
||||||
private void messageBadProgramURL(URL ghidraURL) {
|
if (p != null) {
|
||||||
Msg.showError(this, null, "Invalid Ghidra URL",
|
showProgram(p, url, state);
|
||||||
"Ghidra URL does not reference a Ghidra Program: " + ghidraURL);
|
if (state == ProgramManager.OPEN_CURRENT) {
|
||||||
}
|
gotoProgramRef(p, ghidraURL.getRef());
|
||||||
|
|
||||||
protected Program doOpenProgram(URL ghidraURL, int openState) {
|
|
||||||
if (!GhidraURL.isServerRepositoryURL(ghidraURL)) {
|
|
||||||
Msg.showError(this, null, "Invalid Ghidra URL",
|
|
||||||
"Ghidra URL does not reference a Ghidra Program: " + ghidraURL);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Program openProgram = programMgr.getOpenProgram(ghidraURL);
|
|
||||||
if (openProgram != null) {
|
|
||||||
programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState);
|
|
||||||
if (openState == ProgramManager.OPEN_CURRENT) {
|
|
||||||
gotoProgramRef(openProgram, ghidraURL.getRef());
|
|
||||||
programMgr.saveLocation();
|
programMgr.saveLocation();
|
||||||
}
|
}
|
||||||
contextChanged();
|
return p;
|
||||||
return openProgram;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GhidraURLWrappedContent wrappedContent = null;
|
Program program = Swing.runNow(() -> doOpenProgram(ghidraURL, state));
|
||||||
Object content = null;
|
|
||||||
|
if (program != null) {
|
||||||
|
Msg.info(this, "Opened program in " + tool.getName() + " tool: " + ghidraURL);
|
||||||
|
}
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open GhidraURL which corresponds to {@code ghidra://} remote URLs which correspond to a
|
||||||
|
* repository program file.
|
||||||
|
* @param ghidraURL Ghidra URL which specified Program to be opened which optional ref
|
||||||
|
* @param openState open state
|
||||||
|
* @return program instance of null if open failed
|
||||||
|
*/
|
||||||
|
private Program doOpenProgram(URL ghidraURL, int openState) {
|
||||||
|
Program p = null;
|
||||||
try {
|
try {
|
||||||
GhidraURLConnection c = (GhidraURLConnection) ghidraURL.openConnection();
|
URL url = GhidraURL.getNormalizedURL(ghidraURL);
|
||||||
Object obj = c.getContent();
|
OpenProgramTask task = new OpenProgramTask(url, this);
|
||||||
if (c.getResponseCode() == GhidraURLConnection.GHIDRA_UNAUTHORIZED) {
|
new TaskLauncher(task, tool.getToolFrame());
|
||||||
return null; // assume user already notified
|
OpenProgramRequest openProgramReq = task.getOpenProgram();
|
||||||
|
if (openProgramReq != null) {
|
||||||
|
p = openProgramReq.getProgram();
|
||||||
|
showProgram(p, url, openState);
|
||||||
|
openProgramReq.release();
|
||||||
}
|
}
|
||||||
if (!(obj instanceof GhidraURLWrappedContent)) {
|
|
||||||
messageBadProgramURL(ghidraURL);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
wrappedContent = (GhidraURLWrappedContent) obj;
|
|
||||||
content = wrappedContent.getContent(this);
|
|
||||||
if (!(content instanceof DomainFile)) {
|
|
||||||
messageBadProgramURL(ghidraURL);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
DomainFile df = (DomainFile) content;
|
|
||||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(df.getContentType())) {
|
|
||||||
messageBadProgramURL(ghidraURL);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenProgramTask task = new OpenProgramTask(df, true, this);
|
|
||||||
TaskLauncher.launch(task);
|
|
||||||
|
|
||||||
openProgram = task.getOpenProgram();
|
|
||||||
if (openProgram == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
programMgr.addProgram(openProgram, GhidraURL.getNormalizedURL(ghidraURL), openState);
|
|
||||||
contextChanged();
|
|
||||||
openProgram.release(this);
|
|
||||||
if (openState == ProgramManager.OPEN_CURRENT) {
|
|
||||||
gotoProgramRef(openProgram, ghidraURL.getRef());
|
|
||||||
programMgr.saveLocation();
|
|
||||||
}
|
|
||||||
return openProgram;
|
|
||||||
}
|
|
||||||
catch (NotFoundException e) {
|
|
||||||
messageBadProgramURL(ghidraURL);
|
|
||||||
}
|
|
||||||
catch (MalformedURLException e) {
|
|
||||||
Msg.showError(this, null, "Invalid Ghidra URL",
|
|
||||||
"Improperly formed Ghidra URL: " + ghidraURL);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
Msg.showError(this, null, "Program Open Failed",
|
|
||||||
"Failed to open Ghidra URL: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (content != null) {
|
if (p != null && openState == ProgramManager.OPEN_CURRENT) {
|
||||||
wrappedContent.release(content, this);
|
gotoProgramRef(p, ghidraURL.getRef());
|
||||||
|
programMgr.saveLocation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean gotoProgramRef(Program program, String ref) {
|
private boolean gotoProgramRef(Program program, String ref) {
|
||||||
@@ -296,9 +264,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Program program = Swing.runNow(() -> {
|
Program program = Swing.runNow(() -> {
|
||||||
Program p = doOpenProgram(domainFile, version, state);
|
return doOpenProgram(domainFile, version, state);
|
||||||
contextChanged();
|
|
||||||
return p;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (program != null) {
|
if (program != null) {
|
||||||
@@ -460,13 +426,39 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void openProgram(final Program program, final int state) {
|
public void openProgram(final Program program, final int state) {
|
||||||
|
showProgram(program, program.getDomainFile(), state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showProgram(Program p, URL ghidraUrl, final int state) {
|
||||||
|
if (p == null || p.isClosed()) {
|
||||||
|
throw new AssertException("Opened program required");
|
||||||
|
}
|
||||||
if (locked) {
|
if (locked) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Progam manager is locked and cannot accept a new program");
|
"Progam manager is locked and cannot accept a new program");
|
||||||
}
|
}
|
||||||
|
|
||||||
Runnable r = () -> {
|
Runnable r = () -> {
|
||||||
programMgr.addProgram(program, null, state);
|
programMgr.addProgram(p, ghidraUrl, state);
|
||||||
|
if (state == ProgramManager.OPEN_CURRENT) {
|
||||||
|
programMgr.saveLocation();
|
||||||
|
}
|
||||||
|
contextChanged();
|
||||||
|
};
|
||||||
|
Swing.runNow(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showProgram(Program p, DomainFile domainFile, final int state) {
|
||||||
|
if (p == null || p.isClosed()) {
|
||||||
|
throw new AssertException("Opened program required");
|
||||||
|
}
|
||||||
|
if (locked) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Progam manager is locked and cannot accept a new program");
|
||||||
|
}
|
||||||
|
|
||||||
|
Runnable r = () -> {
|
||||||
|
programMgr.addProgram(p, domainFile, state);
|
||||||
if (state == ProgramManager.OPEN_CURRENT) {
|
if (state == ProgramManager.OPEN_CURRENT) {
|
||||||
programMgr.saveLocation();
|
programMgr.saveLocation();
|
||||||
}
|
}
|
||||||
@@ -625,11 +617,9 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
doOpenProgram(domainFile, version, OPEN_CURRENT);
|
doOpenProgram(domainFile, version, OPEN_CURRENT);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
DomainFileFilter filter = f -> {
|
openDialog = new OpenVersionedFileDialog(tool, "Open Program", f -> {
|
||||||
Class<?> c = f.getDomainObjectClass();
|
return Program.class.isAssignableFrom(f.getDomainObjectClass());
|
||||||
return Program.class.isAssignableFrom(c);
|
});
|
||||||
};
|
|
||||||
openDialog = new OpenVersionedFileDialog(tool, "Open Program", filter);
|
|
||||||
openDialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog"));
|
openDialog.setHelpLocation(new HelpLocation(HelpTopics.PROGRAM, "Open_File_Dialog"));
|
||||||
openDialog.addOkActionListener(listener);
|
openDialog.addOkActionListener(listener);
|
||||||
}
|
}
|
||||||
@@ -638,9 +628,12 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void openPrograms(List<DomainFile> filesToOpen) {
|
public void openPrograms(List<DomainFile> filesToOpen) {
|
||||||
|
Program showIfNeeded = null;
|
||||||
OpenProgramTask openTask = null;
|
OpenProgramTask openTask = null;
|
||||||
for (DomainFile domainFile : filesToOpen) {
|
for (DomainFile domainFile : filesToOpen) {
|
||||||
if (programMgr.getOpenProgram(domainFile, -1) != null) {
|
Program p = programMgr.getOpenProgram(domainFile, -1);
|
||||||
|
if (p != null) {
|
||||||
|
showIfNeeded = p;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (openTask == null) {
|
if (openTask == null) {
|
||||||
@@ -652,32 +645,37 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
}
|
}
|
||||||
if (openTask != null) {
|
if (openTask != null) {
|
||||||
new TaskLauncher(openTask, tool.getToolFrame());
|
new TaskLauncher(openTask, tool.getToolFrame());
|
||||||
List<Program> openPrograms = openTask.getOpenPrograms();
|
List<OpenProgramRequest> openProgramReqs = openTask.getOpenPrograms();
|
||||||
|
boolean isFirst = true;
|
||||||
for (Program program : openPrograms) {
|
for (OpenProgramRequest programReq : openProgramReqs) {
|
||||||
openProgram(program, OPEN_VISIBLE);
|
showProgram(programReq.getProgram(), programReq.getDomainFile(),
|
||||||
program.release(this);
|
isFirst ? OPEN_CURRENT : OPEN_VISIBLE);
|
||||||
}
|
programReq.release();
|
||||||
if (!openPrograms.isEmpty()) {
|
isFirst = false;
|
||||||
openProgram(openPrograms.get(0), OPEN_CURRENT);
|
showIfNeeded = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (showIfNeeded != null) {
|
||||||
|
showProgram(showIfNeeded, showIfNeeded.getDomainFile(), OPEN_CURRENT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Program doOpenProgram(DomainFile domainFile, int version, int openState) {
|
protected Program doOpenProgram(DomainFile domainFile, int version, int openState) {
|
||||||
Program openProgram = programMgr.getOpenProgram(domainFile, version);
|
Program p = programMgr.getOpenProgram(domainFile, version);
|
||||||
if (openProgram != null) {
|
if (p != null) {
|
||||||
openProgram(openProgram, openState);
|
openProgram(p, openState);
|
||||||
return openProgram;
|
|
||||||
}
|
}
|
||||||
OpenProgramTask task = new OpenProgramTask(domainFile, version, this);
|
else {
|
||||||
new TaskLauncher(task, tool.getToolFrame());
|
OpenProgramTask task = new OpenProgramTask(domainFile, version, this);
|
||||||
openProgram = task.getOpenProgram();
|
new TaskLauncher(task, tool.getToolFrame());
|
||||||
if (openProgram != null) {
|
OpenProgramRequest programReq = task.getOpenProgram();
|
||||||
openProgram(openProgram, openState);
|
if (programReq != null) {
|
||||||
openProgram.release(this);
|
p = programReq.getProgram();
|
||||||
|
showProgram(p, programReq.getDomainFile(), openState);
|
||||||
|
programReq.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return openProgram;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -705,18 +703,20 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeDataState(SaveState saveState) {
|
public void writeDataState(SaveState saveState) {
|
||||||
// Only remember programs from non-transient projects
|
|
||||||
ArrayList<Program> programs = new ArrayList<>();
|
ArrayList<ProgramInfo> programInfos = new ArrayList<>();
|
||||||
for (Program p : programMgr.getAllPrograms()) {
|
for (Program p : programMgr.getAllPrograms()) {
|
||||||
ProjectLocator projectLocator = p.getDomainFile().getProjectLocator();
|
ProgramInfo info = programMgr.getInfo(p);
|
||||||
if (projectLocator != null && !projectLocator.isTransient()) {
|
if (info != null) {
|
||||||
programs.add(p);
|
programInfos.add(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
saveState.putInt("NUM_PROGRAMS", programs.size());
|
|
||||||
|
saveState.putInt("NUM_PROGRAMS", programInfos.size());
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Program p : programs) {
|
for (ProgramInfo programInfo : programInfos) {
|
||||||
writeProgramInfo(p, saveState, i++);
|
writeProgramInfo(programInfo, saveState, i++);
|
||||||
}
|
}
|
||||||
Program p = programMgr.getCurrentProgram();
|
Program p = programMgr.getCurrentProgram();
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
@@ -768,13 +768,21 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeProgramInfo(Program program, SaveState saveState, int index) {
|
private void writeProgramInfo(ProgramInfo programInfo, SaveState saveState, int index) {
|
||||||
if (locked) {
|
if (locked) {
|
||||||
return; // do not save state when locked.
|
return; // do not save state when locked.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (programInfo.ghidraURL != null) {
|
||||||
|
saveState.putString("URL_" + index, programInfo.ghidraURL.toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String projectLocation = null;
|
String projectLocation = null;
|
||||||
String projectName = null;
|
String projectName = null;
|
||||||
String path = null;
|
String path = null;
|
||||||
|
|
||||||
|
Program program = programInfo.program;
|
||||||
DomainFile df = program.getDomainFile();
|
DomainFile df = program.getDomainFile();
|
||||||
ProjectLocator projectLocator = df.getProjectLocator();
|
ProjectLocator projectLocator = df.getProjectLocator();
|
||||||
if (projectLocator != null && !projectLocator.isTransient()) {
|
if (projectLocator != null && !projectLocator.isTransient()) {
|
||||||
@@ -797,28 +805,30 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
* Read in my data state.
|
* Read in my data state.
|
||||||
*/
|
*/
|
||||||
private void loadPrograms(SaveState saveState) {
|
private void loadPrograms(SaveState saveState) {
|
||||||
|
|
||||||
int n = saveState.getInt("NUM_PROGRAMS", 0);
|
int n = saveState.getInt("NUM_PROGRAMS", 0);
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
OpenProgramTask openTask = null;
|
OpenProgramTask openTask = new OpenProgramTask(this);
|
||||||
|
|
||||||
for (int index = 0; index < n; index++) {
|
for (int index = 0; index < n; index++) {
|
||||||
|
|
||||||
|
URL url = getGhidraURL(saveState, index);
|
||||||
|
if (url != null) {
|
||||||
|
openTask.addProgramToOpen(url);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
DomainFile domainFile = getDomainFile(saveState, index);
|
DomainFile domainFile = getDomainFile(saveState, index);
|
||||||
if (domainFile == null) {
|
if (domainFile == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int version = getVersion(saveState, index);
|
int version = getVersion(saveState, index);
|
||||||
|
openTask.addProgramToOpen(domainFile, version);
|
||||||
if (openTask == null) {
|
|
||||||
openTask = new OpenProgramTask(domainFile, version, this);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
openTask.addProgramToOpen(domainFile, version);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (openTask == null) {
|
if (!openTask.hasOpenProgramRequests()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -835,10 +845,29 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
"Can't open program", e);
|
"Can't open program", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Program> openPrograms = openTask.getOpenPrograms();
|
List<OpenProgramRequest> openProgramReqs = openTask.getOpenPrograms();
|
||||||
for (Program program : openPrograms) {
|
for (OpenProgramRequest programReq : openProgramReqs) {
|
||||||
openProgram(program, OPEN_VISIBLE);
|
DomainFile df = programReq.getDomainFile();
|
||||||
program.release(this);
|
if (df != null) {
|
||||||
|
showProgram(programReq.getProgram(), df, OPEN_VISIBLE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
showProgram(programReq.getProgram(), programReq.getGhidraURL(), OPEN_VISIBLE);
|
||||||
|
}
|
||||||
|
programReq.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL getGhidraURL(SaveState saveState, int index) {
|
||||||
|
String url = saveState.getString("URL_" + index, null);
|
||||||
|
if (url == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new URL(url);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -853,20 +882,7 @@ public class ProgramManagerPlugin extends Plugin implements ProgramManager {
|
|||||||
|
|
||||||
ProjectData projectData = tool.getProject().getProjectData(projectLocator);
|
ProjectData projectData = tool.getProject().getProjectData(projectLocator);
|
||||||
if (projectData == null) {
|
if (projectData == null) {
|
||||||
// Viewed project not available
|
return null;
|
||||||
try {
|
|
||||||
projectData = new ProjectFileManager(projectLocator, false, false);
|
|
||||||
}
|
|
||||||
catch (NotOwnerException e) {
|
|
||||||
Msg.showError(this, tool.getToolFrame(), "Program Open Failed",
|
|
||||||
"Not project owner: " + projectLocator + "(" + pathname + ")");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
Msg.showError(this, tool.getToolFrame(), "Program Open Failed",
|
|
||||||
"Project error: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DomainFile df = projectData.getFile(pathname);
|
DomainFile df = projectData.getFile(pathname);
|
||||||
|
|||||||
+13
-4
@@ -45,9 +45,17 @@ class ProgramSaveManager {
|
|||||||
ProgramSaveManager(PluginTool tool, ProgramManager programMgr) {
|
ProgramSaveManager(PluginTool tool, ProgramManager programMgr) {
|
||||||
this.tool = tool;
|
this.tool = tool;
|
||||||
this.programMgr = programMgr;
|
this.programMgr = programMgr;
|
||||||
domainFileFilter = f -> {
|
domainFileFilter = new DomainFileFilter() {
|
||||||
Class<?> c = f.getDomainObjectClass();
|
|
||||||
return Program.class.isAssignableFrom(c);
|
@Override
|
||||||
|
public boolean accept(DomainFile df) {
|
||||||
|
return Program.class.isAssignableFrom(df.getDomainObjectClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean followLinkedFolders() {
|
||||||
|
return false; // can't save to linked-folder (read-only)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +252,8 @@ class ProgramSaveManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (existingFile != null) {
|
if (existingFile != null) {
|
||||||
String msg = "Program " + name + " already exists.\n" + "Do you want to overwrite it?";
|
String msg = existingFile.getContentType() + " file " + name + " already exists.\n" +
|
||||||
|
"Do you want to overwrite it?";
|
||||||
if (OptionDialog.showOptionDialog(tool.getToolFrame(), "Duplicate Name", msg,
|
if (OptionDialog.showOptionDialog(tool.getToolFrame(), "Duplicate Name", msg,
|
||||||
"Overwrite", OptionDialog.QUESTION_MESSAGE) == OptionDialog.CANCEL_OPTION) {
|
"Overwrite", OptionDialog.QUESTION_MESSAGE) == OptionDialog.CANCEL_OPTION) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ public class HeadlessAnalyzer {
|
|||||||
Object obj = c.getContent();
|
Object obj = c.getContent();
|
||||||
if (!(obj instanceof GhidraURLWrappedContent)) {
|
if (!(obj instanceof GhidraURLWrappedContent)) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
"Connect to repository folder failed. Response code: " + c.getResponseCode());
|
"Connect to repository folder failed. Response code: " + c.getStatusCode());
|
||||||
}
|
}
|
||||||
GhidraURLWrappedContent wrappedContent = (GhidraURLWrappedContent) obj;
|
GhidraURLWrappedContent wrappedContent = (GhidraURLWrappedContent) obj;
|
||||||
Object content = null;
|
Object content = null;
|
||||||
@@ -336,7 +336,7 @@ public class HeadlessAnalyzer {
|
|||||||
processWithImport(folder.getPathname(), filesToImport);
|
processWithImport(folder.getPathname(), filesToImport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (NotFoundException e) {
|
catch (FileNotFoundException e) {
|
||||||
throw new IOException("Connect to repository folder failed");
|
throw new IOException("Connect to repository folder failed");
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
@@ -369,7 +369,8 @@ public class HeadlessAnalyzer {
|
|||||||
* @param rootFolderPath root folder for imports
|
* @param rootFolderPath root folder for imports
|
||||||
* @param filesToImport directories and files to be imported (null or empty is acceptable if
|
* @param filesToImport directories and files to be imported (null or empty is acceptable if
|
||||||
* we are in -process mode)
|
* we are in -process mode)
|
||||||
* @throws IOException if there was an IO-related problem
|
* @throws IOException if there was an IO-related problem. If caused by a failure to obtain a
|
||||||
|
* write-lock on the project the exception cause will a {@code LockException}.
|
||||||
*/
|
*/
|
||||||
public void processLocal(String projectLocation, String projectName, String rootFolderPath,
|
public void processLocal(String projectLocation, String projectName, String rootFolderPath,
|
||||||
List<File> filesToImport) throws IOException {
|
List<File> filesToImport) throws IOException {
|
||||||
@@ -1107,6 +1108,8 @@ public class HeadlessAnalyzer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this.
|
||||||
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
if (!ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
||||||
return; // skip non-Program files
|
return; // skip non-Program files
|
||||||
}
|
}
|
||||||
@@ -1275,6 +1278,8 @@ public class HeadlessAnalyzer {
|
|||||||
|
|
||||||
for (DomainFile domFile : parentFolder.getFiles()) {
|
for (DomainFile domFile : parentFolder.getFiles()) {
|
||||||
if (filenamePattern == null || filenamePattern.matcher(domFile.getName()).matches()) {
|
if (filenamePattern == null || filenamePattern.matcher(domFile.getName()).matches()) {
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this.
|
||||||
if (ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
if (ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
||||||
filesProcessed = true;
|
filesProcessed = true;
|
||||||
processFileNoImport(domFile);
|
processFileNoImport(domFile);
|
||||||
@@ -1308,6 +1313,8 @@ public class HeadlessAnalyzer {
|
|||||||
boolean filesProcessed = false;
|
boolean filesProcessed = false;
|
||||||
|
|
||||||
DomainFile domFile = parentFolder.getFile(filename);
|
DomainFile domFile = parentFolder.getFile(filename);
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this.
|
||||||
if (domFile != null &&
|
if (domFile != null &&
|
||||||
ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
ProgramContentHandler.PROGRAM_CONTENT_TYPE.equals(domFile.getContentType())) {
|
||||||
filesProcessed = true;
|
filesProcessed = true;
|
||||||
|
|||||||
@@ -16,8 +16,9 @@
|
|||||||
package ghidra.app.util.task;
|
package ghidra.app.util.task;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.net.MalformedURLException;
|
||||||
import java.util.List;
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import docking.widgets.OptionDialog;
|
import docking.widgets.OptionDialog;
|
||||||
import ghidra.app.util.dialog.CheckoutDialog;
|
import ghidra.app.util.dialog.CheckoutDialog;
|
||||||
@@ -25,8 +26,11 @@ import ghidra.framework.client.ClientUtil;
|
|||||||
import ghidra.framework.client.RepositoryAdapter;
|
import ghidra.framework.client.RepositoryAdapter;
|
||||||
import ghidra.framework.main.AppInfo;
|
import ghidra.framework.main.AppInfo;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.protocol.ghidra.*;
|
||||||
|
import ghidra.framework.protocol.ghidra.GhidraURLConnection.StatusCode;
|
||||||
import ghidra.framework.remote.User;
|
import ghidra.framework.remote.User;
|
||||||
import ghidra.framework.store.ExclusiveCheckoutException;
|
import ghidra.framework.store.ExclusiveCheckoutException;
|
||||||
|
import ghidra.program.database.ProgramLinkContentHandler;
|
||||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||||
import ghidra.program.model.listing.Program;
|
import ghidra.program.model.listing.Program;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
@@ -37,8 +41,8 @@ import ghidra.util.task.TaskMonitor;
|
|||||||
|
|
||||||
public class OpenProgramTask extends Task {
|
public class OpenProgramTask extends Task {
|
||||||
|
|
||||||
private final List<DomainFileInfo> domainFileInfoList = new ArrayList<>();
|
private final List<OpenProgramRequest> openProgramRequests = new ArrayList<>();
|
||||||
private List<Program> programList = new ArrayList<>();
|
private List<OpenProgramRequest> openedProgramList = new ArrayList<>();
|
||||||
|
|
||||||
private final Object consumer;
|
private final Object consumer;
|
||||||
private boolean silent; // if true operation does not permit interaction
|
private boolean silent; // if true operation does not permit interaction
|
||||||
@@ -46,11 +50,16 @@ public class OpenProgramTask extends Task {
|
|||||||
|
|
||||||
private String openPromptText = "Open";
|
private String openPromptText = "Open";
|
||||||
|
|
||||||
|
public OpenProgramTask(Object consumer) {
|
||||||
|
super("Open Program(s)", true, false, true);
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
public OpenProgramTask(DomainFile domainFile, int version, boolean forceReadOnly,
|
public OpenProgramTask(DomainFile domainFile, int version, boolean forceReadOnly,
|
||||||
Object consumer) {
|
Object consumer) {
|
||||||
super("Open Program(s)", true, false, true);
|
super("Open Program(s)", true, false, true);
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
domainFileInfoList.add(new DomainFileInfo(domainFile, version, forceReadOnly));
|
openProgramRequests.add(new OpenProgramRequest(domainFile, version, forceReadOnly));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenProgramTask(DomainFile domainFile, int version, Object consumer) {
|
public OpenProgramTask(DomainFile domainFile, int version, Object consumer) {
|
||||||
@@ -65,17 +74,10 @@ public class OpenProgramTask extends Task {
|
|||||||
this(domainFile, DomainFile.DEFAULT_VERSION, false, consumer);
|
this(domainFile, DomainFile.DEFAULT_VERSION, false, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenProgramTask(List<DomainFile> domainFileList, boolean forceReadOnly,
|
public OpenProgramTask(URL ghidraURL, Object consumer) {
|
||||||
Object consumer) {
|
super("Open Program(s)", true, false, true);
|
||||||
super("Open Program(s)", true, domainFileList.size() > 1, true);
|
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
for (DomainFile domainFile : domainFileList) {
|
openProgramRequests.add(new OpenProgramRequest(ghidraURL));
|
||||||
domainFileInfoList.add(new DomainFileInfo(domainFile, -1, forceReadOnly));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenProgramTask(List<DomainFile> domainFileList, Object consumer) {
|
|
||||||
this(domainFileList, false, consumer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOpenPromptText(String text) {
|
public void setOpenPromptText(String text) {
|
||||||
@@ -88,7 +90,16 @@ public class OpenProgramTask extends Task {
|
|||||||
|
|
||||||
public void addProgramToOpen(DomainFile domainFile, int version, boolean forceReadOnly) {
|
public void addProgramToOpen(DomainFile domainFile, int version, boolean forceReadOnly) {
|
||||||
setHasProgress(true);
|
setHasProgress(true);
|
||||||
domainFileInfoList.add(new DomainFileInfo(domainFile, version, forceReadOnly));
|
openProgramRequests.add(new OpenProgramRequest(domainFile, version, forceReadOnly));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addProgramToOpen(URL ghidraURL) {
|
||||||
|
setHasProgress(true);
|
||||||
|
openProgramRequests.add(new OpenProgramRequest(ghidraURL));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOpenProgramRequests() {
|
||||||
|
return !openProgramRequests.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,100 +121,97 @@ public class OpenProgramTask extends Task {
|
|||||||
this.noCheckout = true;
|
this.noCheckout = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Program> getOpenPrograms() {
|
/**
|
||||||
return programList;
|
* Get all successful open program requests
|
||||||
|
* @return all successful open program requests
|
||||||
|
*/
|
||||||
|
public List<OpenProgramRequest> getOpenPrograms() {
|
||||||
|
return Collections.unmodifiableList(openedProgramList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program getOpenProgram() {
|
/**
|
||||||
if (programList.isEmpty()) {
|
* Get the first successful open program request
|
||||||
|
* @return first successful open program request or null if none
|
||||||
|
*/
|
||||||
|
public OpenProgramRequest getOpenProgram() {
|
||||||
|
if (openedProgramList.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return programList.get(0);
|
return openedProgramList.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
|
|
||||||
taskMonitor.initialize(domainFileInfoList.size());
|
taskMonitor.initialize(openProgramRequests.size());
|
||||||
|
|
||||||
for (DomainFileInfo domainFileInfo : domainFileInfoList) {
|
for (OpenProgramRequest domainFileInfo : openProgramRequests) {
|
||||||
if (taskMonitor.isCancelled()) {
|
if (taskMonitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
openDomainFile(domainFileInfo);
|
domainFileInfo.open();
|
||||||
|
|
||||||
taskMonitor.incrementProgress(1);
|
taskMonitor.incrementProgress(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openDomainFile(DomainFileInfo domainFileInfo) {
|
private Object openReadOnlyFile(DomainFile domainFile, URL url, int version) {
|
||||||
int version = domainFileInfo.getVersion();
|
|
||||||
DomainFile domainFile = domainFileInfo.getDomainFile();
|
|
||||||
if (version != DomainFile.DEFAULT_VERSION) {
|
|
||||||
openVersionedFile(domainFile, version);
|
|
||||||
}
|
|
||||||
else if (domainFileInfo.isReadOnly()) {
|
|
||||||
openReadOnlyFile(domainFile, version);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
openUnversionedFile(domainFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openReadOnlyFile(DomainFile domainFile, int version) {
|
|
||||||
taskMonitor.setMessage("Opening " + domainFile.getName());
|
taskMonitor.setMessage("Opening " + domainFile.getName());
|
||||||
openReadOnly(domainFile, version);
|
return openReadOnly(domainFile, url, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openVersionedFile(DomainFile domainFile, int version) {
|
private Object openVersionedFile(DomainFile domainFile, URL url, int version) {
|
||||||
taskMonitor.setMessage("Getting Version " + version + " for " + domainFile.getName());
|
taskMonitor.setMessage("Getting Version " + version + " for " + domainFile.getName());
|
||||||
openReadOnly(domainFile, version);
|
return openReadOnly(domainFile, url, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openReadOnly(DomainFile domainFile, int version) {
|
private Object openReadOnly(DomainFile domainFile, URL url, int version) {
|
||||||
String contentType = null;
|
String contentType = domainFile.getContentType();
|
||||||
|
String path = url != null ? url.toString() : domainFile.getPathname();
|
||||||
|
Object obj = null;
|
||||||
try {
|
try {
|
||||||
contentType = domainFile.getContentType();
|
|
||||||
Program program =
|
|
||||||
(Program) domainFile.getReadOnlyDomainObject(consumer, version, taskMonitor);
|
|
||||||
|
|
||||||
if (program == null) {
|
obj = domainFile.getReadOnlyDomainObject(consumer, version, taskMonitor);
|
||||||
String errorMessage = "Can't open program - \"" + domainFile.getPathname() + "\"";
|
|
||||||
|
if (obj == null) {
|
||||||
|
String errorMessage = "Can't open " + contentType + " - \"" + path + "\"";
|
||||||
if (version != DomainFile.DEFAULT_VERSION) {
|
if (version != DomainFile.DEFAULT_VERSION) {
|
||||||
errorMessage += " version " + version;
|
errorMessage += " version " + version;
|
||||||
}
|
}
|
||||||
|
|
||||||
Msg.showError(this, null, "DomainFile Not Found", errorMessage);
|
Msg.showError(this, null, "File Not Found", errorMessage);
|
||||||
}
|
|
||||||
else {
|
|
||||||
programList.add(program);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (CancelledException e) {
|
catch (CancelledException e) {
|
||||||
// we don't care, the task has been cancelled
|
// we don't care, the task has been cancelled
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
if (domainFile.isInWritableProject()) {
|
if (url == null && domainFile.isInWritableProject()) {
|
||||||
ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e,
|
ClientUtil.handleException(AppInfo.getActiveProject().getRepository(), e,
|
||||||
"Get Versioned Object", null);
|
"Get " + contentType, null);
|
||||||
|
}
|
||||||
|
else if (version != DomainFile.DEFAULT_VERSION) {
|
||||||
|
Msg.showError(this, null, "Error Getting Versioned Program",
|
||||||
|
"Could not get version " + version + " for " + path, e);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Msg.showError(this, null, "Error Getting Versioned Object",
|
Msg.showError(this, null, "Error Getting Program",
|
||||||
"Could not get version " + version + " for " + domainFile.getName(), e);
|
"Open program failed for " + path, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (VersionException e) {
|
catch (VersionException e) {
|
||||||
VersionExceptionHandler.showVersionError(null, domainFile.getName(), contentType,
|
VersionExceptionHandler.showVersionError(null, domainFile.getName(), contentType,
|
||||||
"Open", e);
|
"Open", e);
|
||||||
}
|
}
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openUnversionedFile(DomainFile domainFile) {
|
private Program openUnversionedFile(DomainFile domainFile) {
|
||||||
String filename = domainFile.getName();
|
String filename = domainFile.getName();
|
||||||
taskMonitor.setMessage("Opening " + filename);
|
taskMonitor.setMessage("Opening " + filename);
|
||||||
performOptionalCheckout(domainFile);
|
performOptionalCheckout(domainFile);
|
||||||
try {
|
try {
|
||||||
openFileMaybeUgrade(domainFile);
|
return openFileMaybeUgrade(domainFile);
|
||||||
}
|
}
|
||||||
catch (VersionException e) {
|
catch (VersionException e) {
|
||||||
String contentType = domainFile.getContentType();
|
String contentType = domainFile.getContentType();
|
||||||
@@ -226,9 +234,10 @@ public class OpenProgramTask extends Task {
|
|||||||
"Getting domain object failed.\n" + e.getMessage(), e);
|
"Getting domain object failed.\n" + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openFileMaybeUgrade(DomainFile domainFile)
|
private Program openFileMaybeUgrade(DomainFile domainFile)
|
||||||
throws IOException, CancelledException, VersionException {
|
throws IOException, CancelledException, VersionException {
|
||||||
|
|
||||||
boolean recoverFile = false;
|
boolean recoverFile = false;
|
||||||
@@ -236,24 +245,18 @@ public class OpenProgramTask extends Task {
|
|||||||
recoverFile = askRecoverFile(domainFile.getName());
|
recoverFile = askRecoverFile(domainFile.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Program program = null;
|
||||||
try {
|
try {
|
||||||
Program program =
|
program =
|
||||||
(Program) domainFile.getDomainObject(consumer, false, recoverFile, taskMonitor);
|
(Program) domainFile.getDomainObject(consumer, false, recoverFile, taskMonitor);
|
||||||
|
|
||||||
if (program != null) {
|
|
||||||
programList.add(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (VersionException e) {
|
catch (VersionException e) {
|
||||||
if (VersionExceptionHandler.isUpgradeOK(null, domainFile, openPromptText, e)) {
|
if (VersionExceptionHandler.isUpgradeOK(null, domainFile, openPromptText, e)) {
|
||||||
Program program =
|
program =
|
||||||
(Program) domainFile.getDomainObject(consumer, true, recoverFile, taskMonitor);
|
(Program) domainFile.getDomainObject(consumer, true, recoverFile, taskMonitor);
|
||||||
if (program != null) {
|
|
||||||
programList.add(program);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return program;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean askRecoverFile(final String filename) {
|
private boolean askRecoverFile(final String filename) {
|
||||||
@@ -294,32 +297,158 @@ public class OpenProgramTask extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class DomainFileInfo {
|
public class OpenProgramRequest {
|
||||||
private final DomainFile domainFile;
|
|
||||||
private final int version;
|
|
||||||
private boolean forceReadOnly;
|
|
||||||
|
|
||||||
public DomainFileInfo(DomainFile domainFile, int version, boolean forceReadOnly) {
|
// ghidraURL and domainFile use are mutually exclusive
|
||||||
|
private final URL ghidraURL;
|
||||||
|
private final DomainFile domainFile;
|
||||||
|
|
||||||
|
private URL linkURL; // link URL read from domainFile
|
||||||
|
|
||||||
|
private final int version;
|
||||||
|
private final boolean forceReadOnly;
|
||||||
|
private Program program;
|
||||||
|
|
||||||
|
public OpenProgramRequest(URL ghidraURL) {
|
||||||
|
if (!GhidraURL.PROTOCOL.equals(ghidraURL.getProtocol())) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"unsupported protocol: " + ghidraURL.getProtocol());
|
||||||
|
}
|
||||||
|
this.ghidraURL = ghidraURL;
|
||||||
|
this.domainFile = null;
|
||||||
|
this.version = -1;
|
||||||
|
this.forceReadOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenProgramRequest(DomainFile domainFile, int version, boolean forceReadOnly) {
|
||||||
this.domainFile = domainFile;
|
this.domainFile = domainFile;
|
||||||
|
this.ghidraURL = null;
|
||||||
this.version =
|
this.version =
|
||||||
(domainFile.isReadOnly() && domainFile.isVersioned()) ? domainFile.getVersion()
|
(domainFile.isReadOnly() && domainFile.isVersioned()) ? domainFile.getVersion()
|
||||||
: version;
|
: version;
|
||||||
this.forceReadOnly = forceReadOnly;
|
this.forceReadOnly = forceReadOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReadOnly() {
|
/**
|
||||||
return forceReadOnly || domainFile.isReadOnly() ||
|
* Get the {@link DomainFile} which corresponds to program open request. This will be
|
||||||
version != DomainFile.DEFAULT_VERSION;
|
* null for all URL-based open requests.
|
||||||
}
|
* @return {@link DomainFile} which corresponds to program open request or null.
|
||||||
|
*/
|
||||||
public DomainFile getDomainFile() {
|
public DomainFile getDomainFile() {
|
||||||
return domainFile;
|
return domainFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getVersion() {
|
/**
|
||||||
return version;
|
* Get the {@link URL} which corresponds to program open request. This will be
|
||||||
|
* null for all non-URL-based open requests. URL will be a {@link GhidraURL}.
|
||||||
|
* @return {@link URL} which corresponds to program open request or null.
|
||||||
|
*/
|
||||||
|
public URL getGhidraURL() {
|
||||||
|
return ghidraURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@link URL} which corresponds to the link domainFile used to open a program.
|
||||||
|
* @return {@link URL} which corresponds to the link domainFile used to open a program.
|
||||||
|
*/
|
||||||
|
public URL getLinkURL() {
|
||||||
|
return linkURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the open Program instance which corresponds to this open request.
|
||||||
|
* @return program instance or null if never opened.
|
||||||
|
*/
|
||||||
|
public Program getProgram() {
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release opened program. This must be done once, and only once, on a successful
|
||||||
|
* open request. If handing ownership off to another consumer, they should be added
|
||||||
|
* as a program consumer prior to invoking this method. Releasing the last consumer
|
||||||
|
* will close the program instance.
|
||||||
|
*/
|
||||||
|
public void release() {
|
||||||
|
if (program != null) {
|
||||||
|
program.release(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Program openProgram(DomainFile df, URL url) {
|
||||||
|
if (version != DomainFile.DEFAULT_VERSION) {
|
||||||
|
return (Program) openVersionedFile(df, url, version);
|
||||||
|
}
|
||||||
|
if (forceReadOnly) {
|
||||||
|
return (Program) openReadOnlyFile(df, url, version);
|
||||||
|
}
|
||||||
|
return openUnversionedFile(df);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open() {
|
||||||
|
DomainFile df = domainFile;
|
||||||
|
URL url = ghidraURL;
|
||||||
|
GhidraURLWrappedContent wrappedContent = null;
|
||||||
|
Object content = null;
|
||||||
|
try {
|
||||||
|
if (df == null && url != null) {
|
||||||
|
GhidraURLConnection c = (GhidraURLConnection) url.openConnection();
|
||||||
|
Object obj = c.getContent(); // read-only access
|
||||||
|
if (c.getStatusCode() == StatusCode.UNAUTHORIZED) {
|
||||||
|
return; // assume user already notified
|
||||||
|
}
|
||||||
|
if (!(obj instanceof GhidraURLWrappedContent)) {
|
||||||
|
messageBadProgramURL(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wrappedContent = (GhidraURLWrappedContent) obj;
|
||||||
|
content = wrappedContent.getContent(this);
|
||||||
|
if (!(content instanceof DomainFile)) {
|
||||||
|
messageBadProgramURL(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
df = (DomainFile) content;
|
||||||
|
|
||||||
|
if (ProgramLinkContentHandler.PROGRAM_LINK_CONTENT_TYPE
|
||||||
|
.equals(df.getContentType())) {
|
||||||
|
Msg.showError(this, null, "Program Multi-Link Error",
|
||||||
|
"Multi-link Program access not supported: " + url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Program.class.isAssignableFrom(df.getDomainObjectClass())) {
|
||||||
|
Msg.showError(this, null, "Error Opening Program",
|
||||||
|
"File does not correspond to a Ghidra Program: " + df.getPathname());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
program = openProgram(df, url);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
Msg.showError(this, null, "Invalid Ghidra URL",
|
||||||
|
"Improperly formed Ghidra URL: " + url);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.showError(this, null, "Program Open Failed",
|
||||||
|
"Failed to open Ghidra URL: " + e.getMessage());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (content != null) {
|
||||||
|
wrappedContent.release(content, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program != null) {
|
||||||
|
openedProgramList.add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void messageBadProgramURL(URL url) {
|
||||||
|
Msg.error("Invalid Ghidra URL",
|
||||||
|
"Ghidra URL does not reference a Ghidra Program: " + url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ public class Annotation {
|
|||||||
private static final Pattern QUOTATION_PATTERN =
|
private static final Pattern QUOTATION_PATTERN =
|
||||||
Pattern.compile("(?<!\\\\)[\"](.*?)(?<!\\\\)[\"]");
|
Pattern.compile("(?<!\\\\)[\"](.*?)(?<!\\\\)[\"]");
|
||||||
|
|
||||||
|
private static List<AnnotatedStringHandler> ANNOTATED_STRING_HANDLERS;
|
||||||
private static Map<String, AnnotatedStringHandler> ANNOTATED_STRING_MAP;
|
private static Map<String, AnnotatedStringHandler> ANNOTATED_STRING_MAP;
|
||||||
|
|
||||||
private String annotationText;
|
private String annotationText;
|
||||||
@@ -40,6 +41,13 @@ public class Annotation {
|
|||||||
private AnnotatedStringHandler annotatedStringHandler;
|
private AnnotatedStringHandler annotatedStringHandler;
|
||||||
private AttributedString displayString;
|
private AttributedString displayString;
|
||||||
|
|
||||||
|
public static List<AnnotatedStringHandler> getAnnotatedStringHandlers() {
|
||||||
|
if (ANNOTATED_STRING_HANDLERS == null) {
|
||||||
|
ANNOTATED_STRING_HANDLERS = getSupportedAnnotationHandlers();
|
||||||
|
}
|
||||||
|
return ANNOTATED_STRING_HANDLERS;
|
||||||
|
}
|
||||||
|
|
||||||
private static Map<String, AnnotatedStringHandler> getAnnotatedStringHandlerMap() {
|
private static Map<String, AnnotatedStringHandler> getAnnotatedStringHandlerMap() {
|
||||||
if (ANNOTATED_STRING_MAP == null) { // lazy init due to our use of ClassSearcher
|
if (ANNOTATED_STRING_MAP == null) { // lazy init due to our use of ClassSearcher
|
||||||
ANNOTATED_STRING_MAP = createAnnotatedStringHandlerMap();
|
ANNOTATED_STRING_MAP = createAnnotatedStringHandlerMap();
|
||||||
@@ -47,24 +55,28 @@ public class Annotation {
|
|||||||
return ANNOTATED_STRING_MAP;
|
return ANNOTATED_STRING_MAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
// locates AnnotatedStringHandler implementations to handle annotations
|
|
||||||
private static Map<String, AnnotatedStringHandler> createAnnotatedStringHandlerMap() {
|
private static Map<String, AnnotatedStringHandler> createAnnotatedStringHandlerMap() {
|
||||||
Map<String, AnnotatedStringHandler> map = new HashMap<>();
|
Map<String, AnnotatedStringHandler> map = new HashMap<>();
|
||||||
|
for (AnnotatedStringHandler instance : getAnnotatedStringHandlers()) {
|
||||||
// find all instances of AnnotatedString
|
|
||||||
List<AnnotatedStringHandler> instances =
|
|
||||||
ClassSearcher.getInstances(AnnotatedStringHandler.class);
|
|
||||||
|
|
||||||
for (AnnotatedStringHandler instance : instances) {
|
|
||||||
String[] supportedAnnotations = instance.getSupportedAnnotations();
|
String[] supportedAnnotations = instance.getSupportedAnnotations();
|
||||||
for (String supportedAnnotation : supportedAnnotations) {
|
for (String supportedAnnotation : supportedAnnotations) {
|
||||||
map.put(supportedAnnotation, instance);
|
map.put(supportedAnnotation, instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.unmodifiableMap(map);
|
return Collections.unmodifiableMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// locates AnnotatedStringHandler implementations to handle annotations
|
||||||
|
private static List<AnnotatedStringHandler> getSupportedAnnotationHandlers() {
|
||||||
|
List<AnnotatedStringHandler> list = new ArrayList<>();
|
||||||
|
for (AnnotatedStringHandler h : ClassSearcher.getInstances(AnnotatedStringHandler.class)) {
|
||||||
|
if (h.getSupportedAnnotations().length != 0) {
|
||||||
|
list.add(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
* <b>Note</b>: This constructor assumes that the string starts with "{<pre>@</pre>" and ends with '}'
|
* <b>Note</b>: This constructor assumes that the string starts with "{<pre>@</pre>" and ends with '}'
|
||||||
@@ -184,14 +196,6 @@ public class Annotation {
|
|||||||
return annotationText;
|
return annotationText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AnnotatedStringHandler[] getAnnotatedStringHandlers() {
|
|
||||||
Set<AnnotatedStringHandler> annotations =
|
|
||||||
new HashSet<>(getAnnotatedStringHandlerMap().values());
|
|
||||||
AnnotatedStringHandler[] retVal = new AnnotatedStringHandler[annotations.size()];
|
|
||||||
annotations.toArray(retVal);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*package*/ static Set<String> getAnnotationNames() {
|
/*package*/ static Set<String> getAnnotationNames() {
|
||||||
return Collections.unmodifiableSet(getAnnotatedStringHandlerMap().keySet());
|
return Collections.unmodifiableSet(getAnnotatedStringHandlerMap().keySet());
|
||||||
}
|
}
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.util.viewer.field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation expands {@link URLAnnotatedStringHandler} providing an example form
|
||||||
|
* of a local project Ghidra URL.
|
||||||
|
*/
|
||||||
|
public class GhidraLocalURLAnnotatedStringHandler extends URLAnnotatedStringHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayString() {
|
||||||
|
return "Ghidra-URL(local)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPrototypeString() {
|
||||||
|
return "{@url \"ghidra:/dirpath/myproject?/folder/program.exe#symbol\" \"display string\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.util.viewer.field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This implementation expands {@link URLAnnotatedStringHandler} providing an example form
|
||||||
|
* of a Ghidra Server URL.
|
||||||
|
*/
|
||||||
|
public class GhidraServerURLAnnotatedStringHandler extends URLAnnotatedStringHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisplayString() {
|
||||||
|
return "Ghidra-URL(remote)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPrototypeString() {
|
||||||
|
return "{@url \"ghidra://myserver/myrepo/folder/program.exe#symbol\" \"display string\"}";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+3
-1
@@ -36,8 +36,10 @@ import ghidra.util.Msg;
|
|||||||
* displayed.
|
* displayed.
|
||||||
*/
|
*/
|
||||||
public class URLAnnotatedStringHandler implements AnnotatedStringHandler {
|
public class URLAnnotatedStringHandler implements AnnotatedStringHandler {
|
||||||
|
|
||||||
private static final String INVALID_SYMBOL_TEXT =
|
private static final String INVALID_SYMBOL_TEXT =
|
||||||
"@url annotation must have a URL string " + "optionally followed by a display string";
|
"@url annotation must have a URL string optionally followed by a display string";
|
||||||
|
|
||||||
private static final String[] SUPPORTED_ANNOTATIONS = { "url", "hyperlink", "href", "link" };
|
private static final String[] SUPPORTED_ANNOTATIONS = { "url", "hyperlink", "href", "link" };
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -233,6 +233,13 @@ public class GhidraProject {
|
|||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying ProjectData instance.
|
||||||
|
*/
|
||||||
|
public ProjectData getProjectData() {
|
||||||
|
return projectData;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the ghidra project, closing (without saving!) any open programs in
|
* Closes the ghidra project, closing (without saving!) any open programs in
|
||||||
* that project. Also deletes the project if created as a temporary project.
|
* that project. Also deletes the project if created as a temporary project.
|
||||||
|
|||||||
@@ -93,7 +93,10 @@ public class DataTreeDialog extends DialogComponentProvider
|
|||||||
private Integer treeSelectionMode;
|
private Integer treeSelectionMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new DataTreeDialog.
|
* Construct a new DataTreeDialog. This chooser will show all project files.
|
||||||
|
* Following linked-folders will only be allowed if a type of {@link #CHOOSE_FOLDER}
|
||||||
|
* or {@link #OPEN} is specified. If different behavior is required a filter should
|
||||||
|
* be specified using the other constructor.
|
||||||
*
|
*
|
||||||
* @param parent dialog's parent
|
* @param parent dialog's parent
|
||||||
* @param title title to use
|
* @param title title to use
|
||||||
@@ -101,7 +104,7 @@ public class DataTreeDialog extends DialogComponentProvider
|
|||||||
* @throws IllegalArgumentException if invalid type is specified
|
* @throws IllegalArgumentException if invalid type is specified
|
||||||
*/
|
*/
|
||||||
public DataTreeDialog(Component parent, String title, int type) {
|
public DataTreeDialog(Component parent, String title, int type) {
|
||||||
this(parent, title, type, null);
|
this(parent, title, type, getDefaultFilter(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,6 +122,20 @@ public class DataTreeDialog extends DialogComponentProvider
|
|||||||
initDataTreeDialog(type, filter);
|
initDataTreeDialog(type, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static DomainFileFilter getDefaultFilter(int type) {
|
||||||
|
if (type == CHOOSE_FOLDER || type == OPEN) {
|
||||||
|
// return filter which forces folder selection and allow navigation into linked-folders
|
||||||
|
return new DomainFileFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(DomainFile df) {
|
||||||
|
return true; // show all files (legacy behavior)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTreeSelectionMode(int mode) {
|
public void setTreeSelectionMode(int mode) {
|
||||||
if (treePanel != null) {
|
if (treePanel != null) {
|
||||||
treePanel.getTreeSelectionModel().setSelectionMode(mode);
|
treePanel.getTreeSelectionModel().setSelectionMode(mode);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package ghidra.test;
|
|||||||
import java.awt.Dialog;
|
import java.awt.Dialog;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -47,6 +48,7 @@ import ghidra.framework.plugintool.Plugin;
|
|||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.framework.plugintool.util.PluginException;
|
import ghidra.framework.plugintool.util.PluginException;
|
||||||
import ghidra.framework.project.DefaultProjectManager;
|
import ghidra.framework.project.DefaultProjectManager;
|
||||||
|
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.data.FileDataTypeManager;
|
import ghidra.program.model.data.FileDataTypeManager;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
@@ -874,26 +876,15 @@ public class TestEnv {
|
|||||||
* @param domainFile The domain file used to launch the tool; may be null
|
* @param domainFile The domain file used to launch the tool; may be null
|
||||||
* @return the tool that is launched
|
* @return the tool that is launched
|
||||||
*/
|
*/
|
||||||
public PluginTool launchTool(final String toolName, final DomainFile domainFile) {
|
public PluginTool launchTool(String toolName, DomainFile domainFile) {
|
||||||
AtomicReference<PluginTool> ref = new AtomicReference<>();
|
AtomicReference<PluginTool> ref = new AtomicReference<>();
|
||||||
|
|
||||||
AbstractGenericTest.runSwing(() -> {
|
AbstractGenericTest.runSwing(() -> {
|
||||||
boolean wasErrorGUIEnabled = AbstractDockingTest.isUseErrorGUI();
|
PluginTool newTool = doLaunchTool(toolName);
|
||||||
AbstractDockingTest.setErrorGUIEnabled(false); // disable the error GUI while launching the tool
|
|
||||||
FrontEndTool frontEndToolInstance = getFrontEndTool();
|
|
||||||
|
|
||||||
Project project = frontEndToolInstance.getProject();
|
|
||||||
ToolServices toolServices = project.getToolServices();
|
|
||||||
PluginTool newTool = toolServices.launchTool(toolName, null);
|
|
||||||
if (newTool == null) {
|
|
||||||
// couldn't find the tool in the workspace...check the test area
|
|
||||||
newTool = launchDefaultToolByName(toolName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ref.set(newTool);
|
ref.set(newTool);
|
||||||
|
if (newTool != null) {
|
||||||
AbstractDockingTest.setErrorGUIEnabled(wasErrorGUIEnabled);
|
newTool.acceptDomainFiles(new DomainFile[] { domainFile });
|
||||||
newTool.acceptDomainFiles(new DomainFile[] { domainFile });
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PluginTool launchedTool = ref.get();
|
PluginTool launchedTool = ref.get();
|
||||||
@@ -906,6 +897,52 @@ public class TestEnv {
|
|||||||
return launchedTool;
|
return launchedTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches a tool of the given name using the given Ghidra URL.
|
||||||
|
* <p>
|
||||||
|
* Note: the tool returned will have auto save disabled by default.
|
||||||
|
*
|
||||||
|
* @param toolName the name of the tool to launch
|
||||||
|
* @param ghidraUrl The Ghidra URL to be opened in tool (see {@link GhidraURL})
|
||||||
|
* @return the tool that is launched
|
||||||
|
*/
|
||||||
|
public PluginTool launchToolWithURL(String toolName, URL ghidraUrl) {
|
||||||
|
AtomicReference<PluginTool> ref = new AtomicReference<>();
|
||||||
|
|
||||||
|
AbstractGenericTest.runSwing(() -> {
|
||||||
|
PluginTool newTool = doLaunchTool(toolName);
|
||||||
|
ref.set(newTool);
|
||||||
|
if (newTool != null) {
|
||||||
|
newTool.accept(ghidraUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
PluginTool launchedTool = ref.get();
|
||||||
|
if (launchedTool == null) {
|
||||||
|
throw new NullPointerException("Unable to launch the tool: " + toolName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will make sure that our tool is closed during disposal
|
||||||
|
extraTools.add(launchedTool);
|
||||||
|
return launchedTool;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PluginTool doLaunchTool(String toolName) {
|
||||||
|
boolean wasErrorGUIEnabled = AbstractDockingTest.isUseErrorGUI();
|
||||||
|
AbstractDockingTest.setErrorGUIEnabled(false); // disable the error GUI while launching the tool
|
||||||
|
FrontEndTool frontEndToolInstance = getFrontEndTool();
|
||||||
|
|
||||||
|
Project project = frontEndToolInstance.getProject();
|
||||||
|
ToolServices toolServices = project.getToolServices();
|
||||||
|
PluginTool newTool = toolServices.launchTool(toolName, null);
|
||||||
|
if (newTool == null) {
|
||||||
|
// couldn't find the tool in the workspace...check the test area
|
||||||
|
newTool = launchDefaultToolByName(toolName);
|
||||||
|
}
|
||||||
|
AbstractDockingTest.setErrorGUIEnabled(wasErrorGUIEnabled);
|
||||||
|
return newTool;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the auto-save feature for all tool instances running under the {@link FrontEndTool}
|
* Sets the auto-save feature for all tool instances running under the {@link FrontEndTool}
|
||||||
* created by this TestEnv instance. Auto-save is off by default when testing.
|
* created by this TestEnv instance. Auto-save is off by default when testing.
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 410 B |
+92
-7
@@ -19,6 +19,8 @@ import static org.hamcrest.CoreMatchers.*;
|
|||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -37,6 +39,8 @@ import ghidra.framework.model.*;
|
|||||||
import ghidra.framework.plugintool.ServiceProvider;
|
import ghidra.framework.plugintool.ServiceProvider;
|
||||||
import ghidra.framework.plugintool.TestDummyServiceProvider;
|
import ghidra.framework.plugintool.TestDummyServiceProvider;
|
||||||
import ghidra.framework.project.ProjectDataService;
|
import ghidra.framework.project.ProjectDataService;
|
||||||
|
import ghidra.framework.protocol.ghidra.GhidraURLConnection;
|
||||||
|
import ghidra.framework.store.FileSystem;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
@@ -620,6 +624,60 @@ public class AnnotationTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
assertTrue(spyNavigatable.navigatedTo(otherProgramPath, address));
|
assertTrue(spyNavigatable.navigatedTo(otherProgramPath, address));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGhidraLocalUrlAnnotation_Program_WithAddress() {
|
||||||
|
|
||||||
|
SpyNavigatable spyNavigatable = new SpyNavigatable();
|
||||||
|
SpyServiceProvider spyServiceProvider = new SpyServiceProvider();
|
||||||
|
|
||||||
|
String addresstring = "1001000";
|
||||||
|
|
||||||
|
String pathname = "/a/b/prog";
|
||||||
|
String url = "ghidra:/folder/project?" + pathname + "#" + addresstring;
|
||||||
|
String annotationText = "{@url \"" + url + "\"}";
|
||||||
|
String rawComment = "My comment - " + annotationText;
|
||||||
|
AttributedString prototype = prototype();
|
||||||
|
FieldElement element =
|
||||||
|
CommentUtils.parseTextForAnnotations(rawComment, program, prototype, 0);
|
||||||
|
|
||||||
|
String displayString = element.getText();
|
||||||
|
assertEquals("My comment - " + url, displayString);
|
||||||
|
|
||||||
|
AnnotatedTextFieldElement annotatedElement = getAnnotatedTextFieldElement(element);
|
||||||
|
click(spyNavigatable, spyServiceProvider, annotatedElement);
|
||||||
|
|
||||||
|
assertTrue(spyServiceProvider.programOpened(pathname));
|
||||||
|
|
||||||
|
// Navigation performed by ProgramManager not tested due to use of spyServiceProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGhidraServerUrlAnnotation_Program_WithAddress() {
|
||||||
|
|
||||||
|
SpyNavigatable spyNavigatable = new SpyNavigatable();
|
||||||
|
SpyServiceProvider spyServiceProvider = new SpyServiceProvider();
|
||||||
|
|
||||||
|
String addresstring = "1001000";
|
||||||
|
|
||||||
|
String pathname = "/a/b/prog";
|
||||||
|
String url = "ghidra://server/repo" + pathname + "#" + addresstring;
|
||||||
|
String annotationText = "{@url \"" + url + "\"}";
|
||||||
|
String rawComment = "My comment - " + annotationText;
|
||||||
|
AttributedString prototype = prototype();
|
||||||
|
FieldElement element =
|
||||||
|
CommentUtils.parseTextForAnnotations(rawComment, program, prototype, 0);
|
||||||
|
|
||||||
|
String displayString = element.getText();
|
||||||
|
assertEquals("My comment - " + url, displayString);
|
||||||
|
|
||||||
|
AnnotatedTextFieldElement annotatedElement = getAnnotatedTextFieldElement(element);
|
||||||
|
click(spyNavigatable, spyServiceProvider, annotatedElement);
|
||||||
|
|
||||||
|
assertTrue(spyServiceProvider.programOpened(pathname));
|
||||||
|
|
||||||
|
// Navigation performed by ProgramManager not tested due to use of spyServiceProvider
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnknownAnnotation() {
|
public void testUnknownAnnotation() {
|
||||||
String rawComment = "This is a symbol {@syyyybol bob} annotation";
|
String rawComment = "This is a symbol {@syyyybol bob} annotation";
|
||||||
@@ -903,13 +961,7 @@ public class AnnotationTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
private Set<String> openedPrograms = new HashSet<>();
|
private Set<String> openedPrograms = new HashSet<>();
|
||||||
private Set<String> closedPrograms = new HashSet<>();
|
private Set<String> closedPrograms = new HashSet<>();
|
||||||
|
|
||||||
@Override
|
private Program generateProgram(String pathname, String name) {
|
||||||
public Program openProgram(DomainFile domainFile, int version, int state) {
|
|
||||||
String name = domainFile.getName();
|
|
||||||
String pathname = domainFile.getPathname();
|
|
||||||
|
|
||||||
openedPrograms.add(name);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ProgramBuilder builder = new ProgramBuilder();
|
ProgramBuilder builder = new ProgramBuilder();
|
||||||
builder.setName(pathname);
|
builder.setName(pathname);
|
||||||
@@ -923,6 +975,39 @@ public class AnnotationTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Program openProgram(URL ghidraURL, int state) {
|
||||||
|
try {
|
||||||
|
GhidraURLConnection c = new GhidraURLConnection(ghidraURL);
|
||||||
|
String folderpath = c.getFolderPath();
|
||||||
|
String name = c.getFolderItemName();
|
||||||
|
String pathname = folderpath;
|
||||||
|
if (!pathname.endsWith(FileSystem.SEPARATOR)) {
|
||||||
|
pathname += FileSystem.SEPARATOR;
|
||||||
|
}
|
||||||
|
pathname += name;
|
||||||
|
openedPrograms.add(name);
|
||||||
|
|
||||||
|
Program p = generateProgram(pathname, name);
|
||||||
|
|
||||||
|
// NOTE: URL ref navigation not performed
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
failWithException("Bad URL", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Program openProgram(DomainFile domainFile, int version, int state) {
|
||||||
|
String name = domainFile.getName();
|
||||||
|
String pathname = domainFile.getPathname();
|
||||||
|
openedPrograms.add(name);
|
||||||
|
return generateProgram(pathname, name);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean closeProgram(Program p, boolean ignoreChanges) {
|
public boolean closeProgram(Program p, boolean ignoreChanges) {
|
||||||
String name = FilenameUtils.getName(p.getName());
|
String name = FilenameUtils.getName(p.getName());
|
||||||
|
|||||||
+1
-6
@@ -121,12 +121,7 @@ public class ProjectFileManagerTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
// If there are queued actions, then we have to kick the handling thread and
|
// If there are queued actions, then we have to kick the handling thread and
|
||||||
// let it finish running.
|
// let it finish running.
|
||||||
|
|
||||||
try {
|
assertTrue(eventManager.flushEvents(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS));
|
||||||
assertTrue(eventManager.flushEvents(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS));
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
failWithException("Interrupted waiting for filesystem events", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteAll(File file) {
|
private void deleteAll(File file) {
|
||||||
|
|||||||
+2
-7
@@ -421,15 +421,10 @@ public class DataTreeDialogTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
assertNotNull(dialog);
|
assertNotNull(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DomainFileFilter createStartsWithFilter(String startsWith) {
|
private void showFiltered(final String startsWith) {
|
||||||
return (df) -> df.getName().startsWith(startsWith);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showFiltered(String startsWith) {
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
dialog = new DataTreeDialog(frontEndTool.getToolFrame(), "Test Data Tree Dialog",
|
dialog = new DataTreeDialog(frontEndTool.getToolFrame(), "Test Data Tree Dialog",
|
||||||
DataTreeDialog.OPEN, createStartsWithFilter(startsWith));
|
DataTreeDialog.OPEN, f -> f.getName().startsWith(startsWith));
|
||||||
|
|
||||||
dialog.showComponent();
|
dialog.showComponent();
|
||||||
});
|
});
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|||||||
+2
-2
@@ -79,10 +79,10 @@ public class AddViewToProjectTest extends AbstractGhidraHeadlessIntegrationTest
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
URL view = GhidraURL.makeURL(DIRECTORY_NAME, PROJECT_VIEW1);
|
URL view = GhidraURL.makeURL(DIRECTORY_NAME, PROJECT_VIEW1);
|
||||||
project.addProjectView(view);
|
project.addProjectView(view, true);
|
||||||
|
|
||||||
// add another view that will be removed to test the remove
|
// add another view that will be removed to test the remove
|
||||||
project.addProjectView(GhidraURL.makeURL(DIRECTORY_NAME, PROJECT_VIEW2));
|
project.addProjectView(GhidraURL.makeURL(DIRECTORY_NAME, PROJECT_VIEW2), true);
|
||||||
|
|
||||||
// validate the view was added to project
|
// validate the view was added to project
|
||||||
ProjectLocator[] projViews = project.getProjectViews();
|
ProjectLocator[] projViews = project.getProjectViews();
|
||||||
|
|||||||
+1
-1
@@ -117,7 +117,7 @@ public class CreateDomainObjectTest extends AbstractGhidraHeadedIntegrationTest
|
|||||||
project.close();
|
project.close();
|
||||||
Project project2 = ProjectTestUtils.getProject(testDir, PROJECT_NAME2);
|
Project project2 = ProjectTestUtils.getProject(testDir, PROJECT_NAME2);
|
||||||
try {
|
try {
|
||||||
project2.addProjectView(GhidraURL.makeURL(testDir, PROJECT_NAME1));
|
project2.addProjectView(GhidraURL.makeURL(testDir, PROJECT_NAME1), true);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Assert.fail("View Not found");
|
Assert.fail("View Not found");
|
||||||
|
|||||||
@@ -15,15 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.base.project;
|
package ghidra.base.project;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import generic.test.TestUtils;
|
import generic.test.AbstractGTest;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
import ghidra.framework.remote.User;
|
import ghidra.framework.remote.User;
|
||||||
|
import ghidra.framework.store.local.IndexedV1LocalFileSystem;
|
||||||
import ghidra.framework.store.local.LocalFileSystem;
|
import ghidra.framework.store.local.LocalFileSystem;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents the idea of a shared Ghidra repository. This class is meant to be
|
* This class represents the idea of a shared Ghidra repository. This class is meant to be
|
||||||
@@ -54,11 +57,34 @@ public class FakeRepository {
|
|||||||
private Map<String, User> usersByName = new HashMap<>();
|
private Map<String, User> usersByName = new HashMap<>();
|
||||||
private Map<User, FakeSharedProject> projectsByUser = new HashMap<>();
|
private Map<User, FakeSharedProject> projectsByUser = new HashMap<>();
|
||||||
|
|
||||||
|
private File versionedFSDir;
|
||||||
private LocalFileSystem versionedFileSystem;
|
private LocalFileSystem versionedFileSystem;
|
||||||
|
|
||||||
public FakeRepository() {
|
public FakeRepository() throws IOException {
|
||||||
// validation must be enabled if both environments are utilized by a test
|
// validation must be enabled if both environments are utilized by a test
|
||||||
LocalFileSystem.setValidationRequired();
|
LocalFileSystem.setValidationRequired();
|
||||||
|
|
||||||
|
versionedFSDir =
|
||||||
|
new File(AbstractGTest.getTestDirectoryPath() + File.separator + "TestRepo.rep");
|
||||||
|
if (versionedFSDir.exists()) {
|
||||||
|
FileUtilities.deleteDir(versionedFSDir);
|
||||||
|
}
|
||||||
|
if (versionedFSDir.exists() || !FileUtilities.createDir(versionedFSDir)) {
|
||||||
|
throw new IOException("Failed to create clean repo dir: " + versionedFSDir);
|
||||||
|
}
|
||||||
|
versionedFileSystem = new MyVersionedFileSystem(versionedFSDir.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyVersionedFileSystem extends IndexedV1LocalFileSystem {
|
||||||
|
MyVersionedFileSystem(String rootPath) throws IOException {
|
||||||
|
super(rootPath, true, false, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShared() {
|
||||||
|
// Enables use of asyncronous event dispatching thread
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,11 +135,6 @@ public class FakeRepository {
|
|||||||
|
|
||||||
FakeSharedProject project = new FakeSharedProject(this, user);
|
FakeSharedProject project = new FakeSharedProject(this, user);
|
||||||
projectsByUser.put(user, project);
|
projectsByUser.put(user, project);
|
||||||
|
|
||||||
if (versionedFileSystem == null) {
|
|
||||||
versionedFileSystem = project.getVersionedFileSystem();
|
|
||||||
TestUtils.setInstanceField("isShared", versionedFileSystem, Boolean.TRUE);
|
|
||||||
}
|
|
||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +160,8 @@ public class FakeRepository {
|
|||||||
*/
|
*/
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
projectsByUser.values().forEach(p -> disposeProject(p));
|
projectsByUser.values().forEach(p -> disposeProject(p));
|
||||||
|
versionedFileSystem.dispose();
|
||||||
|
FileUtilities.deleteDir(versionedFSDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disposeProject(FakeSharedProject p) {
|
private void disposeProject(FakeSharedProject p) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import generic.test.AbstractGenericTest;
|
import generic.test.AbstractGTest;
|
||||||
import generic.test.TestUtils;
|
import generic.test.TestUtils;
|
||||||
import ghidra.framework.data.*;
|
import ghidra.framework.data.*;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
@@ -39,6 +39,7 @@ import ghidra.test.TestProgramManager;
|
|||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import junit.framework.AssertionFailedError;
|
import junit.framework.AssertionFailedError;
|
||||||
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents the idea of a shared Ghidra project. Each project is associated with
|
* This class represents the idea of a shared Ghidra project. Each project is associated with
|
||||||
@@ -61,21 +62,18 @@ public class FakeSharedProject {
|
|||||||
public FakeSharedProject(FakeRepository repo, User user) throws IOException {
|
public FakeSharedProject(FakeRepository repo, User user) throws IOException {
|
||||||
|
|
||||||
this.repo = repo;
|
this.repo = repo;
|
||||||
String projectDirPath = AbstractGenericTest.getTestDirectoryPath();
|
String projectDirPath = AbstractGTest.getTestDirectoryPath();
|
||||||
gProject =
|
gProject =
|
||||||
GhidraProject.createProject(projectDirPath, "TestProject_" + user.getName(), true);
|
GhidraProject.createProject(projectDirPath, "TestProject_" + user.getName(), true);
|
||||||
gProject.setDeleteOnClose(true);
|
gProject.setDeleteOnClose(true);
|
||||||
|
|
||||||
LocalFileSystem fs = repo.getSharedFileSystem();
|
// use local shared fake repo versioned file system
|
||||||
if (fs != null) {
|
setVersionedFileSystem(repo.getSharedFileSystem());
|
||||||
// first project will keeps its versioned file system
|
|
||||||
setVersionedFileSystem(fs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FakeSharedProject(User user) throws IOException {
|
FakeSharedProject(User user) throws IOException {
|
||||||
|
|
||||||
String projectDirPath = AbstractGenericTest.getTestDirectoryPath();
|
String projectDirPath = AbstractGTest.getTestDirectoryPath();
|
||||||
gProject =
|
gProject =
|
||||||
GhidraProject.createProject(projectDirPath, "TestProject_" + user.getName(), true);
|
GhidraProject.createProject(projectDirPath, "TestProject_" + user.getName(), true);
|
||||||
}
|
}
|
||||||
@@ -101,7 +99,7 @@ public class FakeSharedProject {
|
|||||||
* @return the project file manager
|
* @return the project file manager
|
||||||
*/
|
*/
|
||||||
public ProjectFileManager getProjectFileManager() {
|
public ProjectFileManager getProjectFileManager() {
|
||||||
return (ProjectFileManager) gProject.getProject().getProjectData();
|
return (ProjectFileManager) gProject.getProjectData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -369,8 +367,11 @@ public class FakeSharedProject {
|
|||||||
* @see FakeRepository#dispose()
|
* @see FakeRepository#dispose()
|
||||||
*/
|
*/
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
ProjectLocator projectLocator = getProjectFileManager().getProjectLocator();
|
||||||
programManager.disposeOpenPrograms();
|
programManager.disposeOpenPrograms();
|
||||||
gProject.close();
|
gProject.close();
|
||||||
|
FileUtilities.deleteDir(projectLocator.getProjectDir());
|
||||||
|
projectLocator.getMarkerFile().delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -400,12 +401,7 @@ public class FakeSharedProject {
|
|||||||
(FileSystemEventManager) TestUtils.getInstanceField("eventManager",
|
(FileSystemEventManager) TestUtils.getInstanceField("eventManager",
|
||||||
versionedFileSystem);
|
versionedFileSystem);
|
||||||
|
|
||||||
try {
|
eventManager.flushEvents(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS);
|
||||||
eventManager.flushEvents(DEFAULT_WAIT_TIMEOUT, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
failWithException("Interrupted waiting for filesystem events", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DomainFolder getFolder(String path) throws Exception {
|
private DomainFolder getFolder(String path) throws Exception {
|
||||||
|
|||||||
@@ -99,6 +99,10 @@ public class CollectFailedRelocations extends GhidraScript {
|
|||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
||||||
programs.add(domainFile);
|
programs.add(domainFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,6 +258,10 @@ public class CreateMultipleLibraries extends GhidraScript {
|
|||||||
DomainFile[] files = myFolder.getFiles();
|
DomainFile[] files = myFolder.getFiles();
|
||||||
for (DomainFile domainFile : files) {
|
for (DomainFile domainFile : files) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
||||||
programs.add(domainFile);
|
programs.add(domainFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -372,6 +372,10 @@ public class FidStatistics extends GhidraScript {
|
|||||||
DomainFile[] files = folder.getFiles();
|
DomainFile[] files = folder.getFiles();
|
||||||
for (DomainFile domainFile : files) {
|
for (DomainFile domainFile : files) {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
||||||
programs.add(domainFile);
|
programs.add(domainFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +16,9 @@
|
|||||||
//Opens all programs under a chosen domain folder, grabs their error count,
|
//Opens all programs under a chosen domain folder, grabs their error count,
|
||||||
//then sorts in increasing error order and prints them
|
//then sorts in increasing error order and prints them
|
||||||
//@category FunctionID
|
//@category FunctionID
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import generic.stl.Pair;
|
import generic.stl.Pair;
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
@@ -27,9 +29,6 @@ import ghidra.util.Msg;
|
|||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class FindErrors extends GhidraScript {
|
public class FindErrors extends GhidraScript {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,6 +81,10 @@ public class FindErrors extends GhidraScript {
|
|||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
||||||
programs.add(domainFile);
|
programs.add(domainFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ public class FindFunctionByHash extends GhidraScript {
|
|||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
||||||
programs.add(domainFile);
|
programs.add(domainFile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
//Opens all programs under a chosen domain folder, scans them for functions
|
//Opens all programs under a chosen domain folder, scans them for functions
|
||||||
//that match a user supplied name, and prints info about the match.
|
//that match a user supplied name, and prints info about the match.
|
||||||
//@category FunctionID
|
//@category FunctionID
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import ghidra.app.script.GhidraScript;
|
import ghidra.app.script.GhidraScript;
|
||||||
import ghidra.feature.fid.service.FidService;
|
import ghidra.feature.fid.service.FidService;
|
||||||
import ghidra.framework.model.DomainFile;
|
import ghidra.framework.model.DomainFile;
|
||||||
@@ -26,9 +29,6 @@ import ghidra.util.Msg;
|
|||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.exception.VersionException;
|
import ghidra.util.exception.VersionException;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class FindNamedFunction extends GhidraScript {
|
public class FindNamedFunction extends GhidraScript {
|
||||||
|
|
||||||
FidService service;
|
FidService service;
|
||||||
@@ -85,6 +85,10 @@ public class FindNamedFunction extends GhidraScript {
|
|||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Do not follow folder-links or consider program links. Using content type
|
||||||
|
// to filter is best way to control this. If program links should be considered
|
||||||
|
// "Program.class.isAssignableFrom(domainFile.getDomainObjectClass())"
|
||||||
|
// should be used.
|
||||||
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
if (domainFile.getContentType().equals(ProgramContentHandler.PROGRAM_CONTENT_TYPE)) {
|
||||||
programs.add(domainFile);
|
programs.add(domainFile);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user