mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge branch 'GP-4433_ghidraffe_GhidraGo_accept_DomainFolder_GhidraURL'
This commit is contained in:
commit
da8ff58ba8
12 changed files with 399 additions and 63 deletions
|
@ -24,6 +24,7 @@ import ghidra.framework.main.AppInfo;
|
|||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.store.FileSystem;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
|
@ -45,8 +46,7 @@ public class FolderLinkContentHandler extends LinkHandler<NullFolderDomainObject
|
|||
if (!(obj instanceof URLLinkObject)) {
|
||||
throw new IOException("Unsupported domain object: " + obj.getClass().getName());
|
||||
}
|
||||
return createFile((URLLinkObject) obj, FOLDER_LINK_CONTENT_TYPE, fs, path, name,
|
||||
monitor);
|
||||
return createFile((URLLinkObject) obj, FOLDER_LINK_CONTENT_TYPE, fs, path, name, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,6 +91,11 @@ public class FolderLinkContentHandler extends LinkHandler<NullFolderDomainObject
|
|||
URL url = getURL(folderLinkFile);
|
||||
|
||||
Project activeProject = AppInfo.getActiveProject();
|
||||
if (activeProject == null) {
|
||||
Msg.error(FolderLinkContentHandler.class,
|
||||
"Use of Linked Folders requires active project.");
|
||||
return null;
|
||||
}
|
||||
GhidraFolder parent = ((GhidraFile) folderLinkFile).getParent();
|
||||
return new LinkedGhidraFolder(activeProject, parent, folderLinkFile.getName(), url);
|
||||
}
|
||||
|
|
|
@ -1067,10 +1067,14 @@ public class GhidraFileData {
|
|||
if (folderItem.isCheckedOut() || versionedFolderItem != null) {
|
||||
throw new IOException("File already versioned");
|
||||
}
|
||||
if (isLinkFile() && !GhidraURL.isServerRepositoryURL(LinkHandler.getURL(folderItem))) {
|
||||
throw new IOException("Local project link-file may not be versioned");
|
||||
ContentHandler<?> contentHandler = getContentHandler();
|
||||
if (contentHandler instanceof LinkHandler linkHandler) {
|
||||
// must check local vs remote URL
|
||||
if (!GhidraURL.isServerRepositoryURL(LinkHandler.getURL(folderItem))) {
|
||||
throw new IOException("Local project link-file may not be versioned");
|
||||
}
|
||||
}
|
||||
if (getContentHandler().isPrivateContentType()) {
|
||||
else if (contentHandler.isPrivateContentType()) {
|
||||
throw new IOException("Content may not be versioned: " + getContentType());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -165,8 +165,7 @@ public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBCon
|
|||
|
||||
@Override
|
||||
public final boolean isPrivateContentType() {
|
||||
// NOTE: URL must be checked - only repository-based links may be versioned
|
||||
return true;
|
||||
throw new UnsupportedOperationException("Link file requires checking server vs local URL");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,8 +34,7 @@ public class LinkedGhidraFolder extends LinkedGhidraSubFolder {
|
|||
|
||||
public static Icon FOLDER_LINK_CLOSED_ICON =
|
||||
new GIcon("icon.content.handler.linked.folder.closed");
|
||||
public static Icon FOLDER_LINK_OPEN_ICON =
|
||||
new GIcon("icon.content.handler.linked.folder.open");
|
||||
public static Icon FOLDER_LINK_OPEN_ICON = new GIcon("icon.content.handler.linked.folder.open");
|
||||
|
||||
private final Project activeProject;
|
||||
private final DomainFolder localParent;
|
||||
|
@ -73,8 +72,8 @@ public class LinkedGhidraFolder extends LinkedGhidraSubFolder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the Ghidra URL associated with this linked folder's project or repository
|
||||
* @return Ghidra URL associated with this linked folder's project or repository
|
||||
* Get the Ghidra URL of the project/repository folder referenced by this object
|
||||
* @return Ghidra URL of the project/repository folder referenced by this object
|
||||
*/
|
||||
public URL getProjectURL() {
|
||||
if (projectUrl == null) {
|
||||
|
@ -83,6 +82,7 @@ public class LinkedGhidraFolder extends LinkedGhidraSubFolder {
|
|||
return projectUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
LinkedGhidraFolder getLinkedRootFolder() {
|
||||
return this;
|
||||
}
|
||||
|
@ -108,12 +108,12 @@ public class LinkedGhidraFolder extends LinkedGhidraSubFolder {
|
|||
|
||||
@Override
|
||||
public ProjectLocator getProjectLocator() {
|
||||
return activeProject.getProjectLocator();
|
||||
return localParent.getProjectLocator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectData getProjectData() {
|
||||
return activeProject.getProjectData();
|
||||
return localParent.getProjectData();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,6 +28,10 @@ import ghidra.util.*;
|
|||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* {@code LinkedGhidraSubFolder} corresponds to a {@link DomainFolder} contained within a
|
||||
* {@link LinkedGhidraFolder} or another {@code LinkedGhidraSubFolder}.
|
||||
*/
|
||||
class LinkedGhidraSubFolder implements LinkedDomainFolder {
|
||||
|
||||
private final LinkedGhidraFolder linkedRootFolder;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/* ###
|
||||
* 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.framework.main;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.framework.data.FolderLinkContentHandler;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLQueryTask;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class AcceptUrlContentTask extends GhidraURLQueryTask {
|
||||
|
||||
private FrontEndPlugin plugin;
|
||||
|
||||
public AcceptUrlContentTask(URL url, FrontEndPlugin plugin) {
|
||||
super("Accepting URL", url);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
private boolean isSameLocalProject(ProjectLocator projectLoc1, ProjectLocator projectLoc2) {
|
||||
if (projectLoc1.isTransient() || projectLoc2.isTransient()) {
|
||||
return false;
|
||||
}
|
||||
if (!projectLoc1.getName().equals(projectLoc2.getName())) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
File proj1Dir = projectLoc1.getProjectDir().getCanonicalFile();
|
||||
File proj2Dir = projectLoc2.getProjectDir().getCanonicalFile();
|
||||
return proj1Dir.equals(proj2Dir);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
|
||||
Project activeProject = AppInfo.getActiveProject();
|
||||
if (activeProject == null) {
|
||||
Msg.showError(this, null, "Ghidra Error",
|
||||
"Unable to accept URL without active project open");
|
||||
return;
|
||||
}
|
||||
|
||||
Swing.runNow(() -> {
|
||||
if (FolderLinkContentHandler.FOLDER_LINK_CONTENT_TYPE
|
||||
.equals(domainFile.getContentType())) {
|
||||
// Simply select folder link-file within project - do not follow - let user do that.
|
||||
if (isSameLocalProject(activeProject.getProjectLocator(),
|
||||
domainFile.getProjectLocator())) {
|
||||
// Select file within active project
|
||||
DomainFile df =
|
||||
activeProject.getProjectData().getFile(domainFile.getPathname());
|
||||
if (df == null) {
|
||||
return; // unexpected race condition
|
||||
}
|
||||
plugin.selectFiles(Set.of(df));
|
||||
}
|
||||
else {
|
||||
// Select file within read-only viewed project
|
||||
plugin.showInViewedProject(url, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
AppInfo.getFrontEndTool().getToolServices().launchDefaultToolWithURL(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFolder domainFolder, URL url, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
|
||||
Project activeProject = AppInfo.getActiveProject();
|
||||
if (activeProject == null) {
|
||||
Msg.showError(this, null, "Ghidra Error",
|
||||
"Unable to accept URL without active project open");
|
||||
return;
|
||||
}
|
||||
|
||||
Swing.runNow(() -> {
|
||||
if (isSameLocalProject(activeProject.getProjectLocator(),
|
||||
domainFolder.getProjectLocator())) {
|
||||
// Select folder within active project
|
||||
DomainFolder df =
|
||||
activeProject.getProjectData().getFolder(domainFolder.getPathname());
|
||||
if (df == null) {
|
||||
return; // unexpected race condition
|
||||
}
|
||||
plugin.selectFolder(df);
|
||||
}
|
||||
else {
|
||||
// Select folder within read-only viewed project
|
||||
plugin.showInViewedProject(url, true);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -51,6 +51,7 @@ import ghidra.framework.options.SaveState;
|
|||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||
import ghidra.framework.remote.User;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.filechooser.GhidraFileChooserModel;
|
||||
|
@ -539,8 +540,22 @@ public class FrontEndPlugin extends Plugin
|
|||
SwingUtilities.invokeLater(() -> {
|
||||
// there was a delete bug; make the set unmodifiable to catch this earlier
|
||||
Set<DomainFile> unmodifiableFiles = Collections.unmodifiableSet(files);
|
||||
if (dataTablePanel.isCapacityExceeded()) {
|
||||
projectDataPanel.showTree();
|
||||
}
|
||||
else {
|
||||
dataTablePanel.setSelectedDomainFiles(unmodifiableFiles);
|
||||
}
|
||||
dataTreePanel.selectDomainFiles(unmodifiableFiles);
|
||||
dataTablePanel.setSelectedDomainFiles(unmodifiableFiles);
|
||||
});
|
||||
}
|
||||
|
||||
void selectFolder(final DomainFolder folder) {
|
||||
// Do this later in case any of the given files are newly created, which means that the
|
||||
// GUIs may have not yet been notified.
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
projectDataPanel.showTree();
|
||||
dataTreePanel.selectDomainFolder(folder);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1081,7 +1096,7 @@ public class FrontEndPlugin extends Plugin
|
|||
public void openDomainFile(DomainFile domainFile) {
|
||||
|
||||
if (FolderLinkContentHandler.FOLDER_LINK_CONTENT_TYPE.equals(domainFile.getContentType())) {
|
||||
showLinkedFolder(domainFile);
|
||||
showLinkedFolderInViewedProject(domainFile);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1109,7 +1124,7 @@ public class FrontEndPlugin extends Plugin
|
|||
"opens this type of file");
|
||||
}
|
||||
|
||||
private void showLinkedFolder(DomainFile domainFile) {
|
||||
private void showLinkedFolderInViewedProject(DomainFile domainFile) {
|
||||
|
||||
try {
|
||||
LinkedGhidraFolder linkedFolder =
|
||||
|
@ -1123,7 +1138,12 @@ public class FrontEndPlugin extends Plugin
|
|||
return;
|
||||
}
|
||||
|
||||
DomainFolder domainFolder = linkedFolder.getLinkedFolder();
|
||||
// Do not hang onto domainFile, linkedFolder or their underlying project data
|
||||
|
||||
ProjectData viewedProjectData = dtp.getProjectData();
|
||||
DomainFolder domainFolder =
|
||||
viewedProjectData.getFolder(linkedFolder.getLinkedPathname());
|
||||
|
||||
if (domainFolder != null) {
|
||||
// delayed to ensure tree is displayed
|
||||
Swing.runLater(() -> dtp.selectDomainFolder(domainFolder));
|
||||
|
@ -1136,6 +1156,52 @@ public class FrontEndPlugin extends Plugin
|
|||
|
||||
}
|
||||
|
||||
void showInViewedProject(URL ghidraURL, boolean isFolder) {
|
||||
|
||||
ProjectDataTreePanel dtp = projectDataPanel.openView(GhidraURL.getProjectURL(ghidraURL));
|
||||
if (dtp == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Swing.runLater(() -> {
|
||||
// delayed to ensure tree is displayed
|
||||
|
||||
ProjectData viewedProjectData = dtp.getProjectData();
|
||||
|
||||
String path = GhidraURL.getProjectPathname(ghidraURL);
|
||||
|
||||
if (isFolder) {
|
||||
DomainFolder viewedProjectFolder = getViewProjectFolder(viewedProjectData, path);
|
||||
if (viewedProjectFolder != null) {
|
||||
dtp.selectDomainFolder(viewedProjectFolder);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DomainFile viewedProjectFile = getViewProjectFile(viewedProjectData, path);
|
||||
if (viewedProjectFile != null) {
|
||||
dtp.selectDomainFile(viewedProjectFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DomainFile getViewProjectFile(ProjectData viewedProjectData, String path) {
|
||||
if (path == null || path.endsWith(DomainFolder.SEPARATOR)) {
|
||||
return null;
|
||||
}
|
||||
return viewedProjectData.getFile(path);
|
||||
}
|
||||
|
||||
private DomainFolder getViewProjectFolder(ProjectData viewedProjectData, String path) {
|
||||
if (path == null || path.equals(DomainFolder.SEPARATOR)) {
|
||||
return viewedProjectData.getRootFolder();
|
||||
}
|
||||
if (path.endsWith(DomainFolder.SEPARATOR)) {
|
||||
path = path.substring(0, path.length() - 1); // remove trailing separator
|
||||
}
|
||||
return viewedProjectData.getFolder(path);
|
||||
}
|
||||
|
||||
private class MyToolChestChangeListener implements ToolChestChangeListener {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -64,6 +64,7 @@ import ghidra.framework.plugintool.util.*;
|
|||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.project.tool.GhidraTool;
|
||||
import ghidra.framework.project.tool.GhidraToolTemplate;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.bean.GGlassPane;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
@ -176,6 +177,15 @@ public class FrontEndTool extends PluginTool implements OptionsChangeListener {
|
|||
System.exit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(URL url) {
|
||||
if (!GhidraURL.isLocalProjectURL(url) && !GhidraURL.isServerRepositoryURL(url)) {
|
||||
return false;
|
||||
}
|
||||
Swing.runLater(() -> execute(new AcceptUrlContentTask(url, plugin)));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ensureSize() {
|
||||
JFrame frame = getToolFrame();
|
||||
Dimension size = frame.getSize();
|
||||
|
|
|
@ -406,6 +406,10 @@ class ProjectDataPanel extends JSplitPane implements ProjectViewListener {
|
|||
}
|
||||
}
|
||||
|
||||
void showTree() {
|
||||
projectTab.setSelectedIndex(0);
|
||||
}
|
||||
|
||||
private void showTable() {
|
||||
projectTab.setSelectedIndex(1);
|
||||
}
|
||||
|
|
|
@ -103,6 +103,14 @@ public class ProjectDataTablePanel extends JPanel {
|
|||
new ProjectDataTableDnDHandler(gTable, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if table capacity has been exceeded and files are not shown
|
||||
* @return true if files are not shown in project data table, else false
|
||||
*/
|
||||
public boolean isCapacityExceeded() {
|
||||
return capacityExceeded;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
table.dispose(); // this will dispose the gTable as well
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue