mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-22 00:22:25 +08:00
Merge remote-tracking branch 'origin/GP-4447_ghidra_AbstractGhidraURLQueryTask--SQUASHED'
This commit is contained in:
+6
-10
@@ -1025,8 +1025,7 @@ public class BulkSignatures implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process(Program program, TaskMonitor monitor)
|
||||
throws IOException, LSHException {
|
||||
protected void process(Program program, TaskMonitor monitor) throws IOException {
|
||||
// NOTE: task monitor not used by DescriptionManager
|
||||
String md5string = program.getExecutableMD5();
|
||||
if ((md5string == null) || (md5string.length() < 10)) {
|
||||
@@ -1059,9 +1058,8 @@ public class BulkSignatures implements AutoCloseable {
|
||||
manager.saveXml(fwrite);
|
||||
}
|
||||
}
|
||||
catch (LSHException | IOException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
throw e;
|
||||
catch (LSHException e) {
|
||||
throw new IOException("Program signature generation failure: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1084,8 +1082,7 @@ public class BulkSignatures implements AutoCloseable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void process(Program program, TaskMonitor monitor)
|
||||
throws IOException, LSHException, DecompileException {
|
||||
protected void process(Program program, TaskMonitor monitor) throws IOException {
|
||||
// NOTE: task monitor not used by DescriptionManager
|
||||
String md5string = program.getExecutableMD5();
|
||||
if ((md5string == null) || (md5string.length() < 10)) {
|
||||
@@ -1121,9 +1118,8 @@ public class BulkSignatures implements AutoCloseable {
|
||||
manager.saveXml(fwrite);
|
||||
fwrite.close();
|
||||
}
|
||||
catch (DecompileException | LSHException | IOException e) {
|
||||
Msg.error(this, e.getMessage());
|
||||
throw e;
|
||||
catch (DecompileException | LSHException e) {
|
||||
throw new IOException("Program signature generation failure: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-85
@@ -15,13 +15,10 @@
|
||||
*/
|
||||
package ghidra.features.bsim.query.ingest;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.features.bsim.query.LSHException;
|
||||
import ghidra.framework.client.NotConnectedException;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.protocol.ghidra.*;
|
||||
@@ -38,11 +35,11 @@ public abstract class IterateRepository {
|
||||
* Perform processing on program obtained from repository.
|
||||
* @param program program obtained from repository
|
||||
* @param monitor processing task monitor
|
||||
* @throws Exception if an error occured during processing.
|
||||
* @throws IOException if an error occured during processing.
|
||||
* @throws CancelledException if processing was cancelled
|
||||
*/
|
||||
protected abstract void process(Program program, TaskMonitor monitor)
|
||||
throws Exception, CancelledException;
|
||||
throws IOException, CancelledException;
|
||||
|
||||
/**
|
||||
* Process the specified repository URL
|
||||
@@ -59,93 +56,32 @@ public abstract class IterateRepository {
|
||||
throw new MalformedURLException("Unsupported repository URL: " + ghidraURL);
|
||||
}
|
||||
|
||||
URL repoURL = GhidraURL.getProjectURL(ghidraURL);
|
||||
String path = GhidraURL.getProjectPathname(ghidraURL);
|
||||
GhidraURLQuery.queryUrl(ghidraURL, new GhidraURLResultHandlerAdapter(true) {
|
||||
|
||||
String finalelement = null;
|
||||
path = path.trim();
|
||||
if (!path.endsWith("/")) {
|
||||
int pos = path.lastIndexOf('/');
|
||||
if (pos >= 0) {
|
||||
String tmp = path.substring(0, pos + 1);
|
||||
if (tmp.length() != 0 && !tmp.equals("/")) {
|
||||
finalelement = path.substring(pos + 1); // A possible file name at the end of the path
|
||||
path = tmp;
|
||||
@Override
|
||||
public void processResult(DomainFolder domainFolder, URL url, TaskMonitor m)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
if (GhidraURL.isServerRepositoryURL(ghidraURL)) {
|
||||
ghidraURL = new URL(repoURL + path);
|
||||
}
|
||||
else {
|
||||
ghidraURL = new URL(repoURL + "?" + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
GhidraURLConnection c = (GhidraURLConnection) ghidraURL.openConnection();
|
||||
|
||||
Msg.debug(IterateRepository.class, "Opening ghidra repository: " + ghidraURL);
|
||||
Object obj = c.getContent();
|
||||
if (!(obj instanceof GhidraURLWrappedContent)) {
|
||||
throw new IOException("Connect to repository folder failed");
|
||||
}
|
||||
|
||||
Object consumer = new Object();
|
||||
|
||||
GhidraURLWrappedContent wrappedContent = (GhidraURLWrappedContent) obj;
|
||||
Object content = null;
|
||||
try {
|
||||
content = wrappedContent.getContent(consumer);
|
||||
if (!(content instanceof DomainFolder)) {
|
||||
throw new IOException("Connect to repository folder failed");
|
||||
}
|
||||
|
||||
DomainFolder folder = (DomainFolder) content;
|
||||
|
||||
int totalFiles = getTotalFileCount(folder);
|
||||
int totalFiles = getTotalFileCount(domainFolder);
|
||||
|
||||
monitor.setMaximum(totalFiles);
|
||||
monitor.setShowProgressValue(true);
|
||||
|
||||
if (finalelement != null) {
|
||||
DomainFolder subfolder = folder.getFolder(finalelement);
|
||||
|
||||
if (subfolder != null) {
|
||||
folder = subfolder;
|
||||
// fall thru to the DomainFile and DomainFolder loop
|
||||
}
|
||||
else {
|
||||
DomainFile file = folder.getFile(finalelement);
|
||||
|
||||
if (file == null) {
|
||||
throw new IOException("Bad folder/file element: " + finalelement);
|
||||
}
|
||||
|
||||
process(file, monitor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
process(folder, monitor);
|
||||
process(domainFolder, monitor);
|
||||
}
|
||||
finally {
|
||||
if (content != null) {
|
||||
wrappedContent.release(content, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor m)
|
||||
throws IOException, CancelledException {
|
||||
process(domainFile, monitor);
|
||||
}
|
||||
}
|
||||
catch (NotConnectedException e) {
|
||||
throw new IOException(
|
||||
"Ghidra repository connection failed (" + repoURL + "): " + e.getMessage());
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new IOException("Repository path not found: " + path);
|
||||
}
|
||||
|
||||
}, monitor);
|
||||
|
||||
}
|
||||
|
||||
private void process(DomainFolder folder, TaskMonitor monitor)
|
||||
throws Exception, CancelledException {
|
||||
throws IOException, CancelledException {
|
||||
|
||||
for (DomainFile file : folder.getFiles()) {
|
||||
monitor.checkCancelled();
|
||||
@@ -177,7 +113,7 @@ public abstract class IterateRepository {
|
||||
}
|
||||
|
||||
private void process(DomainFile file, TaskMonitor monitor)
|
||||
throws Exception, CancelledException {
|
||||
throws IOException, CancelledException {
|
||||
|
||||
// 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
|
||||
@@ -189,12 +125,11 @@ public abstract class IterateRepository {
|
||||
}
|
||||
|
||||
Program program = null;
|
||||
Object consumer = new Object();
|
||||
try {
|
||||
Msg.debug(IterateRepository.class, "Processing " + file.getPathname() + "...");
|
||||
monitor.setMessage("Processing: " + file.getName());
|
||||
monitor.incrementProgress(1);
|
||||
program = (Program) file.getReadOnlyDomainObject(consumer, -1, monitor);
|
||||
program = (Program) file.getReadOnlyDomainObject(this, -1, monitor);
|
||||
process(program, monitor);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
@@ -203,7 +138,7 @@ public abstract class IterateRepository {
|
||||
}
|
||||
finally {
|
||||
if (program != null) {
|
||||
program.release(consumer);
|
||||
program.release(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ import ghidra.framework.remote.User;
|
||||
import ghidra.framework.store.LockException;
|
||||
import ghidra.framework.store.local.LocalFileSystem;
|
||||
import ghidra.program.database.ProgramContentHandler;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.GhidraProgramUtilities;
|
||||
@@ -310,52 +309,55 @@ public class HeadlessAnalyzer {
|
||||
|
||||
Msg.info(HeadlessAnalyzer.class, "HEADLESS: execution starts");
|
||||
|
||||
GhidraURLConnection c = (GhidraURLConnection) ghidraURL.openConnection();
|
||||
c.setReadOnly(options.readOnly); // writable repository connection
|
||||
// force explicit folder access since file may have same name as folder
|
||||
ghidraURL = GhidraURL.getFolderURL(ghidraURL);
|
||||
|
||||
if (c.getRepositoryName() == null) {
|
||||
throw new MalformedURLException("Unsupported repository URL: " + ghidraURL);
|
||||
}
|
||||
Msg.info(this, "Opening ghidra repository folder: " + ghidraURL);
|
||||
|
||||
Msg.info(this, "Opening ghidra repository project: " + ghidraURL);
|
||||
Object obj = c.getContent();
|
||||
if (!(obj instanceof GhidraURLWrappedContent)) {
|
||||
throw new IOException(
|
||||
"Connect to repository folder failed. Response code: " + c.getStatusCode());
|
||||
}
|
||||
GhidraURLWrappedContent wrappedContent = (GhidraURLWrappedContent) obj;
|
||||
Object content = null;
|
||||
try {
|
||||
content = wrappedContent.getContent(this);
|
||||
if (!(content instanceof DomainFolder)) {
|
||||
throw new IOException("Connect to repository folder failed");
|
||||
}
|
||||
GhidraURLQuery.queryRepositoryUrl(ghidraURL, options.readOnly,
|
||||
new GhidraURLResultHandlerAdapter() {
|
||||
|
||||
DomainFolder folder = (DomainFolder) content;
|
||||
project = new HeadlessProject(getProjectManager(), c);
|
||||
@Override
|
||||
public void processResult(DomainFolder domainFolder, URL url,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
try {
|
||||
project = new HeadlessProject(getProjectManager(),
|
||||
domainFolder.getProjectData());
|
||||
|
||||
if (!checkUpdateOptions()) {
|
||||
return; // TODO: Should an exception be thrown?
|
||||
}
|
||||
if (!checkUpdateOptions()) {
|
||||
return; // TODO: Should an exception be thrown?
|
||||
}
|
||||
|
||||
if (options.runScriptsNoImport) {
|
||||
processNoImport(folder.getPathname());
|
||||
}
|
||||
else {
|
||||
processWithImport(folder.getPathname(), filesToImport);
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
throw new IOException("Connect to repository folder failed");
|
||||
}
|
||||
finally {
|
||||
if (content != null) {
|
||||
wrappedContent.release(content, this);
|
||||
}
|
||||
if (project != null) {
|
||||
project.close();
|
||||
}
|
||||
}
|
||||
if (options.runScriptsNoImport) {
|
||||
processNoImport(domainFolder.getPathname());
|
||||
}
|
||||
else {
|
||||
processWithImport(domainFolder.getPathname(), filesToImport);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (project != null) {
|
||||
project.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(String title, String message, URL url,
|
||||
IOException cause) throws IOException {
|
||||
if (cause instanceof FileNotFoundException) {
|
||||
throw new IOException("Connect to repository folder failed");
|
||||
}
|
||||
if (cause != null) {
|
||||
throw cause;
|
||||
}
|
||||
throw new IOException(title + ": " + message);
|
||||
}
|
||||
}, TaskMonitor.DUMMY);
|
||||
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
throw new IOException(e); // unexpected
|
||||
}
|
||||
finally {
|
||||
GhidraScriptUtil.dispose();
|
||||
@@ -1835,17 +1837,16 @@ public class HeadlessAnalyzer {
|
||||
*/
|
||||
private static class HeadlessProject extends DefaultProject {
|
||||
|
||||
HeadlessProject(HeadlessGhidraProjectManager projectManager, GhidraURLConnection connection)
|
||||
throws IOException {
|
||||
super(projectManager, connection);
|
||||
AppInfo.setActiveProject(this);
|
||||
}
|
||||
|
||||
HeadlessProject(HeadlessGhidraProjectManager projectManager, ProjectLocator projectLocator)
|
||||
throws NotOwnerException, LockException, IOException {
|
||||
super(projectManager, projectLocator, false);
|
||||
AppInfo.setActiveProject(this);
|
||||
}
|
||||
|
||||
HeadlessProject(HeadlessGhidraProjectManager projectManager, ProjectData projectData) {
|
||||
super(projectManager, (DefaultProjectData) projectData);
|
||||
AppInfo.setActiveProject(this);
|
||||
}
|
||||
}
|
||||
|
||||
private static class HeadlessGhidraProjectManager extends DefaultProjectManager {
|
||||
|
||||
@@ -17,6 +17,7 @@ package ghidra.app.util.task;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.plugin.core.progmgr.ProgramLocator;
|
||||
@@ -25,9 +26,8 @@ import ghidra.framework.client.ClientUtil;
|
||||
import ghidra.framework.client.RepositoryAdapter;
|
||||
import ghidra.framework.main.AppInfo;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLConnection;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLConnection.StatusCode;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLWrappedContent;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLQuery;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLResultHandlerAdapter;
|
||||
import ghidra.framework.remote.User;
|
||||
import ghidra.framework.store.ExclusiveCheckoutException;
|
||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||
@@ -35,6 +35,7 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
@@ -84,74 +85,42 @@ public class ProgramOpener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the program for the given location
|
||||
* Opens the program for the given location.
|
||||
* This method is intended to be invoked from within a {@link Task} or for headless operations.
|
||||
* @param locator the program location to open
|
||||
* @param monitor the TaskMonitor used for status and cancelling
|
||||
* @return the opened program or null if the operation failed or was cancelled
|
||||
*/
|
||||
public Program openProgram(ProgramLocator locator, TaskMonitor monitor) {
|
||||
if (locator.isURL()) {
|
||||
return openURL(locator, monitor);
|
||||
try {
|
||||
return openURL(locator, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Program Open Failed",
|
||||
"Failed to open Ghidra URL: " + locator.getURL(), e);
|
||||
}
|
||||
}
|
||||
return openProgram(locator, locator.getDomainFile(), monitor);
|
||||
}
|
||||
|
||||
private Program openURL(ProgramLocator locator, TaskMonitor monitor) {
|
||||
URL url = locator.getURL();
|
||||
GhidraURLWrappedContent wrappedContent = getWrappedContent(url);
|
||||
if (wrappedContent == null) {
|
||||
return null;
|
||||
}
|
||||
DomainFile remoteDomainFile = getDomainFile(url, wrappedContent);
|
||||
if (remoteDomainFile == null) {
|
||||
return null;
|
||||
}
|
||||
private Program openURL(ProgramLocator locator, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
URL ghidraUrl = locator.getURL();
|
||||
|
||||
try {
|
||||
return openProgram(locator, remoteDomainFile, monitor);
|
||||
}
|
||||
finally {
|
||||
wrappedContent.release(remoteDomainFile, this);
|
||||
}
|
||||
}
|
||||
|
||||
private DomainFile getDomainFile(URL url, GhidraURLWrappedContent wrappedContent) {
|
||||
try {
|
||||
Object content = wrappedContent.getContent(this);
|
||||
if (content instanceof DomainFile domainFile) {
|
||||
return domainFile;
|
||||
AtomicReference<Program> openedProgram = new AtomicReference<>();
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, new GhidraURLResultHandlerAdapter() {
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor m) {
|
||||
Program p = openProgram(locator, domainFile, m); // may return null
|
||||
openedProgram.set(p);
|
||||
}
|
||||
messageBadProgramURL(url);
|
||||
if (content != null) {
|
||||
wrappedContent.release(content, this);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Program Open Failed", "Failed to open Ghidra URL: " + url,
|
||||
e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, monitor);
|
||||
|
||||
private GhidraURLWrappedContent getWrappedContent(URL url) {
|
||||
try {
|
||||
GhidraURLConnection c = (GhidraURLConnection) url.openConnection();
|
||||
Object obj = c.getContent(); // read-only access
|
||||
|
||||
if (c.getStatusCode() == StatusCode.UNAUTHORIZED) {
|
||||
return null; // assume user already notified
|
||||
}
|
||||
|
||||
if (obj instanceof GhidraURLWrappedContent wrappedContent) {
|
||||
return wrappedContent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "Program Open Failed", "Failed to open Ghidra URL: " + url,
|
||||
e);
|
||||
}
|
||||
return null;
|
||||
return openedProgram.get();
|
||||
}
|
||||
|
||||
private Program openProgram(ProgramLocator locator, DomainFile domainFile,
|
||||
@@ -253,9 +222,8 @@ public class ProgramOpener {
|
||||
if (domainFile.checkout(dialog.exclusiveCheckout(), monitor)) {
|
||||
return;
|
||||
}
|
||||
Msg.showError(this, null, "Checkout Failed",
|
||||
"Exclusive checkout failed for: " + domainFile.getName() +
|
||||
"\nOne or more users have file checked out!");
|
||||
Msg.showError(this, null, "Checkout Failed", "Exclusive checkout failed for: " +
|
||||
domainFile.getName() + "\nOne or more users have file checked out!");
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
// we don't care, the task has been cancelled
|
||||
@@ -298,8 +266,4 @@ public class ProgramOpener {
|
||||
return option == OptionDialog.OPTION_ONE;
|
||||
}
|
||||
|
||||
private void messageBadProgramURL(URL url) {
|
||||
Msg.error("Invalid Ghidra URL", "Ghidra URL does not reference a Ghidra Program: " + url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -69,7 +69,6 @@ public class GhidraGoPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
layout = (GhidraApplicationLayout) createApplicationLayout();
|
||||
ghidraGo = new GhidraGo();
|
||||
|
||||
|
||||
CheckForFileProcessedRunnable.WAIT_FOR_PROCESSING_DELAY_MS = 1000;
|
||||
CheckForFileProcessedRunnable.MAX_WAIT_FOR_PROCESSING_MIN = 1;
|
||||
CheckForFileProcessedRunnable.WAIT_FOR_PROCESSING_PERIOD_MS = 10;
|
||||
@@ -129,7 +128,7 @@ public class GhidraGoPluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
}
|
||||
});
|
||||
AbstractErrDialog err = waitForErrorDialog();
|
||||
assertEquals("Unsupported Content", err.getTitle());
|
||||
assertEquals("Content Not Found", err.getTitle());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@ import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
import generic.theme.GIcon;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.protocol.ghidra.*;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLConnection.StatusCode;
|
||||
import ghidra.framework.store.FileSystem;
|
||||
import ghidra.framework.store.FolderItem;
|
||||
import ghidra.framework.store.local.LocalFileSystem;
|
||||
@@ -91,47 +91,56 @@ public abstract class LinkHandler<T extends DomainObjectAdapterDB> extends DBCon
|
||||
return getObject(item, version, consumer, monitor, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private T getObject(FolderItem item, int version, Object consumer, TaskMonitor monitor,
|
||||
boolean immutable) throws IOException, VersionException, CancelledException {
|
||||
|
||||
URL url = getURL(item);
|
||||
URL ghidraUrl = getURL(item);
|
||||
|
||||
Class<?> domainObjectClass = getDomainObjectClass();
|
||||
if (domainObjectClass == null) {
|
||||
throw new UnsupportedOperationException("");
|
||||
}
|
||||
|
||||
GhidraURLWrappedContent wrappedContent = null;
|
||||
Object content = null;
|
||||
final Object transientConsumer = new Object();
|
||||
try {
|
||||
GhidraURLConnection c = (GhidraURLConnection) url.openConnection();
|
||||
Object obj = c.getContent(); // read-only access
|
||||
if (c.getStatusCode() == StatusCode.UNAUTHORIZED) {
|
||||
AtomicReference<VersionException> verExcRef = new AtomicReference<>();
|
||||
AtomicReference<T> domainObjectRef = new AtomicReference<>();
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, new GhidraURLResultHandlerAdapter(true) {
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor m)
|
||||
throws IOException, CancelledException {
|
||||
if (!getDomainObjectClass().isAssignableFrom(domainFile.getDomainObjectClass())) {
|
||||
throw new BadLinkException("Expected " + getDomainObjectClass() +
|
||||
" but linked to " + domainFile.getDomainObjectClass());
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
T linkedObject = immutable
|
||||
? (T) domainFile.getImmutableDomainObject(consumer, version, monitor)
|
||||
: (T) domainFile.getReadOnlyDomainObject(consumer, version, monitor);
|
||||
domainObjectRef.set(linkedObject);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
verExcRef.set(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUnauthorizedAccess(URL url) throws IOException {
|
||||
throw new IOException("Authorization failure");
|
||||
}
|
||||
if (!(obj instanceof GhidraURLWrappedContent)) {
|
||||
throw new IOException("Unsupported linked content");
|
||||
}
|
||||
wrappedContent = (GhidraURLWrappedContent) obj;
|
||||
content = wrappedContent.getContent(transientConsumer);
|
||||
if (!(content instanceof DomainFile)) {
|
||||
throw new IOException("Unsupported linked content: " + content.getClass());
|
||||
}
|
||||
DomainFile linkedFile = (DomainFile) content;
|
||||
if (!getDomainObjectClass().isAssignableFrom(linkedFile.getDomainObjectClass())) {
|
||||
throw new BadLinkException("Expected " + getDomainObjectClass() +
|
||||
" but linked to " + linkedFile.getDomainObjectClass());
|
||||
}
|
||||
return immutable ? (T) linkedFile.getImmutableDomainObject(consumer, version, monitor)
|
||||
: (T) linkedFile.getReadOnlyDomainObject(consumer, version, monitor);
|
||||
}, monitor);
|
||||
|
||||
VersionException versionException = verExcRef.get();
|
||||
if (versionException != null) {
|
||||
throw versionException;
|
||||
}
|
||||
finally {
|
||||
if (content != null) {
|
||||
wrappedContent.release(content, transientConsumer);
|
||||
}
|
||||
|
||||
T domainObj = domainObjectRef.get();
|
||||
if (domainObj == null) {
|
||||
throw new IOException(
|
||||
"Failed to obtain linked object for unknown reason: " + item.getPathName());
|
||||
}
|
||||
return domainObj;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-11
@@ -120,22 +120,15 @@ public class DefaultProject implements Project {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for opening a URL-based project
|
||||
* Construct a project with specific project manager and data
|
||||
*
|
||||
* @param projectManager the manager of this project
|
||||
* @param connection project URL connection (not previously used)
|
||||
* @throws IOException if I/O error occurs.
|
||||
* @param projectData the project data
|
||||
*/
|
||||
protected DefaultProject(DefaultProjectManager projectManager, GhidraURLConnection connection)
|
||||
throws IOException {
|
||||
protected DefaultProject(DefaultProjectManager projectManager, DefaultProjectData projectData) {
|
||||
|
||||
this.projectManager = projectManager;
|
||||
|
||||
Msg.info(this, "Opening project/repository: " + connection.getURL());
|
||||
projectData = (DefaultProjectData) connection.getProjectData();
|
||||
if (projectData == null) {
|
||||
throw new IOException("Failed to open project/repository: " + connection.getURL());
|
||||
}
|
||||
this.projectData = projectData;
|
||||
|
||||
projectLocator = projectData.getProjectLocator();
|
||||
if (!SystemUtilities.isInHeadlessMode()) {
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ import ghidra.framework.main.FrontEndTool;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.protocol.ghidra.GetUrlContentTypeTask;
|
||||
import ghidra.framework.protocol.ghidra.ContentTypeQueryTask;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURL;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.filechooser.GhidraFileChooserModel;
|
||||
@@ -272,7 +272,7 @@ class ToolServicesImpl implements ToolServices {
|
||||
}
|
||||
|
||||
private String getContentType(URL url) throws IllegalArgumentException {
|
||||
GetUrlContentTypeTask task = new GetUrlContentTypeTask(url);
|
||||
ContentTypeQueryTask task = new ContentTypeQueryTask(url);
|
||||
TaskLauncher.launch(task); // blocking task
|
||||
return task.getContentType();
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/* ###
|
||||
* 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.protocol.ghidra;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A blocking/modal Ghidra URL content type discovery task
|
||||
*/
|
||||
public class ContentTypeQueryTask extends GhidraURLQueryTask {
|
||||
|
||||
private String contentType = "Unknown";
|
||||
|
||||
/**
|
||||
* Construct a Ghidra URL content type query task
|
||||
* @param ghidraUrl Ghidra URL (local or remote)
|
||||
* @throws IllegalArgumentException if specified URL is not a Ghidra URL
|
||||
* (see {@link GhidraURL}).
|
||||
*/
|
||||
public ContentTypeQueryTask(URL ghidraUrl) {
|
||||
super("Query URL Content Type", ghidraUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the discovered content type (e.g., "Program")
|
||||
* @return content type or null if error occured or unsupported URL content
|
||||
* @throws IllegalStateException if task has not completed execution
|
||||
*/
|
||||
public String getContentType() {
|
||||
if (!isDone()) {
|
||||
throw new IllegalStateException("task has not completed");
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor monitor) {
|
||||
contentType = domainFile.getContentType();
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/* ###
|
||||
* 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.protocol.ghidra;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLConnection.StatusCode;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* A blocking/modal Ghidra URL content type discovery task
|
||||
*/
|
||||
public class GetUrlContentTypeTask extends Task {
|
||||
|
||||
private final URL ghidraUrl;
|
||||
|
||||
private String contentType;
|
||||
private boolean done = false;
|
||||
|
||||
/**
|
||||
* Construct a Ghidra URL content type discovery task
|
||||
* @param ghidraUrl Ghidra URL (local or remote)
|
||||
* @throws IllegalArgumentException if specified URL is not a Ghidra URL
|
||||
* (see {@link GhidraURL}).
|
||||
*/
|
||||
public GetUrlContentTypeTask(URL ghidraUrl) {
|
||||
super("Checking URL Content Type", true, false, true);
|
||||
if (!GhidraURL.isLocalProjectURL(ghidraUrl) &&
|
||||
!GhidraURL.isServerRepositoryURL(ghidraUrl)) {
|
||||
throw new IllegalArgumentException("unsupported URL");
|
||||
}
|
||||
this.ghidraUrl = ghidraUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the discovered content type (e.g., "Program")
|
||||
* @return content type or null if error occured or unsupported URL content
|
||||
* @throws IllegalStateException if task has not completed execution
|
||||
*/
|
||||
public String getContentType() {
|
||||
if (!done) {
|
||||
throw new IllegalStateException("task has not completed");
|
||||
}
|
||||
return contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) {
|
||||
final Thread t = Thread.currentThread();
|
||||
monitor.addCancelledListener(() -> {
|
||||
t.interrupt();
|
||||
});
|
||||
GhidraURLWrappedContent wrappedContent = null;
|
||||
Object content = null;
|
||||
try {
|
||||
GhidraURLConnection c = (GhidraURLConnection) ghidraUrl.openConnection();
|
||||
Object obj = c.getContent(); // read-only access
|
||||
if (c.getStatusCode() == StatusCode.UNAUTHORIZED) {
|
||||
return; // assume user already notified
|
||||
}
|
||||
if (obj instanceof GhidraURLWrappedContent) {
|
||||
wrappedContent = (GhidraURLWrappedContent) obj;
|
||||
content = wrappedContent.getContent(this);
|
||||
}
|
||||
if (!(content instanceof DomainFile)) {
|
||||
Msg.showError(this, null, "Unsupported Content",
|
||||
"Invalid project file URL: " + ghidraUrl);
|
||||
return;
|
||||
}
|
||||
contentType = ((DomainFile) content).getContentType();
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
Msg.showError(this, null, "Content Not Found", e.getMessage());
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
Msg.showError(this, null, "Invalid Ghidra URL",
|
||||
"Improperly formed Ghidra URL: " + ghidraUrl);
|
||||
}
|
||||
catch (InterruptedIOException e) {
|
||||
// ignore - assume cancelled
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.showError(this, null, "URL Access Failure",
|
||||
"Failed to open Ghidra URL: " + e.getMessage());
|
||||
}
|
||||
finally {
|
||||
if (content != null) {
|
||||
wrappedContent.release(content, this);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+49
-3
@@ -121,7 +121,8 @@ public class GhidraURL {
|
||||
* Confirm local project URL with {@link #isLocalProjectURL(URL)} prior to method use.
|
||||
* @param localProjectURL local Ghidra project URL
|
||||
* @return project locator or null if invalid path specified
|
||||
* @throws IllegalArgumentException URL is not a valid local project URL
|
||||
* @throws IllegalArgumentException URL is not a valid
|
||||
* {@link #isLocalProjectURL(URL) local project URL}.
|
||||
*/
|
||||
public static ProjectLocator getProjectStorageLocator(URL localProjectURL) {
|
||||
if (!isLocalProjectURL(localProjectURL)) {
|
||||
@@ -330,9 +331,12 @@ public class GhidraURL {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get normalized URL which corresponds to the local-project or repository
|
||||
* Get Ghidra URL which corresponds to the local-project or repository with any
|
||||
* file path or query details removed.
|
||||
* @param ghidraUrl ghidra file/folder URL (server-only URL not permitted)
|
||||
* @return local-project or repository URL
|
||||
* @throws IllegalArgumentException if URL does not specify the {@code ghidra} protocol
|
||||
* or does not properly identify a remote repository or local project.
|
||||
*/
|
||||
public static URL getProjectURL(URL ghidraUrl) {
|
||||
if (!PROTOCOL.equals(ghidraUrl.getProtocol())) {
|
||||
@@ -456,6 +460,48 @@ public class GhidraURL {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the specified URL to specify a folder. This may be neccessary when only folders
|
||||
* are supported since Ghidra permits both a folder and file to have the same name within
|
||||
* its parent folder. This method simply ensures that the URL path ends with a {@code /}
|
||||
* character if needed.
|
||||
* @param ghidraUrl ghidra URL
|
||||
* @return ghidra folder URL
|
||||
* @throws IllegalArgumentException if specified URL is niether a
|
||||
* {@link #isServerRepositoryURL(URL) valid remote server URL}
|
||||
* or {@link #isLocalProjectURL(URL) local project URL}.
|
||||
*/
|
||||
public static URL getFolderURL(URL ghidraUrl) {
|
||||
|
||||
if (!GhidraURL.isServerRepositoryURL(ghidraUrl) &&
|
||||
!GhidraURL.isLocalProjectURL(ghidraUrl)) {
|
||||
throw new IllegalArgumentException("Invalid Ghidra URL: " + ghidraUrl);
|
||||
}
|
||||
|
||||
URL repoURL = GhidraURL.getProjectURL(ghidraUrl);
|
||||
String path = GhidraURL.getProjectPathname(ghidraUrl);
|
||||
|
||||
path = path.trim();
|
||||
if (!path.endsWith("/")) {
|
||||
|
||||
// force explicit folder path
|
||||
path += "/";
|
||||
|
||||
try {
|
||||
if (GhidraURL.isServerRepositoryURL(ghidraUrl)) {
|
||||
ghidraUrl = new URL(repoURL + path);
|
||||
}
|
||||
else {
|
||||
ghidraUrl = new URL(repoURL + "?" + path);
|
||||
}
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
return ghidraUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a normalized URL which eliminates use of host names and optional URL ref
|
||||
* which may prevent direct comparison.
|
||||
@@ -634,7 +680,7 @@ public class GhidraURL {
|
||||
* @param ref optional URL ref or null
|
||||
* Folder paths should end with a '/' character.
|
||||
* @return Ghidra Server repository content URL
|
||||
* @throws IllegalArgumentException if invalid arguments are specified
|
||||
* @throws IllegalArgumentException if required arguments are blank or invalid
|
||||
*/
|
||||
public static URL makeURL(String host, int port, String repositoryName,
|
||||
String repositoryFolderPath, String fileName, String ref) {
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/* ###
|
||||
* 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.protocol.ghidra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.protocol.ghidra.GhidraURLConnection.StatusCode;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.Task;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* {@link GhidraURLQuery} performs remote Ghidra repository and read-only local project
|
||||
* queries for processing either a {@link DomainFile} or {@link DomainFolder} that a
|
||||
* Ghidra URL may reference.
|
||||
*/
|
||||
public abstract class GhidraURLQuery {
|
||||
|
||||
/**
|
||||
* Perform read-only query using specified GhidraURL and process result.
|
||||
* Both local project and remote repository URLs are supported.
|
||||
* This method is intended to be invoked from within a {@link Task} or for headless operations.
|
||||
* @param ghidraUrl local or remote Ghidra URL
|
||||
* @param resultHandler query result handler
|
||||
* @param monitor task monitor
|
||||
* @throws IOException if an IO error occurs which was re-thrown by {@code resultHandler}
|
||||
* @throws CancelledException if task is cancelled
|
||||
*/
|
||||
public static void queryUrl(URL ghidraUrl, GhidraURLResultHandler resultHandler,
|
||||
TaskMonitor monitor) throws IOException, CancelledException {
|
||||
doQueryUrl(ghidraUrl, true, resultHandler, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform query using specified GhidraURL and process result.
|
||||
* Both local project and remote repository URLs are supported.
|
||||
* This method is intended to be invoked from within a {@link Task} or for headless operations.
|
||||
* @param ghidraUrl local or remote Ghidra URL
|
||||
* @param readOnly allows update/commit (false) or read-only (true) access.
|
||||
* @param resultHandler query result handler
|
||||
* @param monitor task monitor
|
||||
* @throws IOException if an IO error occurs which was re-thrown by {@code resultHandler}
|
||||
* @throws CancelledException if task is cancelled
|
||||
*/
|
||||
public static void queryRepositoryUrl(URL ghidraUrl, boolean readOnly,
|
||||
GhidraURLResultHandler resultHandler, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
if (!GhidraURL.isServerRepositoryURL(ghidraUrl)) {
|
||||
throw new IllegalArgumentException("Unsupported repository URL: " + ghidraUrl);
|
||||
}
|
||||
doQueryUrl(ghidraUrl, readOnly, resultHandler, monitor);
|
||||
}
|
||||
|
||||
private static void doQueryUrl(URL ghidraUrl, boolean readOnly,
|
||||
GhidraURLResultHandler resultHandler, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
|
||||
GhidraURLConnection c;
|
||||
Object obj = null;
|
||||
StatusCode status = null;
|
||||
try {
|
||||
c = (GhidraURLConnection) ghidraUrl.openConnection();
|
||||
c.setReadOnly(readOnly); // writable repository connection
|
||||
obj = c.getContent(); // read-only access
|
||||
status = c.getStatusCode();
|
||||
}
|
||||
catch (IOException e) {
|
||||
resultHandler.handleError("URL Connection Error", e.getMessage(), ghidraUrl, e);
|
||||
}
|
||||
|
||||
GhidraURLWrappedContent wrappedContent = null;
|
||||
Object content = null;
|
||||
try {
|
||||
IOException generatedErr = null;
|
||||
switch (status) {
|
||||
case OK:
|
||||
break;
|
||||
|
||||
case UNAUTHORIZED:
|
||||
resultHandler.handleUnauthorizedAccess(ghidraUrl);
|
||||
return;
|
||||
|
||||
case NOT_FOUND:
|
||||
generatedErr = new IOException("Project or repository not found");
|
||||
break;
|
||||
|
||||
case LOCKED:
|
||||
// Local projects are only accessed read-only, this condition should not occur
|
||||
throw new AssertionError("Unexpected local project lock condition");
|
||||
|
||||
case UNAVAILABLE:
|
||||
generatedErr =
|
||||
new IOException("Server connection error occured (see log files)");
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
if (generatedErr != null) {
|
||||
resultHandler.handleError("Content Not Found", generatedErr.getMessage(), ghidraUrl,
|
||||
generatedErr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(obj instanceof GhidraURLWrappedContent)) {
|
||||
resultHandler.handleError("Unsupported Content",
|
||||
"URL does not correspond to a file or folder", null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
wrappedContent = (GhidraURLWrappedContent) obj;
|
||||
try {
|
||||
content = wrappedContent.getContent(resultHandler);
|
||||
}
|
||||
catch (IOException e) {
|
||||
resultHandler.handleError("Content Not Found", e.getMessage(), null, e);
|
||||
return;
|
||||
}
|
||||
|
||||
monitor.checkCancelled();
|
||||
if (content instanceof DomainFile file) {
|
||||
resultHandler.processResult(file, ghidraUrl, monitor);
|
||||
}
|
||||
else if (content instanceof DomainFolder folder) {
|
||||
resultHandler.processResult(folder, ghidraUrl, monitor);
|
||||
}
|
||||
else {
|
||||
// unexpected condition
|
||||
resultHandler.handleError("Unsupported Content",
|
||||
"Content class: " + content.getClass().getName(), ghidraUrl, null);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (content != null) {
|
||||
wrappedContent.release(content, resultHandler);
|
||||
}
|
||||
monitor.checkCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/* ###
|
||||
* 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.protocol.ghidra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.*;
|
||||
|
||||
/**
|
||||
* {@link GhidraURLQueryTask} provides an abstract Task which performs remote Ghidra
|
||||
* repository and read-only local project queries for processing either a {@link DomainFile}
|
||||
* or {@link DomainFolder} that a Ghidra URL may reference.
|
||||
* <P>
|
||||
* All implementations of this Task should override one or
|
||||
* both of the processing methods {@link #processResult(DomainFile, URL, TaskMonitor)}
|
||||
* and {@link #processResult(DomainFolder, URL, TaskMonitor)}. For any process method
|
||||
* not overriden the default behavior is reporting <I>Unsupported Content</I>.
|
||||
* <P>
|
||||
* If {@link #handleError(String, String, URL, IOException)}
|
||||
* is not overriden all errors are reported via
|
||||
* {@link Msg#showError(Object, java.awt.Component, String, Object)}.
|
||||
*/
|
||||
public abstract class GhidraURLQueryTask extends Task implements GhidraURLResultHandler {
|
||||
|
||||
private final URL ghidraUrl;
|
||||
|
||||
private boolean done = false;
|
||||
|
||||
/**
|
||||
* Construct a Ghidra URL read-only query task.
|
||||
* @param title task dialog title
|
||||
* @param ghidraUrl Ghidra URL (local or remote)
|
||||
* @throws IllegalArgumentException if specified URL is not a Ghidra URL
|
||||
* (see {@link GhidraURL}).
|
||||
*/
|
||||
protected GhidraURLQueryTask(String title, URL ghidraUrl) {
|
||||
super(title, true, false, true);
|
||||
if (!GhidraURL.isLocalProjectURL(ghidraUrl) &&
|
||||
!GhidraURL.isServerRepositoryURL(ghidraUrl)) {
|
||||
throw new IllegalArgumentException("Unsupported URL: " + ghidraUrl);
|
||||
}
|
||||
this.ghidraUrl = ghidraUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the task has completed its execution
|
||||
* @return true if done executing else false
|
||||
*/
|
||||
protected boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
final Thread t = Thread.currentThread();
|
||||
CancelledListener cancelledListener = () -> t.interrupt();
|
||||
monitor.addCancelledListener(cancelledListener);
|
||||
|
||||
try {
|
||||
GhidraURLQuery.queryUrl(ghidraUrl, this, monitor);
|
||||
}
|
||||
catch (InterruptedIOException e) {
|
||||
// ignore - assume cancelled
|
||||
}
|
||||
catch (IOException e) {
|
||||
handleError("URL Access Failure", e.getMessage(), ghidraUrl, e);
|
||||
}
|
||||
finally {
|
||||
monitor.removeCancelledListener(cancelledListener);
|
||||
monitor.checkCancelled();
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(String title, String message, URL url, IOException cause) {
|
||||
Msg.showError(GhidraURLQuery.class, null, title, message + ":\n" + url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
handleError("Unsupported Content", "File URL: " + url, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFolder domainFolder, URL url, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
handleError("Unsupported Content", "Folder URL: " + url, null, null);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/* ###
|
||||
* 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.protocol.ghidra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public interface GhidraURLResultHandler {
|
||||
|
||||
/**
|
||||
* Process the specified {@code domainFile} query result.
|
||||
* Dissemination of the {@code domainFile} instance should be restricted and any use of it
|
||||
* completed before the call to this method returns. Upon return from this method call the
|
||||
* underlying connection will be closed and at which time the {@code domainFile} instance
|
||||
* will become invalid.
|
||||
* @param domainFile {@link DomainFile} to which the URL refers.
|
||||
* @param url URL which was used to retrieve the specified {@code domainFile}
|
||||
* @param monitor task monitor
|
||||
* @throws IOException if an IO error occurs
|
||||
* @throws CancelledException if task is cancelled
|
||||
*/
|
||||
void processResult(DomainFile domainFile, URL url, TaskMonitor monitor)
|
||||
throws IOException, CancelledException;
|
||||
|
||||
/**
|
||||
* Process the specified {@code domainFolder} query result.
|
||||
* Dissemination of the {@code domainFolder} instance should be restricted and any use of it
|
||||
* completed before the call to this method returns. Upon return from this method call the
|
||||
* underlying connection will be closed and at which time the {@code domainFolder} instance
|
||||
* will become invalid.
|
||||
* @param domainFolder {@link DomainFolder} to which the URL refers.
|
||||
* @param url URL which was used to retrieve the specified {@code domainFolder}
|
||||
* @param monitor task monitor
|
||||
* @throws IOException if an IO error occurs
|
||||
* @throws CancelledException if task is cancelled
|
||||
*/
|
||||
void processResult(DomainFolder domainFolder, URL url, TaskMonitor monitor)
|
||||
throws IOException, CancelledException;
|
||||
|
||||
/**
|
||||
* Handle error which occurs during query operation.
|
||||
* @param title error title
|
||||
* @param message error detail
|
||||
* @param url URL which was used for query
|
||||
* @param cause cause of error (may be null)
|
||||
* @throws IOException may be thrown if handler decides to propogate error
|
||||
*/
|
||||
void handleError(String title, String message, URL url, IOException cause) throws IOException;
|
||||
|
||||
/**
|
||||
* Handle authorization error.
|
||||
* This condition is generally logged and user notified via GUI during connection processing.
|
||||
* This method does not do anything by default but is provided to flag failure if needed since
|
||||
* {@link #handleError(String, String, URL, IOException)} will not be invoked.
|
||||
* @param url connection URL
|
||||
* @throws IOException may be thrown if handler decides to propogate error
|
||||
*/
|
||||
default void handleUnauthorizedAccess(URL url) throws IOException {
|
||||
// do nothing - assume user has already been notified or issue has been logged
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/* ###
|
||||
* 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.protocol.ghidra;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* {@link GhidraURLResultHandlerAdapter} provides a basic result handler for
|
||||
* {@link GhidraURLQuery}. All uses of this adapter should override one or
|
||||
* both of the processing methods {@link #processResult(DomainFile, URL, TaskMonitor)}
|
||||
* and {@link #processResult(DomainFolder, URL, TaskMonitor)}. For any process method
|
||||
* not overriden the default behavior is reporting <I>Unsupported Content</I>.
|
||||
*/
|
||||
public class GhidraURLResultHandlerAdapter implements GhidraURLResultHandler {
|
||||
|
||||
private final boolean throwErrorByDefault;
|
||||
|
||||
/**
|
||||
* Construct adapter. If {@link #handleError(String, String, URL, IOException)}
|
||||
* is not overriden all errors are reported via
|
||||
* {@link Msg#showError(Object, java.awt.Component, String, Object)}.
|
||||
*/
|
||||
public GhidraURLResultHandlerAdapter() {
|
||||
throwErrorByDefault = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct adapter with preferred error handling. There is no need to use this constructor
|
||||
* if {@link #handleError(String, String, URL, IOException)} is override.
|
||||
* @param throwErrorByDefault if true all errors will be thrown as an {@link IOException},
|
||||
* otherwise error is reported via {@link Msg#showError(Object, java.awt.Component, String, Object)}.
|
||||
*/
|
||||
public GhidraURLResultHandlerAdapter(boolean throwErrorByDefault) {
|
||||
this.throwErrorByDefault = throwErrorByDefault;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFile domainFile, URL url, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
handleError("Unsupported Content", "File URL: " + url, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processResult(DomainFolder domainFolder, URL url, TaskMonitor monitor)
|
||||
throws IOException, CancelledException {
|
||||
handleError("Unsupported Content", "Folder URL: " + url, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleError(String title, String message, URL url, IOException cause)
|
||||
throws IOException {
|
||||
if (!throwErrorByDefault) {
|
||||
Msg.showError(GhidraURLQuery.class, null, title, message + ":\n" + url);
|
||||
}
|
||||
if (cause != null) {
|
||||
throw cause;
|
||||
}
|
||||
throw new IOException(title + ": " + message);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user