Merge remote-tracking branch 'origin/GP-4735_dev747368_pdb_trusted_symbolserver--SQUASHED'

This commit is contained in:
Ryan Kurtz 2024-08-12 14:19:48 -04:00
commit 33867f718b
26 changed files with 1057 additions and 890 deletions

View File

@ -4,9 +4,9 @@
* 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.
@ -19,13 +19,9 @@
//The ~/symbols directory should already exist and be initialized as a symbol
//storage location.
//@category PDB
import java.io.File;
import java.util.List;
import java.io.File;
import java.net.URI;
import ghidra.app.plugin.core.analysis.PdbAnalyzer;
import ghidra.app.plugin.core.analysis.PdbUniversalAnalyzer;
import ghidra.app.script.GhidraScript;
import pdb.PdbPlugin;
import pdb.symbolserver.*;
@ -38,16 +34,11 @@ public class PdbSymbolServerExamplePrescript extends GhidraScript {
File symDir = new File(homeDir, "symbols");
LocalSymbolStore localSymbolStore = new LocalSymbolStore(symDir);
HttpSymbolServer msSymbolServer =
new HttpSymbolServer(URI.create("https://msdl.microsoft.com/download/symbols/"));
HttpSymbolServer.createTrusted("https://msdl.microsoft.com/download/symbols/");
SymbolServerService symbolServerService =
new SymbolServerService(localSymbolStore, List.of(msSymbolServer));
PdbPlugin.saveSymbolServerServiceConfig(symbolServerService);
// You only need to enable the "allow remote" option on the specific
// analyzer you are using
PdbUniversalAnalyzer.setAllowRemoteOption(currentProgram, true);
PdbAnalyzer.setAllowRemoteOption(currentProgram, true);
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -25,7 +25,6 @@
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.AccessMode;
import java.util.List;
@ -44,8 +43,8 @@ public class GetMSDownloadLinkScript extends GhidraScript {
@Override
protected void run() throws Exception {
SymbolServerService symbolService =
new SymbolServerService(new SameDirSymbolStore(null), List.of(
new HttpSymbolServer(URI.create(MS_PUBLIC_SYMBOL_SERVER_URL))));
new SymbolServerService(new SameDirSymbolStore(null),
List.of(HttpSymbolServer.createTrusted(MS_PUBLIC_SYMBOL_SERVER_URL)));
File f = askFile("File To Scan", "Select");
if (f == null) {
@ -63,9 +62,7 @@ public class GetMSDownloadLinkScript extends GhidraScript {
", sizeOfImage: " + Integer.toHexString(sizeOfImage));
SymbolFileInfo symbolFileInfo = SymbolFileInfo.fromValues(f.getName().toLowerCase(),
Integer.toHexString(timeDateStamp), sizeOfImage);
List<SymbolFileLocation> findResults =
symbolService.find(symbolFileInfo, FindOption.of(FindOption.ALLOW_REMOTE),
monitor);
List<SymbolFileLocation> findResults = symbolService.find(symbolFileInfo, monitor);
if (findResults.isEmpty()) {
println("Not found on " + MS_PUBLIC_SYMBOL_SERVER_URL);
return;

View File

@ -4,9 +4,9 @@
* 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.
@ -42,7 +42,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
private static final String ERROR_TITLE = "Error in PDB Analyzer";
private boolean searchRemoteLocations = false;
private boolean searchUntrustedLocations = false;
// only try once per transaction due to extensive error logging which may get duplicated
private long lastTransactionId = -1;
@ -85,7 +85,7 @@ public class PdbAnalyzer extends AbstractAnalyzer {
return false;
}
File pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchRemoteLocations, monitor);
File pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchUntrustedLocations, monitor);
if (pdbFile == null) {
// warnings have already been logged
return false;
@ -138,15 +138,15 @@ public class PdbAnalyzer extends AbstractAnalyzer {
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS,
searchRemoteLocations, null,
PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS);
options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_UNTRUSTED_LOCATIONS,
searchUntrustedLocations, null,
PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_UNTRUSTED_LOCATIONS);
}
@Override
public void optionsChanged(Options options, Program program) {
searchRemoteLocations = options.getBoolean(
PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS, searchRemoteLocations);
searchUntrustedLocations = options.getBoolean(
PdbAnalyzerCommon.OPTION_NAME_SEARCH_UNTRUSTED_LOCATIONS, searchUntrustedLocations);
}
/**
@ -165,18 +165,18 @@ public class PdbAnalyzer extends AbstractAnalyzer {
}
/**
* Sets the "allow remote" option that will be used by the analyzer when it is next invoked
* Sets the "allow untrusted" option that will be used by the analyzer when it is next invoked
* on the specified program.
* <p>
* Normally when the analyzer attempts to locate a matching PDB file it
* will default to NOT searching remote symbol servers. A headless script could
* use this method to allow the analyzer to search remote symbol servers.
* will default to NOT searching untrusted symbol servers. A headless script could
* use this method to allow the analyzer to search untrusted symbol servers.
*
* @param program {@link Program}
* @param allowRemote boolean flag, true means analyzer can search remote symbol
* @param allowUntrusted boolean flag, true means analyzer can search untrusted symbol
* servers
*/
public static void setAllowRemoteOption(Program program, boolean allowRemote) {
PdbAnalyzerCommon.setAllowRemoteOption(NAME, program, allowRemote);
public static void setAllowUntrustedOption(Program program, boolean allowUntrusted) {
PdbAnalyzerCommon.setAllowUntrustedOption(NAME, program, allowUntrusted);
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -33,9 +33,9 @@ import pdb.symbolserver.SymbolFileInfo;
* Shared configuration values and pdb searching logic
*/
public class PdbAnalyzerCommon {
static final String OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS =
"If checked, allow searching remote symbol servers for PDB files.";
static final String OPTION_NAME_SEARCH_REMOTE_LOCATIONS = "Search remote symbol servers";
static final String OPTION_DESCRIPTION_SEARCH_UNTRUSTED_LOCATIONS =
"If checked, allow searching untrusted symbol servers for PDB files.";
static final String OPTION_NAME_SEARCH_UNTRUSTED_LOCATIONS = "Search untrusted symbol servers";
static final String OPTION_DESCRIPTION_PDB_FILE = "Path to a manually chosen PDB file.";
static final String OPTION_NAME_PDB_FILE = "PDB File";
@ -101,12 +101,13 @@ public class PdbAnalyzerCommon {
*
* @param analyzerName name of analyzer
* @param program {@link Program}
* @param allowRemote boolean flag, true means the analyzer can search remote
* @param allowUntrusted boolean flag, true means the analyzer can search remote
* symbol servers
*/
static void setAllowRemoteOption(String analyzerName, Program program, boolean allowRemote) {
static void setAllowUntrustedOption(String analyzerName, Program program, boolean allowUntrusted) {
Options options = program.getOptions(Program.ANALYSIS_PROPERTIES);
options.setBoolean(analyzerName + "." + OPTION_NAME_SEARCH_REMOTE_LOCATIONS, allowRemote);
options.setBoolean(analyzerName + "." + OPTION_NAME_SEARCH_UNTRUSTED_LOCATIONS,
allowUntrusted);
}
/**
@ -141,7 +142,7 @@ public class PdbAnalyzerCommon {
: null;
if (pdbFile == null) {
Set<FindOption> findOpts = allowRemote
? FindOption.of(FindOption.ALLOW_REMOTE)
? FindOption.of(FindOption.ALLOW_UNTRUSTED)
: FindOption.NO_OPTIONS;
pdbFile = PdbPlugin.findPdb(program, findOpts, monitor);
}

View File

@ -4,9 +4,9 @@
* 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.
@ -83,7 +83,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
private File DEFAULT_FORCE_LOAD_FILE = new File(DEFAULT_SYMBOLS_DIR, "sample.pdb");
private File forceLoadFile;
private boolean searchRemoteLocations = false;
private boolean searchUntrustedLocations = false;
//==============================================================================================
// Additional instance data
@ -164,7 +164,7 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
pdbFile = forceLoadFile;
}
else {
pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchRemoteLocations, monitor);
pdbFile = PdbAnalyzerCommon.findPdb(this, program, searchUntrustedLocations, monitor);
}
if (pdbFile == null) {
// warnings have already been logged
@ -262,9 +262,9 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
options.registerOption(OPTION_NAME_FORCELOAD_FILE, OptionType.FILE_TYPE,
DEFAULT_FORCE_LOAD_FILE, null, OPTION_DESCRIPTION_FORCELOAD_FILE);
}
options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS,
searchRemoteLocations, null,
PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_REMOTE_LOCATIONS);
options.registerOption(PdbAnalyzerCommon.OPTION_NAME_SEARCH_UNTRUSTED_LOCATIONS,
searchUntrustedLocations, null,
PdbAnalyzerCommon.OPTION_DESCRIPTION_SEARCH_UNTRUSTED_LOCATIONS);
pdbReaderOptions.registerOptions(options);
pdbApplicatorOptions.registerAnalyzerOptions(options);
@ -281,8 +281,8 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
forceLoadFile = options.getFile(OPTION_NAME_FORCELOAD_FILE, forceLoadFile);
}
searchRemoteLocations = options.getBoolean(
PdbAnalyzerCommon.OPTION_NAME_SEARCH_REMOTE_LOCATIONS, searchRemoteLocations);
searchUntrustedLocations = options.getBoolean(
PdbAnalyzerCommon.OPTION_NAME_SEARCH_UNTRUSTED_LOCATIONS, searchUntrustedLocations);
pdbReaderOptions.loadOptions(options);
pdbApplicatorOptions.loadAnalyzerOptions(options);
@ -312,19 +312,19 @@ public class PdbUniversalAnalyzer extends AbstractAnalyzer {
}
/**
* Sets the "allow remote" option that will be used by the analyzer when it is next invoked
* Sets the "allow untrusted" option that will be used by the analyzer when it is next invoked
* on the specified program.
* <p>
* Normally when the analyzer attempts to locate a matching PDB file it
* will default to NOT searching remote symbol servers. A headless script could
* use this method to allow the analyzer to search remote symbol servers.
* will default to NOT searching untrusted symbol servers. A headless script could
* use this method to allow the analyzer to search untrusted symbol servers.
*
* @param program {@link Program}
* @param allowRemote boolean flag, true means analyzer can search remote symbol
* @param allowUntrusted boolean flag, true means analyzer can search remote symbol
* servers
*/
public static void setAllowRemoteOption(Program program, boolean allowRemote) {
PdbAnalyzerCommon.setAllowRemoteOption(NAME, program, allowRemote);
public static void setAllowUntrustedOption(Program program, boolean allowUntrusted) {
PdbAnalyzerCommon.setAllowUntrustedOption(NAME, program, allowUntrusted);
}
//==============================================================================================

View File

@ -4,9 +4,9 @@
* 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.
@ -136,11 +136,6 @@ public class ContainerFileSymbolServer implements SymbolServer {
return fsFSRL.withPath(filename).toPrettyFullpathString();
}
@Override
public boolean isLocal() {
return true;
}
@Override
public String toString() {
return "ContainerFileSymbolServer: [ fsrl: %s ]".formatted(fsFSRL);

View File

@ -4,9 +4,9 @@
* 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.
@ -114,11 +114,6 @@ public class DisabledSymbolServer implements SymbolServer {
return delegate.getFileLocation(filename);
}
@Override
public boolean isLocal() {
return delegate.isLocal();
}
@Override
public String toString() {
return String.format("DisabledSymbolServer: [ %s ]", delegate.toString());

View File

@ -4,9 +4,9 @@
* 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.
@ -15,18 +15,16 @@
*/
package pdb.symbolserver;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* Options that control how Pdb files are searched for on a SymbolServer.
*/
public enum FindOption {
/**
* Allow connections to remote symbol servers
* Allow connections to untrusted symbol servers
*/
ALLOW_REMOTE,
ALLOW_UNTRUSTED,
/**
* Only return the first result
*/

View File

@ -4,9 +4,9 @@
* 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.
@ -25,23 +25,31 @@ import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ghidra.net.HttpClients;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.CancelledListener;
import ghidra.util.task.TaskMonitor;
import pdb.symbolserver.SymbolServer.MutableTrust;
/**
* A {@link SymbolServer} that is accessed via HTTP.
* <p>
*
*/
public class HttpSymbolServer extends AbstractSymbolServer {
public class HttpSymbolServer extends AbstractSymbolServer implements MutableTrust {
private static final String GHIDRA_USER_AGENT = "Ghidra_HttpSymbolServer_client";
private static final int HTTP_STATUS_OK = HttpURLConnection.HTTP_OK;
private static final int HTTP_REQUEST_TIMEOUT_MS = 10 * 1000; // 10 seconds
/**
* pattern to match an optional "!" in front of a typical url string
*/
private static final Pattern NAMEPAT = Pattern.compile("(\\!?)(http(s?)://.*)");
/**
* Predicate that tests if the location string is an instance of a HttpSymbolServer location.
*
@ -49,10 +57,48 @@ public class HttpSymbolServer extends AbstractSymbolServer {
* @return boolean true if the string should be handled by the HttpSymbolServer class
*/
public static boolean isHttpSymbolServerLocation(String locationString) {
return locationString.startsWith("http://") || locationString.startsWith("https://");
return NAMEPAT.matcher(locationString).matches();
}
/**
* Creates a new HttpSymbolServer instance from a locationString.
*
* @param locationString string previously returned by {@link #getName()}
* @param context {@link SymbolServerInstanceCreatorContext}
* @return new instance
*/
public static SymbolServer createInstance(String locationString,
SymbolServerInstanceCreatorContext context) {
Matcher m = NAMEPAT.matcher(locationString);
if (!m.matches()) {
return null;
}
boolean isTrusted = "!".equals(m.group(1));
String url = m.group(2);
return new HttpSymbolServer(URI.create(url), isTrusted);
}
/**
* Create a trusted http symbol server
*
* @param url string url
* @return new {@link HttpSymbolServer} instance
*/
public static HttpSymbolServer createTrusted(String url) {
return new HttpSymbolServer(URI.create(url), true);
}
/**
* Create an untrusted http symbol server
* @param url string url
* @return new {@link HttpSymbolServer} instance
*/
public static HttpSymbolServer createUntrusted(String url) {
return new HttpSymbolServer(URI.create(url), false);
}
private final URI serverURI;
private boolean trusted;
/**
* Creates a new instance of a HttpSymbolServer.
@ -60,13 +106,29 @@ public class HttpSymbolServer extends AbstractSymbolServer {
* @param serverURI URI / URL of the symbol server
*/
public HttpSymbolServer(URI serverURI) {
this(serverURI, false);
}
/**
* Creates a new instance of a HttpSymbolServer.
*
* @param serverURI URI / URL of the symbol server
* @param isTrusted flag, if true the the http server can be trusted when querying and downloading
*/
public HttpSymbolServer(URI serverURI, boolean isTrusted) {
String path = serverURI.getPath();
this.serverURI =
path.endsWith("/") ? serverURI : serverURI.resolve(serverURI.getPath() + "/");
this.trusted = isTrusted;
}
@Override
public String getName() {
return (trusted ? "!" : "") + serverURI.toString();
}
@Override
public String getDescriptiveName() {
return serverURI.toString();
}
@ -170,14 +232,19 @@ public class HttpSymbolServer extends AbstractSymbolServer {
}
@Override
public boolean isLocal() {
return false;
public boolean isTrusted() {
return trusted;
}
@Override
public void setTrusted(boolean isTrusted) {
this.trusted = isTrusted;
}
@Override
public String toString() {
return String.format("HttpSymbolServer: [ url: %s, storageLevel: %d]", serverURI.toString(),
storageLevel);
return String.format("HttpSymbolServer: [ url: %s, trusted: %b, storageLevel: %d]",
serverURI.toString(), trusted, storageLevel);
}
private String logPrefix() {

View File

@ -4,9 +4,9 @@
* 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.
@ -371,11 +371,6 @@ public class LocalSymbolStore extends AbstractSymbolServer implements SymbolStor
return new SymbolServerInputStream(new FileInputStream(file), file.length());
}
@Override
public boolean isLocal() {
return true;
}
@Override
public String toString() {
return String.format("LocalSymbolStore: [ rootDir: %s, storageLevel: %d]",

View File

@ -4,9 +4,9 @@
* 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.
@ -20,6 +20,7 @@ import java.util.*;
import ghidra.formats.gfilesystem.FSRL;
import ghidra.util.task.TaskMonitor;
import pdb.symbolserver.SymbolServer.StatusRequiresContext;
/**
* A Pdb symbol server / symbol store, similar to the {@link LocalSymbolStore},
@ -27,7 +28,7 @@ import ghidra.util.task.TaskMonitor;
* <p>
*
*/
public class SameDirSymbolStore implements SymbolStore {
public class SameDirSymbolStore implements SymbolStore, StatusRequiresContext {
/**
* Descriptive string
@ -166,11 +167,6 @@ public class SameDirSymbolStore implements SymbolStore {
return getFile(filename).getPath();
}
@Override
public boolean isLocal() {
return true;
}
@Override
public String toString() {
return String.format("SameDirSymbolStore: [ dir: %s ]", rootDir);

View File

@ -4,9 +4,9 @@
* 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.
@ -17,8 +17,7 @@ package pdb.symbolserver;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
import java.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -29,6 +28,27 @@ import ghidra.util.task.TaskMonitor;
*
*/
public interface SymbolServer {
/**
* Optional add-on interface for {@link SymbolServer}s that flag server types as requiring a
* valid context object to be queried for {@link SymbolServer#isValid(TaskMonitor)}
*/
interface StatusRequiresContext {
// empty
}
/**
* Optional add-on interface for {@link SymbolServer}s that allow their trusted-ness value to
* be modified.
*/
public interface MutableTrust {
/**
* Sets the trusted attribute of this symbol server.
*
* @param isTrusted boolean flag, if true this symbolserver will be marked as trusted
*/
void setTrusted(boolean isTrusted);
}
/**
* Name of the symbol server, suitable to use as the identity of this instance,
@ -55,6 +75,16 @@ public interface SymbolServer {
*/
boolean isValid(TaskMonitor monitor);
/**
* Returns true if this {@link SymbolServer} is 'trusted', meaning
* it can be searched without security issues / warning the user.
*
* @return boolean true if this symbolserver is trusted, false if untrusted
*/
default boolean isTrusted() {
return true;
}
/**
* Returns true if the raw filename exists in the symbol server.
*
@ -103,10 +133,13 @@ public interface SymbolServer {
String getFileLocation(String filename);
/**
* Returns true if this {@link SymbolServer} is 'local', meaning
* it can be searched without security issues / warning the user.
* Returns the number of configured symbol servers that are considered 'untrusted'.
*
* @return boolean true if this symbolserver is 'local', false if remote
* @param symbolServers list of {@link SymbolServer}s
* @return number of untrusted symbol servers
*/
boolean isLocal();
static int getUntrustedCount(Collection<SymbolServer> symbolServers) {
return (int) symbolServers.stream().filter(ss -> !ss.isTrusted()).count();
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -16,7 +16,6 @@
package pdb.symbolserver;
import java.io.File;
import java.net.URI;
import java.util.*;
import java.util.function.Predicate;
@ -167,7 +166,7 @@ public class SymbolServerInstanceCreatorRegistry {
registerSymbolServerInstanceCreator(0, DisabledSymbolServer::isDisabledSymbolServerLocation,
DisabledSymbolServer::createInstance);
registerSymbolServerInstanceCreator(100, HttpSymbolServer::isHttpSymbolServerLocation,
(loc, context) -> new HttpSymbolServer(URI.create(loc)));
HttpSymbolServer::createInstance);
registerSymbolServerInstanceCreator(200, SameDirSymbolStore::isSameDirLocation,
SameDirSymbolStore::createInstance);
registerSymbolServerInstanceCreator(300, LocalSymbolStore::isLocalSymbolStoreLocation,

View File

@ -4,9 +4,9 @@
* 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.
@ -15,11 +15,10 @@
*/
package pdb.symbolserver;
import java.util.*;
import java.util.stream.Collectors;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
@ -31,8 +30,7 @@ import pdb.PdbUtils;
/**
* A (lowercase-'S') service that searches for and fetches symbol files
* from a set of local and remote {@link SymbolServer symbolservers}. (not to be
* confused with a Plugin service)
* from a set of {@link SymbolServer symbolservers}. (not to be confused with a Plugin service)
* <p>
* Instances of this class are meant to be easily created when needed
* and just as easily thrown away when not used or when the search
@ -50,9 +48,8 @@ public class SymbolServerService {
/**
* Creates a new SymbolServerService instance.
* <p>
* @param symbolStore a {@link SymbolStore} - where all
* remote files are placed when downloaded. Also treated as a SymbolServer
* and searched first
* @param symbolStore a {@link SymbolStore} - where all remote files are placed when
* downloaded. Also treated as a SymbolServer and searched first
* @param symbolServers a list of {@link SymbolServer symbol servers} - searched in order
*/
public SymbolServerService(SymbolStore symbolStore, List<SymbolServer> symbolServers) {
@ -91,19 +88,6 @@ public class SymbolServerService {
return new ArrayList<>(symbolServers.subList(1, symbolServers.size()));
}
/**
* Returns the number of configured symbol servers that are considered 'remote'.
* @return number of remote symbol servers
*/
public int getRemoteSymbolServerCount() {
int remoteSymbolServerCount = (int) getSymbolServers()
.stream()
.filter(ss -> !ss.isLocal())
.count();
return remoteSymbolServerCount;
}
/**
* Searches all {@link SymbolServer symbol servers} for a matching pdb symbol file.
*
@ -150,9 +134,9 @@ public class SymbolServerService {
for_each_symbol_server_loop: for (SymbolServer symbolServer : symbolServers) {
monitor.checkCancelled();
if (!symbolServer.isLocal() && !findOptions.contains(FindOption.ALLOW_REMOTE)) {
if (!symbolServer.isTrusted() && !findOptions.contains(FindOption.ALLOW_UNTRUSTED)) {
Msg.debug(this,
logPrefix() + ": skipping non-local symbol server " +
logPrefix() + ": skipping untrusted symbol server " +
symbolServer.getDescriptiveName());
continue;
}

View File

@ -4,9 +4,9 @@
* 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.
@ -15,11 +15,37 @@
*/
package pdb.symbolserver.ui;
import java.util.List;
import static pdb.symbolserver.ui.SymbolServerRow.LocationStatus.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.swing.*;
import docking.DialogComponentProvider;
import docking.DockingWindowManager;
import docking.widgets.OptionDialog;
import docking.widgets.button.BrowseButton;
import docking.widgets.button.GButton;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import docking.widgets.label.GHtmlLabel;
import docking.widgets.label.GLabel;
import docking.widgets.table.GTable;
import docking.widgets.textfield.HintTextField;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.framework.preferences.Preferences;
import ghidra.util.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.layout.PairLayout;
import ghidra.util.task.*;
import pdb.PdbPlugin;
import pdb.symbolserver.*;
import resources.Icons;
import utilities.util.FileUtilities;
/**
* Dialog that allows the user to configure the Pdb search locations and symbol directory
@ -32,13 +58,40 @@ public class ConfigPdbDialog extends DialogComponentProvider {
return choosePdbDialog.wasSuccess;
}
private static final String MS_SYMBOLSERVER_ENVVAR = "_NT_SYMBOL_PATH";
private static final Dimension BUTTON_SIZE = new Dimension(32, 32);
private List<WellKnownSymbolServerLocation> knownSymbolServers =
WellKnownSymbolServerLocation.loadAll();
private SymbolStore localSymbolStore;
private SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext =
SymbolServerInstanceCreatorRegistry.getInstance().getContext();
private SymbolServerTableModel tableModel;
private SymbolServerPanel symbolServerConfigPanel;
private boolean wasSuccess;
private boolean configChanged;
public ConfigPdbDialog() {
super("Configure Symbol Server Search", true, false, true, false);
super("Configure Symbol Server Search", true, false, true, true);
build();
tableModel.addTableModelListener(e -> updateButtonEnablement());
setupInitialSymbolServer();
}
private void setupInitialSymbolServer() {
SymbolServerService temporarySymbolServerService =
PdbPlugin.getSymbolServerService(symbolServerInstanceCreatorContext);
if (temporarySymbolServerService
.getSymbolStore() instanceof LocalSymbolStore tempLocalSymbolStore) {
setSymbolStorageLocation(tempLocalSymbolStore.getRootDir(), false);
tableModel.addSymbolServers(temporarySymbolServerService.getSymbolServers());
setConfigChanged(false);
}
}
@Override
@ -48,27 +101,33 @@ public class ConfigPdbDialog extends DialogComponentProvider {
@Override
protected void okCallback() {
if (symbolServerConfigPanel.isConfigChanged()) {
symbolServerConfigPanel.saveConfig();
if (isConfigChanged()) {
saveConfig();
}
wasSuccess = true;
close();
}
@Override
protected void dialogShown() {
TableColumnInitializer.initializeTableColumns(symbolServerConfigPanel.table, tableModel);
symbolServerConfigPanel.refreshSymbolServerLocationStatus(true /* only query trusted */);
}
private void build() {
symbolServerConfigPanel = new SymbolServerPanel(this::onSymbolServerServiceChange,
SymbolServerInstanceCreatorRegistry.getInstance().getContext());
tableModel = new SymbolServerTableModel();
symbolServerConfigPanel = new SymbolServerPanel();
addButtons();
addWorkPanel(symbolServerConfigPanel);
setRememberSize(false);
okButton.setEnabled(symbolServerConfigPanel.getSymbolServerService() != null);
setMinimumSize(400, 250);
okButton.setEnabled(hasSymbolServer());
}
private void onSymbolServerServiceChange(SymbolServerService newService) {
okButton.setEnabled(newService != null);
rootPanel.revalidate();
private void updateButtonEnablement() {
okButton.setEnabled(hasSymbolServer());
symbolServerConfigPanel.updatePanelButtonEnablement();
}
private void addButtons() {
@ -81,7 +140,16 @@ public class ConfigPdbDialog extends DialogComponentProvider {
* Screen shot usage only
*/
public void pushAddLocationButton() {
symbolServerConfigPanel.pushAddLocationButton();
symbolServerConfigPanel.addLocation();
}
/**
* Screen shot usage only
*
* @param list fake well known symbol servers
*/
public void setWellknownSymbolServers(List<WellKnownSymbolServerLocation> list) {
knownSymbolServers = list;
}
/**
@ -90,9 +158,520 @@ public class ConfigPdbDialog extends DialogComponentProvider {
* @param fakeDirectoryText fake text to display in the storage directory text field
* @param symbolServers list of symbol servers to force set
*/
public void setSymbolServerService(String fakeDirectoryText,
List<SymbolServer> symbolServers) {
symbolServerConfigPanel.setSymbolServers(symbolServers);
symbolServerConfigPanel.setSymbolStorageDirectoryTextOnly(fakeDirectoryText);
public void setSymbolServerService(String fakeDirectoryText, List<SymbolServer> symbolServers) {
setSymbolServers(symbolServers);
setSymbolStorageLocationPath(fakeDirectoryText);
}
private void setSymbolStorageLocationPath(String path) {
symbolServerConfigPanel.symbolStorageLocationTextField.setText(path);
}
/**
* Returns a new {@link SymbolServerService} instance representing the currently
* displayed configuration, or null if the displayed configuration is not valid.
*
* @return new {@link SymbolServerService} or null
*/
SymbolServerService getSymbolServerService() {
return (localSymbolStore != null)
? new SymbolServerService(localSymbolStore, tableModel.getSymbolServers())
: null;
}
boolean hasSymbolServer() {
return localSymbolStore != null;
}
void setSymbolServers(List<SymbolServer> symbolServers) {
tableModel.setSymbolServers(symbolServers);
}
private void setSymbolStorageLocation(File symbolStorageDir, boolean allowGUIPrompt) {
if (symbolStorageDir == null) {
return;
}
if (!symbolStorageDir.exists()) {
if (!allowGUIPrompt) {
return;
}
int opt =
OptionDialog.showOptionDialog(rootPanel, "Create Local Symbol Storage Directory?",
"<html>Symbol storage directory<br>" +
HTMLUtilities.escapeHTML(symbolStorageDir.getPath()) +
"<br>does not exist. Create?",
"Yes", OptionDialog.QUESTION_MESSAGE);
if (opt == OptionDialog.CANCEL_OPTION) {
return;
}
try {
FileUtilities.checkedMkdirs(symbolStorageDir);
}
catch (IOException e) {
Msg.showError(this, rootPanel, "Failure",
"Failed to create symbol storage directory %s: %s".formatted(symbolStorageDir,
e.getMessage()));
return;
}
}
if (allowGUIPrompt && isEmptyDirectory(symbolStorageDir)) {
if (OptionDialog.showYesNoDialog(rootPanel, "Initialize Symbol Storage Directory?",
"<html>Initialize new directory as Microsoft symbol storage directory?<br>" +
"(Answer <b>No</b> to leave as unorganized storage directory)") == OptionDialog.YES_OPTION) {
try {
LocalSymbolStore.create(symbolStorageDir,
1 /* level1 MS symbol storage directory */);
}
catch (IOException e) {
Msg.showError(this, rootPanel, "Initialize Failure",
"Failed to initialize symbol storage directory " + symbolStorageDir, e);
}
}
}
localSymbolStore =
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.newSymbolServer(symbolStorageDir.getPath(), symbolServerInstanceCreatorContext,
SymbolStore.class);
setSymbolStorageLocationPath(symbolStorageDir.getPath());
updateButtonEnablement();
}
void executeMonitoredRunnable(String taskTitle, boolean canCancel, boolean hasProgress,
int delay, MonitoredRunnable runnable) {
Task task = new Task(taskTitle, canCancel, hasProgress, false) {
@Override
public void run(TaskMonitor monitor) throws CancelledException {
runnable.monitoredRun(monitor);
}
};
executeProgressTask(task, delay);
}
/**
* The union of the changed status of the local storage path and the additional
* search paths table model changed status.
*
* @return boolean true if the config has changed
*/
boolean isConfigChanged() {
return configChanged || tableModel.isDataChanged();
}
void setConfigChanged(boolean configChanged) {
this.configChanged = configChanged;
tableModel.setDataChanged(configChanged);
}
/* package */ void saveConfig() {
SymbolServerService temporarySymbolServerService = getSymbolServerService();
if (temporarySymbolServerService != null) {
PdbPlugin.saveSymbolServerServiceConfig(temporarySymbolServerService);
Preferences.store();
setConfigChanged(false);
updateButtonEnablement();
}
}
//---------------------------------------------------------------------------------------------
class SymbolServerPanel extends JPanel {
private GTable table;
private JPanel additionalSearchLocationsPanel;
private JPanel defaultConfigNotice;
private JButton refreshSearchLocationsStatusButton;
private JButton moveLocationUpButton;
private JButton moveLocationDownButton;
private JButton deleteLocationButton;
private JButton addLocationButton;
private JPanel symbolStorageLocationPanel;
private HintTextField symbolStorageLocationTextField;
private JButton chooseSymbolStorageLocationButton;
private JButton saveSearchLocationsButton;
SymbolServerPanel() {
build();
DockingWindowManager.getHelpService()
.registerHelp(this,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config"));
}
private void build() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createTitledBorder("Symbol Server Search Config"));
buildSymbolStorageLocationPanel();
JPanel tableButtonPanel = buildButtonPanel();
JScrollPane tableScrollPane = buildTable();
defaultConfigNotice = new JPanel();
GHtmlLabel label = new GHtmlLabel("<html><center><font color=\"" +
Messages.ERROR.toHexString() + "\"><br>Missing / invalid configuration.<br><br>" +
"Using default search location:<br>Program's Import Location<br>");
label.setHorizontalAlignment(SwingConstants.CENTER);
defaultConfigNotice.add(label);
defaultConfigNotice.setPreferredSize(tableScrollPane.getPreferredSize());
additionalSearchLocationsPanel = new JPanel();
additionalSearchLocationsPanel
.setLayout(new BoxLayout(additionalSearchLocationsPanel, BoxLayout.Y_AXIS));
additionalSearchLocationsPanel.add(tableButtonPanel);
additionalSearchLocationsPanel.add(tableScrollPane);
add(symbolStorageLocationPanel, BorderLayout.NORTH);
add(additionalSearchLocationsPanel, BorderLayout.CENTER);
}
private void updateLayout(boolean showTable) {
if (showTable == (additionalSearchLocationsPanel.getParent() != null)) {
return;
}
remove(additionalSearchLocationsPanel);
remove(defaultConfigNotice);
add(showTable ? additionalSearchLocationsPanel : defaultConfigNotice,
BorderLayout.CENTER);
invalidate();
repaint();
}
void refreshSymbolServerLocationStatus(boolean trustedOnly) {
executeMonitoredRunnable("Refresh Symbol Server Location Status", true, true, 0,
monitor -> {
List<SymbolServerRow> rowsCopy = new ArrayList<>(tableModel.getModelData());
monitor.initialize(rowsCopy.size(), "Refreshing symbol server status");
try {
for (SymbolServerRow row : rowsCopy) {
if (monitor.isCancelled()) {
break;
}
monitor.setMessage("Checking " + row.getSymbolServer().getName());
monitor.incrementProgress();
SymbolServer symbolServer = row.getSymbolServer();
if (symbolServer instanceof SymbolServer.StatusRequiresContext || // we don't have program context here in the config dialog
(trustedOnly && !symbolServer.isTrusted())) {
continue;
}
row.setStatus(symbolServer.isValid(monitor) ? VALID : INVALID);
}
}
finally {
Swing.runLater(() -> tableModel.fireTableDataChanged());
}
});
}
private JScrollPane buildTable() {
table = new GTable(tableModel);
table.setVisibleRowCount(4);
table.setUserSortingEnabled(false);
table.getSelectionManager()
.addListSelectionListener(e -> updatePanelButtonEnablement());
table.setPreferredScrollableViewportSize(new Dimension(500, 100));
return new JScrollPane(table);
}
private JPanel buildButtonPanel() {
refreshSearchLocationsStatusButton = createImageButton(Icons.REFRESH_ICON,
"Refresh Status", "SymbolServerConfig Refresh Status");
refreshSearchLocationsStatusButton.addActionListener(
e -> refreshSymbolServerLocationStatus(false /* query all */));
moveLocationUpButton =
createImageButton(Icons.UP_ICON, "Up", "SymbolServerConfig MoveUpDown");
moveLocationUpButton.addActionListener(e -> moveLocation(-1));
moveLocationUpButton.setToolTipText("Move location up");
moveLocationDownButton =
createImageButton(Icons.DOWN_ICON, "Down", "SymbolServerConfig MoveUpDown");
moveLocationDownButton.addActionListener(e -> moveLocation(1));
moveLocationDownButton.setToolTipText("Move location down");
deleteLocationButton =
createImageButton(Icons.DELETE_ICON, "Delete", "SymbolServerConfig Delete");
deleteLocationButton.addActionListener(e -> deleteLocation());
addLocationButton = createImageButton(Icons.ADD_ICON, "Add", "SymbolServerConfig Add");
addLocationButton.addActionListener(e -> addLocation());
saveSearchLocationsButton =
createImageButton(Icons.SAVE_ICON, "Save Configuration", "SymbolServerConfig Save");
saveSearchLocationsButton.addActionListener(e -> saveConfig());
JPanel tableButtonPanel = new JPanel();
tableButtonPanel.setLayout(new BoxLayout(tableButtonPanel, BoxLayout.X_AXIS));
tableButtonPanel.add(new GLabel("Additional Search Paths:"));
tableButtonPanel.add(Box.createHorizontalGlue());
tableButtonPanel.add(addLocationButton);
tableButtonPanel.add(deleteLocationButton);
tableButtonPanel.add(moveLocationUpButton);
tableButtonPanel.add(moveLocationDownButton);
tableButtonPanel.add(refreshSearchLocationsStatusButton);
tableButtonPanel.add(saveSearchLocationsButton);
return tableButtonPanel;
}
private JPanel buildSymbolStorageLocationPanel() {
symbolStorageLocationTextField = new HintTextField(" Required ");
symbolStorageLocationTextField.setEditable(false);
symbolStorageLocationTextField.setToolTipText(
"User-specified directory where PDB files are stored. Required.");
chooseSymbolStorageLocationButton = new BrowseButton();
chooseSymbolStorageLocationButton.addActionListener(e -> chooseSymbolStorageLocation());
symbolStorageLocationPanel = new JPanel(new PairLayout(5, 5));
GLabel symbolStorageLocLabel =
new GLabel("Local Symbol Storage:", SwingConstants.RIGHT);
symbolStorageLocLabel.setToolTipText(symbolStorageLocationTextField.getToolTipText());
symbolStorageLocationPanel.add(symbolStorageLocLabel);
symbolStorageLocationPanel.add(LoadPdbDialog.join(null, symbolStorageLocationTextField,
chooseSymbolStorageLocationButton));
return symbolStorageLocationPanel;
}
private void updatePanelButtonEnablement() {
boolean hasLocalSymbolStore = localSymbolStore != null;
boolean singleRow = table.getSelectedRowCount() == 1;
boolean moreThanOneRow = table.getRowCount() > 1;
refreshSearchLocationsStatusButton
.setEnabled(hasLocalSymbolStore && !tableModel.isEmpty());
moveLocationUpButton.setEnabled(hasLocalSymbolStore && singleRow && moreThanOneRow);
moveLocationDownButton.setEnabled(hasLocalSymbolStore && singleRow && moreThanOneRow);
addLocationButton.setEnabled(hasLocalSymbolStore);
deleteLocationButton.setEnabled(hasLocalSymbolStore && table.getSelectedRowCount() > 0);
saveSearchLocationsButton.setEnabled(hasLocalSymbolStore && isConfigChanged());
updateLayout(hasLocalSymbolStore);
}
private void chooseSymbolStorageLocation() {
GhidraFileChooser chooser = getChooser();
File f = chooser.getSelectedFile();
chooser.dispose();
if (f != null) {
configChanged = true;
setSymbolStorageLocation(f, true);
updateButtonEnablement();
}
}
private void importLocations() {
String envVar = (String) JOptionPane.showInputDialog(this,
"<html>Enter value:<br><br>Example: SVR*c:\\symbols*https://msdl.microsoft.com/download/symbols/<br><br>",
"Enter Symbol Server Search Path Value", JOptionPane.QUESTION_MESSAGE, null, null,
Objects.requireNonNullElse(System.getenv(MS_SYMBOLSERVER_ENVVAR), ""));
if (envVar == null) {
return;
}
List<String> symbolServerPaths = getSymbolPathsFromEnvStr(envVar);
if (!symbolServerPaths.isEmpty()) {
// if the first item in the path list looks like a local symbol storage path,
// allow the user to set it as the storage dir (and remove it from the elements
// that will be added to the search list)
String firstSearchPath = symbolServerPaths.get(0);
SymbolServer symbolServer =
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.newSymbolServer(firstSearchPath, symbolServerInstanceCreatorContext);
if (symbolServer instanceof LocalSymbolStore localSymbolStore &&
localSymbolStore.isValid()) {
int choice =
OptionDialog.showYesNoCancelDialog(this, "Set Symbol Storage Location",
"Set symbol storage location to " + firstSearchPath + "?");
if (choice == OptionDialog.CANCEL_OPTION) {
return;
}
if (choice == OptionDialog.YES_OPTION) {
symbolServerPaths.remove(0);
configChanged = true;
setSymbolStorageLocation(localSymbolStore.getRootDir(), true);
}
}
}
tableModel.addSymbolServers(
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.createSymbolServersFromPathList(symbolServerPaths,
symbolServerInstanceCreatorContext));
updateButtonEnablement();
}
private void addLocation() {
JPopupMenu menu = createAddLocationPopupMenu();
menu.show(addLocationButton, 0, 0);
}
private JPopupMenu createAddLocationPopupMenu() {
JPopupMenu menu = new JPopupMenu();
JMenuItem addDirMenuItem = new JMenuItem("Directory");
addDirMenuItem.addActionListener(e -> addDirectoryLocation());
menu.add(addDirMenuItem);
JMenuItem addURLMenuItem = new JMenuItem("URL");
addURLMenuItem.addActionListener(e -> addUrlLocation());
menu.add(addURLMenuItem);
JMenuItem addProgLocMenuItem =
new JMenuItem(SameDirSymbolStore.PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR);
addProgLocMenuItem.addActionListener(e -> addSameDirLocation());
menu.add(addProgLocMenuItem);
JMenuItem importEnvMenuItem = new JMenuItem("Import _NT_SYMBOL_PATH");
importEnvMenuItem.addActionListener(e -> importLocations());
menu.add(importEnvMenuItem);
if (!knownSymbolServers.isEmpty()) {
menu.add(new JSeparator());
for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) {
JMenuItem mi = new JMenuItem(ssloc.location());
mi.addActionListener(e -> addKnownLocation(ssloc));
mi.setToolTipText(" [from " + ssloc.fileOrigin() + "]");
menu.add(mi);
}
}
DockingWindowManager.getHelpService()
.registerHelp(menu, new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC,
"SymbolServerConfig_Add"));
return menu;
}
private void addSameDirLocation() {
SameDirSymbolStore sameDirSymbolStore =
new SameDirSymbolStore(symbolServerInstanceCreatorContext.getRootDir());
tableModel.addSymbolServer(sameDirSymbolStore);
}
private void addKnownLocation(WellKnownSymbolServerLocation ssloc) {
SymbolServer symbolServer =
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.newSymbolServer(ssloc.location(), symbolServerInstanceCreatorContext);
if (symbolServer != null) {
tableModel.addSymbolServer(symbolServer);
}
}
private void addUrlLocation() {
String urlLocationString = OptionDialog.showInputSingleLineDialog(this, "Enter URL",
"Enter the URL of a Symbol Server: ", "https://");
if (urlLocationString == null || urlLocationString.isBlank()) {
return;
}
urlLocationString = urlLocationString.toLowerCase();
if (!(urlLocationString.startsWith("http://") ||
urlLocationString.startsWith("https://"))) {
Msg.showWarn(this, this, "Bad URL", "Invalid URL: " + urlLocationString);
return;
}
try {
HttpSymbolServer httpSymbolServer =
HttpSymbolServer.createUntrusted(urlLocationString);
tableModel.addSymbolServer(httpSymbolServer);
}
catch (IllegalArgumentException e) {
Msg.showWarn(this, this, "Bad URL", "Invalid URL: " + urlLocationString);
}
}
private void addDirectoryLocation() {
File dir =
FilePromptDialog.chooseDirectory("Enter Path", "Symbol Storage Location: ", null);
if (dir == null) {
return;
}
if (!dir.exists() || !dir.isDirectory()) {
Msg.showError(this, this, "Bad path", "Invalid path: " + dir);
return;
}
LocalSymbolStore symbolStore = new LocalSymbolStore(dir);
tableModel.addSymbolServer(symbolStore);
}
private void deleteLocation() {
int selectedRow = table.getSelectedRow();
tableModel.deleteRows(table.getSelectedRows());
if (selectedRow >= 0 && selectedRow < table.getRowCount()) {
table.selectRow(selectedRow);
}
}
private void moveLocation(int delta) {
if (table.getSelectedRowCount() == 1) {
tableModel.moveRow(table.getSelectedRow(), delta);
}
}
private GhidraFileChooser getChooser() {
GhidraFileChooser chooser = new GhidraFileChooser(this);
chooser.setMultiSelectionEnabled(false);
chooser.setApproveButtonText("Choose");
chooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
chooser.setTitle("Select Symbol Storage Dir");
return chooser;
}
}
//---------------------------------------------------------------------------------------------
private static JButton createImageButton(Icon buttonIcon, String alternateText,
String helpLoc) {
JButton button = new GButton(buttonIcon);
button.setToolTipText(alternateText);
button.setPreferredSize(BUTTON_SIZE);
DockingWindowManager.getHelpService()
.registerHelp(button, new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, helpLoc));
return button;
}
/**
* Returns true if the given file path is a directory that contains no files.
* <p>
*
* @param directory path to a location on the file system
* @return true if is a directory and it contains no files
*/
private static boolean isEmptyDirectory(File directory) {
if (directory.isDirectory()) {
File[] dirContents = directory.listFiles();
return dirContents != null && dirContents.length == 0;
}
return false;
}
private static List<String> getSymbolPathsFromEnvStr(String envString) {
// Expect the environment string to be in the MS symbol server search path form:
// srv*[local cache]*[private symbol server]*https://msdl.microsoft.com/download/symbols
// srv*c:\symbols*https://msdl.microsoft.com/download/symbols;srv*c:\additional*https://symbol.server.tld/
String[] envParts = envString.split("[*;]");
List<String> results = new ArrayList<>();
Set<String> locationStringDeduplicationSet = new HashSet<>();
for (String envPart : envParts) {
String locationString = envPart.trim();
if (!locationString.isBlank() && !locationString.equalsIgnoreCase("srv") &&
!locationStringDeduplicationSet.contains(locationString)) {
results.add(locationString);
locationStringDeduplicationSet.add(locationString);
}
}
return results;
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -72,6 +72,8 @@ public class LoadPdbDialog extends DialogComponentProvider {
ExtensionFileFilter.forExtensions("Microsoft Program Databases", "pdb", "pd_", "pdb.xml");
private static final SymbolFileInfo UNKNOWN_SYMFILE = makeUnknownSymbolFileInstance("");
private static final List<WellKnownSymbolServerLocation> knownSymbolServers =
WellKnownSymbolServerLocation.loadAll();
public static class LoadPdbResults {
public File pdbFile;
@ -270,7 +272,7 @@ public class LoadPdbDialog extends DialogComponentProvider {
return SymbolFileInfo.fromValues(pdbPath, uid, age);
}
private void searchForPdbs(boolean allowRemote) {
private void searchForPdbs(boolean allowUntrusted) {
if (pdbAgeTextField.getText().isBlank() ||
pdbAgeTextField.getValue() > NumericUtilities.MAX_UNSIGNED_INT32_AS_LONG) {
Msg.showWarn(this, null, "Bad PDB Age", "Invalid PDB Age value");
@ -282,8 +284,8 @@ public class LoadPdbDialog extends DialogComponentProvider {
return;
}
Set<FindOption> findOptions = symbolFilePanel.getFindOptions();
if (allowRemote) {
findOptions.add(FindOption.ALLOW_REMOTE);
if (allowUntrusted) {
findOptions.add(FindOption.ALLOW_UNTRUSTED);
}
executeMonitoredRunnable("Search for PDBs", true, true, 0, monitor -> {
try {
@ -316,10 +318,11 @@ public class LoadPdbDialog extends DialogComponentProvider {
setHelpLocation(new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "Load PDB File"));
addStatusTextSupplier(() -> lastSearchOptions != null && advancedToggleButton.isSelected()
? SymbolServerPanel.getSymbolServerWarnings(symbolServerService.getSymbolServers())
? WellKnownSymbolServerLocation.getWarningsFor(knownSymbolServers,
symbolServerService.getSymbolServers())
: null);
addStatusTextSupplier(this::getSelectedPdbNoticeText);
addStatusTextSupplier(this::getAllowRemoteWarning);
addStatusTextSupplier(this::getAllowUntrustedWarning);
addStatusTextSupplier(this::getFoundCountInfo);
addButtons();
@ -600,12 +603,13 @@ public class LoadPdbDialog extends DialogComponentProvider {
}
}
private StatusText getAllowRemoteWarning() {
int remoteSymbolServerCount = symbolServerService.getRemoteSymbolServerCount();
private StatusText getAllowUntrustedWarning() {
int untrustedSymbolServerCount =
SymbolServer.getUntrustedCount(symbolServerService.getSymbolServers());
return lastSearchOptions != null && advancedToggleButton.isSelected() &&
remoteSymbolServerCount != 0 && !lastSearchOptions.contains(FindOption.ALLOW_REMOTE)
untrustedSymbolServerCount != 0 && !lastSearchOptions.contains(FindOption.ALLOW_UNTRUSTED)
? new StatusText(
"Remote servers were excluded. Use \"Search All\" button to also search remote servers.",
"Untrusted servers were excluded. Use \"Search All\" button to also include untrusted servers.",
MessageType.INFO, false)
: null;
}

View File

@ -4,9 +4,9 @@
* 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.
@ -39,7 +39,7 @@ import pdb.symbolserver.FindOption;
*/
class SymbolFilePanel extends JPanel {
interface SearchCallback {
void searchForPdbs(boolean allowRemote);
void searchForPdbs(boolean allowUntrusted);
}
static final String SEARCH_OPTIONS_HELP_ANCHOR = "PDB_Search_Search_Options";
@ -149,10 +149,10 @@ class SymbolFilePanel extends JPanel {
}
private JPanel buildButtonPanel() {
searchLocalButton = new JButton("Search Local");
searchLocalButton.setToolTipText("Search local symbol servers only.");
searchLocalButton = new JButton("Search");
searchLocalButton.setToolTipText("Search trusted symbol servers only.");
searchAllButton = new JButton("Search All");
searchAllButton.setToolTipText("Search local and remote symbol servers.");
searchAllButton.setToolTipText("Search trusted and untrusted symbol servers.");
ignorePdbUid = new GCheckBox("Ignore GUID/ID");
ignorePdbUid.setToolTipText(

View File

@ -1,590 +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 pdb.symbolserver.ui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.swing.*;
import javax.swing.table.TableColumn;
import docking.DockingWindowManager;
import docking.widgets.OptionDialog;
import docking.widgets.button.BrowseButton;
import docking.widgets.button.GButton;
import docking.widgets.filechooser.GhidraFileChooser;
import docking.widgets.filechooser.GhidraFileChooserMode;
import docking.widgets.label.GHtmlLabel;
import docking.widgets.label.GLabel;
import docking.widgets.table.GTable;
import docking.widgets.textfield.HintTextField;
import generic.theme.GThemeDefaults.Colors.Messages;
import ghidra.framework.preferences.Preferences;
import ghidra.util.*;
import ghidra.util.layout.PairLayout;
import pdb.PdbPlugin;
import pdb.symbolserver.*;
import pdb.symbolserver.ui.LoadPdbDialog.StatusText;
import resources.Icons;
import utilities.util.FileUtilities;
/**
* Panel that allows the user to configure a SymbolServerService: a local
* symbol storage directory and a list of search locations.
*/
class SymbolServerPanel extends JPanel {
private static final String MS_SYMBOLSERVER_ENVVAR = "_NT_SYMBOL_PATH";
private static final Dimension BUTTON_SIZE = new Dimension(32, 32);
private static List<WellKnownSymbolServerLocation> knownSymbolServers =
WellKnownSymbolServerLocation.loadAll();
private SymbolStore localSymbolStore;
private SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext;
private SymbolServerTableModel tableModel;
private GTable table;
private JPanel additionalSearchLocationsPanel;
private JPanel defaultConfigNotice;
private Consumer<SymbolServerService> changeCallback;
private JButton refreshSearchLocationsStatusButton;
private JButton moveLocationUpButton;
private JButton moveLocationDownButton;
private JButton deleteLocationButton;
private JButton addLocationButton;
private JPanel symbolStorageLocationPanel;
private HintTextField symbolStorageLocationTextField;
private JButton chooseSymbolStorageLocationButton;
private JButton saveSearchLocationsButton;
private boolean configChanged;
SymbolServerPanel(Consumer<SymbolServerService> changeCallback,
SymbolServerInstanceCreatorContext symbolServerInstanceCreatorContext) {
this.symbolServerInstanceCreatorContext = symbolServerInstanceCreatorContext;
build();
DockingWindowManager.getHelpService()
.registerHelp(this,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "Symbol Server Config"));
SymbolServerService temporarySymbolServerService =
PdbPlugin.getSymbolServerService(symbolServerInstanceCreatorContext);
if (temporarySymbolServerService
.getSymbolStore() instanceof LocalSymbolStore tempLocalSymbolStore) {
setSymbolStorageLocation(tempLocalSymbolStore.getRootDir(), false);
}
tableModel.addSymbolServers(temporarySymbolServerService.getSymbolServers());
setConfigChanged(false);
this.changeCallback = changeCallback;
}
private void build() {
setLayout(new BorderLayout());
setBorder(BorderFactory.createTitledBorder("Symbol Server Search Config"));
buildSymbolStorageLocationPanel();
JPanel buttonPanel = buildButtonPanel();
JScrollPane tableScrollPane = buildTable();
defaultConfigNotice = new JPanel();
GHtmlLabel label = new GHtmlLabel("<html><center><font color=\"" +
Messages.ERROR.toHexString() + "\"><br>Missing / invalid configuration.<br><br>" +
"Using default search location:<br>Program's Import Location<br>");
label.setHorizontalAlignment(SwingConstants.CENTER);
defaultConfigNotice.add(label);
defaultConfigNotice.setPreferredSize(tableScrollPane.getPreferredSize());
additionalSearchLocationsPanel = new JPanel();
additionalSearchLocationsPanel
.setLayout(new BoxLayout(additionalSearchLocationsPanel, BoxLayout.Y_AXIS));
additionalSearchLocationsPanel.add(buttonPanel);
additionalSearchLocationsPanel.add(tableScrollPane);
add(symbolStorageLocationPanel, BorderLayout.NORTH);
add(additionalSearchLocationsPanel, BorderLayout.CENTER);
}
private void updateLayout(boolean showTable) {
if (showTable == (additionalSearchLocationsPanel.getParent() != null)) {
return;
}
remove(additionalSearchLocationsPanel);
remove(defaultConfigNotice);
add(showTable ? additionalSearchLocationsPanel : defaultConfigNotice, BorderLayout.CENTER);
invalidate();
}
/**
* Returns a new {@link SymbolServerService} instance representing the currently
* displayed configuration, or null if the displayed configuration is not valid.
*
* @return new {@link SymbolServerService} or null
*/
SymbolServerService getSymbolServerService() {
return (localSymbolStore != null)
? new SymbolServerService(localSymbolStore, tableModel.getSymbolServers())
: null;
}
void setSymbolServers(List<SymbolServer> symbolServers) {
tableModel.setSymbolServers(symbolServers);
}
/**
* The union of the changed status of the local storage path and the additional
* search paths table model changed status.
*
* @return boolean true if the config has changed
*/
boolean isConfigChanged() {
return configChanged || tableModel.isDataChanged();
}
void setConfigChanged(boolean configChanged) {
this.configChanged = configChanged;
tableModel.setDataChanged(configChanged);
}
private JScrollPane buildTable() {
tableModel = new SymbolServerTableModel();
table = new GTable(tableModel);
table.setVisibleRowCount(4);
table.setUserSortingEnabled(false);
table.getSelectionManager().addListSelectionListener(e -> {
updateButtonEnablement();
});
tableModel.addTableModelListener(e -> {
updateButtonEnablement();
fireChanged();
});
TableColumn enabledColumn = table.getColumnModel().getColumn(0);
enabledColumn.setResizable(false);
enabledColumn.setPreferredWidth(32);
enabledColumn.setMaxWidth(32);
enabledColumn.setMinWidth(32);
TableColumn statusColumn = table.getColumnModel().getColumn(1);
statusColumn.setResizable(false);
statusColumn.setPreferredWidth(32);
statusColumn.setMaxWidth(32);
statusColumn.setMinWidth(32);
table.setPreferredScrollableViewportSize(new Dimension(100, 100));
return new JScrollPane(table);
}
private JPanel buildButtonPanel() {
refreshSearchLocationsStatusButton =
createImageButton(Icons.REFRESH_ICON, "Refresh Status");
refreshSearchLocationsStatusButton.addActionListener(e -> refreshSearchLocationStatus());
DockingWindowManager.getHelpService()
.registerHelp(refreshSearchLocationsStatusButton, new HelpLocation(
PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Refresh Status"));
moveLocationUpButton = createImageButton(Icons.UP_ICON, "Up");
moveLocationUpButton.addActionListener(e -> moveLocation(-1));
moveLocationUpButton.setToolTipText("Move location up");
DockingWindowManager.getHelpService()
.registerHelp(moveLocationUpButton, new HelpLocation(
PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig MoveUpDown"));
moveLocationDownButton = createImageButton(Icons.DOWN_ICON, "Down");
moveLocationDownButton.addActionListener(e -> moveLocation(1));
moveLocationDownButton.setToolTipText("Move location down");
DockingWindowManager.getHelpService()
.registerHelp(moveLocationDownButton, new HelpLocation(
PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig MoveUpDown"));
deleteLocationButton = createImageButton(Icons.DELETE_ICON, "Delete");
deleteLocationButton.addActionListener(e -> deleteLocation());
DockingWindowManager.getHelpService()
.registerHelp(deleteLocationButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Delete"));
addLocationButton = createImageButton(Icons.ADD_ICON, "Add");
addLocationButton.addActionListener(e -> addLocation());
DockingWindowManager.getHelpService()
.registerHelp(addLocationButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Add"));
saveSearchLocationsButton = createImageButton(Icons.SAVE_ICON, "Save Configuration");
saveSearchLocationsButton.addActionListener(e -> saveConfig());
DockingWindowManager.getHelpService()
.registerHelp(saveSearchLocationsButton,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig Save"));
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
buttonPanel.add(new GLabel("Additional Search Paths:"));
buttonPanel.add(Box.createHorizontalGlue());
buttonPanel.add(addLocationButton);
buttonPanel.add(deleteLocationButton);
buttonPanel.add(moveLocationUpButton);
buttonPanel.add(moveLocationDownButton);
buttonPanel.add(refreshSearchLocationsStatusButton);
buttonPanel.add(saveSearchLocationsButton);
return buttonPanel;
}
private JPanel buildSymbolStorageLocationPanel() {
symbolStorageLocationTextField = new HintTextField(" Required ");
symbolStorageLocationTextField.setEditable(false);
symbolStorageLocationTextField
.setToolTipText("User-specified directory where PDB files are stored. Required.");
chooseSymbolStorageLocationButton = new BrowseButton();
chooseSymbolStorageLocationButton.addActionListener(e -> chooseSymbolStorageLocation());
symbolStorageLocationPanel = new JPanel(new PairLayout(5, 5));
GLabel symbolStorageLocLabel = new GLabel("Local Symbol Storage:", SwingConstants.RIGHT);
symbolStorageLocLabel.setToolTipText(symbolStorageLocationTextField.getToolTipText());
symbolStorageLocationPanel.add(symbolStorageLocLabel);
symbolStorageLocationPanel.add(LoadPdbDialog.join(null, symbolStorageLocationTextField,
chooseSymbolStorageLocationButton));
return symbolStorageLocationPanel;
}
private void updateButtonEnablement() {
boolean hasLocalSymbolStore = localSymbolStore != null;
boolean singleRow = table.getSelectedRowCount() == 1;
boolean moreThanOneRow = table.getRowCount() > 1;
refreshSearchLocationsStatusButton.setEnabled(hasLocalSymbolStore && !tableModel.isEmpty());
moveLocationUpButton.setEnabled(hasLocalSymbolStore && singleRow && moreThanOneRow);
moveLocationDownButton.setEnabled(hasLocalSymbolStore && singleRow && moreThanOneRow);
addLocationButton.setEnabled(hasLocalSymbolStore);
deleteLocationButton.setEnabled(hasLocalSymbolStore && table.getSelectedRowCount() > 0);
saveSearchLocationsButton.setEnabled(hasLocalSymbolStore && isConfigChanged());
updateLayout(hasLocalSymbolStore);
}
private void setSymbolStorageLocation(File symbolStorageDir, boolean allowGUIPrompt) {
if (symbolStorageDir == null) {
return;
}
if (!symbolStorageDir.exists()) {
if (!allowGUIPrompt) {
return;
}
int opt = OptionDialog.showOptionDialog(this, "Create Local Symbol Storage Directory?",
"<html>Symbol storage directory<br>" +
HTMLUtilities.escapeHTML(symbolStorageDir.getPath()) +
"<br>does not exist. Create?",
"Yes", OptionDialog.QUESTION_MESSAGE);
if (opt == OptionDialog.CANCEL_OPTION) {
return;
}
try {
FileUtilities.checkedMkdirs(symbolStorageDir);
}
catch (IOException e) {
Msg.showError(this, this, "Failure", "Failed to create symbol storage directory " +
symbolStorageDir + ": " + e.getMessage());
return;
}
}
if (allowGUIPrompt && isEmptyDirectory(symbolStorageDir)) {
if (OptionDialog.showYesNoDialog(this, "Initialize Symbol Storage Directory?",
"<html>Initialize new directory as Microsoft symbol storage directory?") == OptionDialog.YES_OPTION) {
try {
LocalSymbolStore.create(symbolStorageDir,
1 /* level1 MS symbol storage directory */);
}
catch (IOException e) {
Msg.showError(this, this, "Initialize Failure",
"Failed to initialize symbol storage directory " + symbolStorageDir, e);
}
}
}
localSymbolStore =
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.newSymbolServer(symbolStorageDir.getPath(), symbolServerInstanceCreatorContext,
SymbolStore.class);
symbolStorageLocationTextField.setText(symbolStorageDir.getPath());
fireChanged();
}
private void fireChanged() {
if (changeCallback != null) {
changeCallback.accept(getSymbolServerService());
}
}
private void chooseSymbolStorageLocation() {
configChanged = true;
GhidraFileChooser chooser = getChooser();
setSymbolStorageLocation(chooser.getSelectedFile(), true);
updateButtonEnablement();
chooser.dispose();
}
private void importLocations() {
String envVar = (String) JOptionPane.showInputDialog(this,
"<html>Enter value:<br><br>Example: SVR*c:\\symbols*https://msdl.microsoft.com/download/symbols/<br><br>",
"Enter Symbol Server Search Path Value", JOptionPane.QUESTION_MESSAGE, null, null,
Objects.requireNonNullElse(System.getenv(MS_SYMBOLSERVER_ENVVAR), ""));
if (envVar == null) {
return;
}
List<String> symbolServerPaths = getSymbolPathsFromEnvStr(envVar);
if (!symbolServerPaths.isEmpty()) {
// if the first item in the path list looks like a local symbol storage path,
// allow the user to set it as the storage dir (and remove it from the elements
// that will be added to the search list)
String firstSearchPath = symbolServerPaths.get(0);
SymbolServer symbolServer =
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.newSymbolServer(firstSearchPath, symbolServerInstanceCreatorContext);
if (symbolServer instanceof LocalSymbolStore localSymbolStore &&
localSymbolStore.isValid()) {
int choice = OptionDialog.showYesNoCancelDialog(this, "Set Symbol Storage Location",
"Set symbol storage location to " + firstSearchPath + "?");
if (choice == OptionDialog.CANCEL_OPTION) {
return;
}
if (choice == OptionDialog.YES_OPTION) {
symbolServerPaths.remove(0);
configChanged = true;
setSymbolStorageLocation(localSymbolStore.getRootDir(), true);
symbolStorageLocationTextField.setText(symbolServer.getName());
}
}
}
tableModel.addSymbolServers(
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.createSymbolServersFromPathList(symbolServerPaths,
symbolServerInstanceCreatorContext));
fireChanged();
}
private List<String> getSymbolPathsFromEnvStr(String envString) {
// Expect the environment string to be in the MS symbol server search path form:
// srv*[local cache]*[private symbol server]*https://msdl.microsoft.com/download/symbols
// srv*c:\symbols*https://msdl.microsoft.com/download/symbols;srv*c:\additional*https://symbol.server.tld/
String[] envParts = envString.split("[*;]");
List<String> results = new ArrayList<>();
Set<String> locationStringDeduplicationSet = new HashSet<>();
for (String envPart : envParts) {
String locationString = envPart.trim();
if (!locationString.isBlank() && !locationString.equalsIgnoreCase("srv") &&
!locationStringDeduplicationSet.contains(locationString)) {
results.add(locationString);
locationStringDeduplicationSet.add(locationString);
}
}
return results;
}
private void addLocation() {
JPopupMenu menu = createAddLocationPopupMenu();
menu.show(addLocationButton, 0, 0);
}
private JPopupMenu createAddLocationPopupMenu() {
JPopupMenu menu = new JPopupMenu();
JMenuItem addDirMenuItem = new JMenuItem("Directory");
addDirMenuItem.addActionListener(e -> addDirectoryLocation());
menu.add(addDirMenuItem);
JMenuItem addURLMenuItem = new JMenuItem("URL");
addURLMenuItem.addActionListener(e -> addUrlLocation());
menu.add(addURLMenuItem);
JMenuItem addProgLocMenuItem =
new JMenuItem(SameDirSymbolStore.PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR);
addProgLocMenuItem.addActionListener(e -> addSameDirLocation());
menu.add(addProgLocMenuItem);
JMenuItem importEnvMenuItem = new JMenuItem("Import _NT_SYMBOL_PATH");
importEnvMenuItem.addActionListener(e -> importLocations());
menu.add(importEnvMenuItem);
if (!knownSymbolServers.isEmpty()) {
menu.add(new JSeparator());
for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) {
JMenuItem mi = new JMenuItem(ssloc.getLocation());
mi.addActionListener(e -> addKnownLocation(ssloc));
mi.setToolTipText(" [from " + ssloc.getFileOrigin() + "]");
menu.add(mi);
}
}
DockingWindowManager.getHelpService()
.registerHelp(menu,
new HelpLocation(PdbPlugin.PDB_PLUGIN_HELP_TOPIC, "SymbolServerConfig_Add"));
return menu;
}
private void addSameDirLocation() {
SameDirSymbolStore sameDirSymbolStore =
new SameDirSymbolStore(symbolServerInstanceCreatorContext.getRootDir());
tableModel.addSymbolServer(sameDirSymbolStore);
}
private void addKnownLocation(WellKnownSymbolServerLocation ssloc) {
SymbolServer symbolServer =
symbolServerInstanceCreatorContext.getSymbolServerInstanceCreatorRegistry()
.newSymbolServer(ssloc.getLocation(), symbolServerInstanceCreatorContext);
if (symbolServer != null) {
tableModel.addSymbolServer(symbolServer);
}
}
private void addUrlLocation() {
String urlLocationString = OptionDialog.showInputSingleLineDialog(this, "Enter URL",
"Enter the URL of a Symbol Server: ", "https://");
if (urlLocationString == null || urlLocationString.isBlank()) {
return;
}
urlLocationString = urlLocationString.toLowerCase();
if (!(urlLocationString.startsWith("http://") ||
urlLocationString.startsWith("https://"))) {
Msg.showWarn(this, this, "Bad URL", "Invalid URL: " + urlLocationString);
return;
}
try {
HttpSymbolServer httpSymbolServer = new HttpSymbolServer(URI.create(urlLocationString));
tableModel.addSymbolServer(httpSymbolServer);
}
catch (IllegalArgumentException e) {
Msg.showWarn(this, this, "Bad URL", "Invalid URL: " + urlLocationString);
}
}
private void addDirectoryLocation() {
File dir =
FilePromptDialog.chooseDirectory("Enter Path", "Symbol Storage Location: ", null);
if (dir == null) {
return;
}
if (!dir.exists() || !dir.isDirectory()) {
Msg.showError(this, this, "Bad path", "Invalid path: " + dir);
return;
}
LocalSymbolStore symbolStore = new LocalSymbolStore(dir);
tableModel.addSymbolServer(symbolStore);
}
private void deleteLocation() {
int selectedRow = table.getSelectedRow();
tableModel.deleteRows(table.getSelectedRows());
if (selectedRow >= 0 && selectedRow < table.getRowCount()) {
table.selectRow(selectedRow);
}
}
private void moveLocation(int delta) {
if (table.getSelectedRowCount() == 1) {
tableModel.moveRow(table.getSelectedRow(), delta);
}
}
private void refreshSearchLocationStatus() {
tableModel.refreshSymbolServerLocationStatus();
updateButtonEnablement();
}
/* package */ void saveConfig() {
SymbolServerService temporarySymbolServerService = getSymbolServerService();
if (temporarySymbolServerService != null) {
PdbPlugin.saveSymbolServerServiceConfig(temporarySymbolServerService);
Preferences.store();
setConfigChanged(false);
fireChanged();
updateButtonEnablement();
}
}
private GhidraFileChooser getChooser() {
GhidraFileChooser chooser = new GhidraFileChooser(this);
chooser.setMultiSelectionEnabled(false);
chooser.setApproveButtonText("Choose");
chooser.setFileSelectionMode(GhidraFileChooserMode.DIRECTORIES_ONLY);
chooser.setTitle("Select Symbol Storage Dir");
return chooser;
}
/* screen shot usage */ void pushAddLocationButton() {
addLocation();
}
/* screen shot usage */ void setSymbolStorageDirectoryTextOnly(String pathStr) {
symbolStorageLocationTextField.setText(pathStr);
}
/**
* Returns true if the given file path is a directory that contains no files.
* <p>
*
* @param directory path to a location on the file system
* @return true if is a directory and it contains no files
*/
private static boolean isEmptyDirectory(File directory) {
if (directory.isDirectory()) {
File[] dirContents = directory.listFiles();
return dirContents != null && dirContents.length == 0;
}
return false;
}
private static JButton createImageButton(Icon buttonIcon, String alternateText) {
JButton button = new GButton(buttonIcon);
button.setToolTipText(alternateText);
button.setPreferredSize(BUTTON_SIZE);
return button;
}
static StatusText getSymbolServerWarnings(List<SymbolServer> symbolServers) {
Map<String, String> warningsByLocation = new HashMap<>();
for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) {
if (ssloc.getWarning() != null && !ssloc.getWarning().isBlank()) {
warningsByLocation.put(ssloc.getLocation(), ssloc.getWarning());
}
}
String warning = symbolServers.stream()
.map(symbolServer -> warningsByLocation.get(symbolServer.getName()))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.joining("<br>\n"));
return !warning.isEmpty() ? new StatusText(warning, MessageType.WARNING, false) : null;
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -15,8 +15,12 @@
*/
package pdb.symbolserver.ui;
import static pdb.symbolserver.ui.SymbolServerRow.LocationStatus.*;
import ghidra.util.task.TaskMonitor;
import pdb.symbolserver.DisabledSymbolServer;
import pdb.symbolserver.SymbolServer;
import pdb.symbolserver.SymbolServer.MutableTrust;
/**
* Represents a row in the {@link SymbolServerTableModel}
@ -59,6 +63,16 @@ class SymbolServerRow {
}
}
boolean isTrusted() {
return symbolServer.isTrusted();
}
void setTrusted(boolean isTrusted) {
if (symbolServer instanceof MutableTrust sswt) {
sswt.setTrusted(isTrusted);
}
}
LocationStatus getStatus() {
return status;
}
@ -67,6 +81,12 @@ class SymbolServerRow {
this.status = status;
}
void updateStatus(TaskMonitor monitor) {
if (!(symbolServer instanceof SymbolServer.StatusRequiresContext)) {
this.status = symbolServer.isValid(monitor) ? VALID : INVALID;
}
}
@Override
public String toString() {
return String.format("SymbolServerRow: [ status: %s, server: %s]", status.toString(),

View File

@ -4,9 +4,9 @@
* 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.
@ -15,30 +15,28 @@
*/
package pdb.symbolserver.ui;
import static java.util.stream.Collectors.toList;
import static pdb.symbolserver.ui.SymbolServerRow.LocationStatus.INVALID;
import static pdb.symbolserver.ui.SymbolServerRow.LocationStatus.VALID;
import static java.util.stream.Collectors.*;
import java.awt.Component;
import java.awt.FontMetrics;
import java.util.ArrayList;
import java.util.List;
import java.awt.Component;
import javax.swing.*;
import javax.swing.table.TableColumn;
import docking.widgets.table.*;
import generic.theme.GIcon;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.framework.plugintool.ServiceProviderStub;
import ghidra.util.Swing;
import ghidra.util.table.column.AbstractGColumnRenderer;
import ghidra.util.table.column.GColumnRenderer;
import ghidra.util.task.TaskLauncher;
import pdb.symbolserver.SymbolServer;
import resources.Icons;
/**
* Table model for the {@link SymbolServerPanel} table
* Table model for the {@link ConfigPdbDialog} table
*/
class SymbolServerTableModel
extends GDynamicColumnTableModel<SymbolServerRow, List<SymbolServerRow>> {
@ -64,9 +62,7 @@ class SymbolServerTableModel
}
List<SymbolServer> getSymbolServers() {
return rows.stream()
.map(SymbolServerRow::getSymbolServer)
.collect(toList());
return rows.stream().map(SymbolServerRow::getSymbolServer).collect(toList());
}
void addSymbolServer(SymbolServer ss) {
@ -92,26 +88,6 @@ class SymbolServerTableModel
fireTableDataChanged();
}
void refreshSymbolServerLocationStatus() {
List<SymbolServerRow> rowsCopy = new ArrayList<>(this.rows);
TaskLauncher.launchNonModal("Refresh Symbol Server Location Status", monitor -> {
monitor.initialize(rowsCopy.size());
monitor.setMessage("Refreshing symbol server status");
try {
for (SymbolServerRow row : rowsCopy) {
if (monitor.isCancelled()) {
break;
}
monitor.setMessage("Checking " + row.getSymbolServer().getName());
row.setStatus(row.getSymbolServer().isValid(monitor) ? VALID : INVALID);
}
}
finally {
Swing.runLater(SymbolServerTableModel.this::fireTableDataChanged);
}
});
}
void moveRow(int rowIndex, int deltaIndex) {
int destIndex = rowIndex + deltaIndex;
if (rowIndex < 0 || rowIndex >= rows.size() || destIndex < 0 || destIndex >= rows.size()) {
@ -159,18 +135,24 @@ class SymbolServerTableModel
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
DynamicTableColumn<SymbolServerRow, ?, ?> column = getColumn(columnIndex);
if (column instanceof EnabledColumn) {
if (column instanceof EnabledColumn && aValue instanceof Boolean) {
SymbolServerRow row = getRowObject(rowIndex);
row.setEnabled((Boolean) aValue);
dataChanged = true;
fireTableDataChanged();
}
else if (column instanceof TrustedColumn && aValue instanceof Boolean) {
SymbolServerRow row = getRowObject(rowIndex);
row.setTrusted((Boolean) aValue);
dataChanged = true;
fireTableDataChanged();
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
DynamicTableColumn<SymbolServerRow, ?, ?> column = getColumn(columnIndex);
return column instanceof EnabledColumn;
return column instanceof EnabledColumn || column instanceof TrustedColumn;
}
@Override
@ -178,6 +160,7 @@ class SymbolServerTableModel
TableColumnDescriptor<SymbolServerRow> descriptor = new TableColumnDescriptor<>();
descriptor.addVisibleColumn(new EnabledColumn());
descriptor.addVisibleColumn(new TrustedColumn());
descriptor.addVisibleColumn(new StatusColumn());
descriptor.addVisibleColumn(new LocationColumn());
@ -187,9 +170,10 @@ class SymbolServerTableModel
//-------------------------------------------------------------------------------------------
private static class StatusColumn extends
AbstractDynamicTableColumnStub<SymbolServerRow, SymbolServerRow.LocationStatus> {
AbstractDynamicTableColumnStub<SymbolServerRow, SymbolServerRow.LocationStatus>
implements TableColumnInitializer {
private static final Icon VALID_ICON = Icons.get("images/checkmark_green.gif");
private static final Icon VALID_ICON = new GIcon("icon.checkmark.green");
private static final Icon INVALID_ICON = Icons.ERROR_ICON;
private static Icon[] icons = new Icon[] { null, VALID_ICON, INVALID_ICON };
@ -206,7 +190,7 @@ class SymbolServerTableModel
@Override
public String getColumnDisplayName(Settings settings) {
return "";
return "Status";
}
@Override
@ -219,14 +203,23 @@ class SymbolServerTableModel
return renderer;
}
@Override
public void initializeTableColumn(TableColumn col, FontMetrics fm, int padding) {
int colWidth = fm.stringWidth("Status") + padding;
col.setPreferredWidth(colWidth);
col.setMaxWidth(colWidth * 2);
col.setMinWidth(colWidth);
}
}
private static class EnabledColumn
extends AbstractDynamicTableColumnStub<SymbolServerRow, Boolean> {
extends AbstractDynamicTableColumnStub<SymbolServerRow, Boolean>
implements TableColumnInitializer {
@Override
public String getColumnDisplayName(Settings settings) {
return "";
return "Enabled";
}
@Override
@ -240,6 +233,43 @@ class SymbolServerTableModel
return "Enabled";
}
@Override
public void initializeTableColumn(TableColumn col, FontMetrics fm, int padding) {
int colWidth = fm.stringWidth("Enabled") + padding;
col.setPreferredWidth(colWidth);
col.setMaxWidth(colWidth * 2);
col.setMinWidth(colWidth);
}
}
private static class TrustedColumn
extends AbstractDynamicTableColumnStub<SymbolServerRow, Boolean>
implements TableColumnInitializer {
@Override
public String getColumnDisplayName(Settings settings) {
return "Trusted";
}
@Override
public Boolean getValue(SymbolServerRow rowObject, Settings settings,
ServiceProvider serviceProvider) throws IllegalArgumentException {
return rowObject.isTrusted();
}
@Override
public String getColumnName() {
return "Trusted";
}
@Override
public void initializeTableColumn(TableColumn col, FontMetrics fm, int padding) {
int colWidth = fm.stringWidth("Trusted") + padding;
col.setPreferredWidth(colWidth);
col.setMaxWidth(colWidth * 2);
col.setMinWidth(colWidth);
}
}
private static class LocationColumn
@ -305,5 +335,6 @@ class SymbolServerTableModel
public String getFilterString(E t, Settings settings) {
return t == null ? "" : t.toString();
}
}
}

View File

@ -0,0 +1,64 @@
/* ###
* 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 pdb.symbolserver.ui;
import java.awt.FontMetrics;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import docking.ComponentProvider;
import docking.DialogComponentProvider;
import docking.widgets.table.*;
/**
* For Pdb symbolserver gui stuff only.
*
* Add on interface for DynamicTableColumn classes that let them control aspects of the
* matching TableColumn.
*/
public interface TableColumnInitializer {
/**
* Best called during {@link DialogComponentProvider#dialogShown} or
* {@link ComponentProvider#componentShown}
*
* @param table table component
* @param model table model
*/
static void initializeTableColumns(GTable table, GDynamicColumnTableModel<?, ?> model) {
TableColumnModel colModel = table.getColumnModel();
FontMetrics fm = table.getTableHeader().getFontMetrics(table.getTableHeader().getFont());
int padding = fm.stringWidth("WW"); // w.a.g. for the left+right padding on the header column component
for (int colIndex = 0; colIndex < model.getColumnCount(); colIndex++) {
DynamicTableColumn<?, ?, ?> dtableCol = model.getColumn(colIndex);
if (dtableCol instanceof TableColumnInitializer colInitializer) {
TableColumn tableCol = colModel.getColumn(colIndex);
colInitializer.initializeTableColumn(tableCol, fm, padding);
}
}
}
/**
* Called to allow the initializer to modify the specified TableColumn
*
* @param col {@link TableColumn}
* @param fm {@link FontMetrics} used by the table header gui component
* @param padding padding to use in the column
*/
void initializeTableColumn(TableColumn col, FontMetrics fm, int padding);
}

View File

@ -4,9 +4,9 @@
* 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.
@ -17,68 +17,27 @@ package pdb.symbolserver.ui;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import generic.jar.ResourceFile;
import ghidra.framework.Application;
import ghidra.util.MessageType;
import ghidra.util.Msg;
import pdb.symbolserver.SymbolServer;
import pdb.symbolserver.ui.LoadPdbDialog.StatusText;
import utilities.util.FileUtilities;
/**
* Represents a well-known symbol server location.
* <p>
* See the PDB_SYMBOL_SERVER_URLS.pdburl file.
* @param location url string
* @param locationCategory grouping criteria
* @param warning string
* @param fileOrigin file name that contained this info
*/
class WellKnownSymbolServerLocation {
private String locationCategory;
private String location;
private String warning;
private String fileOrigin;
WellKnownSymbolServerLocation(String location, String locationCategory, String warning,
String fileOrigin) {
this.location = location;
this.locationCategory = locationCategory;
this.warning = warning;
this.fileOrigin = fileOrigin;
}
String getLocationCategory() {
return locationCategory;
}
String getLocation() {
return location;
}
String getWarning() {
return warning;
}
String getFileOrigin() {
return fileOrigin;
}
@Override
public int hashCode() {
return Objects.hash(location, locationCategory, warning);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
WellKnownSymbolServerLocation other = (WellKnownSymbolServerLocation) obj;
return Objects.equals(location, other.location) &&
Objects.equals(locationCategory, other.locationCategory) &&
Objects.equals(warning, other.warning);
}
public record WellKnownSymbolServerLocation(String location, String locationCategory,
String warning, String fileOrigin) {
/**
* Loads all symbol server location files (*.pdburl) and returns a list of entries.
@ -103,10 +62,37 @@ class WellKnownSymbolServerLocation {
}
}
catch (IOException e) {
Msg.warn(WellKnownSymbolServerLocation.class, "Unable to read pdburl file: " + file);
Msg.warn(WellKnownSymbolServerLocation.class,
"Unable to read pdburl file: " + file);
}
}
return results;
}
/**
* Returns a formatted StatusText containing all the warnings published by any untrusted
* {@link WellKnownSymbolServerLocation} found in the list of symbolservers.
*
* @param knownSymbolServers list
* @param symbolServers list
* @return StatusText
*/
public static StatusText getWarningsFor(List<WellKnownSymbolServerLocation> knownSymbolServers,
List<SymbolServer> symbolServers) {
Map<String, String> warningsByLocation = new HashMap<>();
for (WellKnownSymbolServerLocation ssloc : knownSymbolServers) {
if (ssloc.warning() != null && !ssloc.warning().isBlank()) {
warningsByLocation.put(ssloc.location(), ssloc.warning());
}
}
String warning = symbolServers.stream()
.filter(symbolServer -> !symbolServer.isTrusted())
.map(symbolServer -> warningsByLocation.get(symbolServer.getName()))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.joining("<br>\n"));
return !warning.isEmpty() ? new StatusText(warning, MessageType.WARNING, false) : null;
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -21,14 +21,16 @@ import java.util.List;
import java.util.Set;
import ghidra.util.task.TaskMonitor;
import pdb.symbolserver.SymbolServer.MutableTrust;
/**
* A "remote" symbol server that answers affirmatively for any query.
*/
public class DummySymbolServer implements SymbolServer {
public class DummySymbolServer implements SymbolServer, MutableTrust {
private final byte[] dummyPayload;
private final boolean returnCompressedFilenames;
private boolean trusted;
public DummySymbolServer(String dummyPayload) {
this(dummyPayload.getBytes(), false);
@ -78,8 +80,13 @@ public class DummySymbolServer implements SymbolServer {
}
@Override
public boolean isLocal() {
return false;
public boolean isTrusted() {
return trusted;
}
@Override
public void setTrusted(boolean isTrusted) {
this.trusted = isTrusted;
}
}

View File

@ -4,9 +4,9 @@
* 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.
@ -15,16 +15,14 @@
*/
package pdb.symbolserver;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
@ -128,14 +126,14 @@ public class SymbolServerServiceTest extends AbstractGenericTest {
}
@Test
public void test_Remote() throws IOException, CancelledException {
public void test_Trusted() throws IOException, CancelledException {
String payload = "testdummy";
SymbolServerService symbolServerService =
new SymbolServerService(localSymbolStore1,
List.of(localSymbolStore2, new DummySymbolServer(payload)));
SymbolFileInfo searchPdb = SymbolFileInfo.fromValues("file1.pdb", "11223344", 0);
List<SymbolFileLocation> results =
symbolServerService.find(searchPdb, FindOption.of(FindOption.ALLOW_REMOTE),
symbolServerService.find(searchPdb, FindOption.of(FindOption.ALLOW_UNTRUSTED),
TaskMonitor.DUMMY);
assertEquals(1, results.size());
@ -148,8 +146,9 @@ public class SymbolServerServiceTest extends AbstractGenericTest {
@Test
public void test_NoRemote() throws CancelledException {
String payload = "testdummy";
DummySymbolServer dummySymbolServer = new DummySymbolServer(payload);
SymbolServerService symbolServerService =
new SymbolServerService(localSymbolStore1, List.of(new DummySymbolServer(payload)));
new SymbolServerService(localSymbolStore1, List.of(dummySymbolServer));
SymbolFileInfo searchPdb = SymbolFileInfo.fromValues("file1.pdb", "11223344", 0);
List<SymbolFileLocation> results =
symbolServerService.find(searchPdb, FindOption.NO_OPTIONS, TaskMonitor.DUMMY);

View File

@ -4,9 +4,9 @@
* 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.
@ -31,8 +31,7 @@ import ghidra.framework.options.Options;
import ghidra.program.model.listing.Program;
import pdb.PdbPlugin;
import pdb.symbolserver.*;
import pdb.symbolserver.ui.ConfigPdbDialog;
import pdb.symbolserver.ui.LoadPdbDialog;
import pdb.symbolserver.ui.*;
public class PdbScreenShots extends GhidraScreenShotGenerator {
@ -64,7 +63,7 @@ public class PdbScreenShots extends GhidraScreenShotGenerator {
}
@Test
public void testSymbolServerConfig_Screenshot() throws IOException {
public void testSymbolServerConfig_Screenshot() {
PdbPlugin.saveSymbolServerServiceConfig(null);
ConfigPdbDialog configPdbDialog = new ConfigPdbDialog();
showDialogWithoutBlocking(tool, configPdbDialog);
@ -79,7 +78,7 @@ public class PdbScreenShots extends GhidraScreenShotGenerator {
LocalSymbolStore localSymbolStore1 = new LocalSymbolStore(localSymbolStore1Root);
SameDirSymbolStore sameDirSymbolStore = new SameDirSymbolStore(null);
List<SymbolServer> symbolServers = List.of(sameDirSymbolStore,
new HttpSymbolServer(URI.create("https://msdl.microsoft.com/download/symbols/")));
HttpSymbolServer.createTrusted("https://msdl.microsoft.com/download/symbols/"));
SymbolServerService symbolServerService =
new SymbolServerService(localSymbolStore1, symbolServers);
PdbPlugin.saveSymbolServerServiceConfig(symbolServerService);
@ -88,7 +87,7 @@ public class PdbScreenShots extends GhidraScreenShotGenerator {
configPdbDialog.setSymbolServerService("/home/user/symbols", symbolServers);
showDialogWithoutBlocking(tool, configPdbDialog);
waitForSwing();
captureDialog(ConfigPdbDialog.class, 410, 280);
captureDialog(ConfigPdbDialog.class, 520, 280);
}
@Test
@ -112,12 +111,29 @@ public class PdbScreenShots extends GhidraScreenShotGenerator {
showDialogWithoutBlocking(tool, configPdbDialog);
waitForSwing();
runSwing(() -> {
configPdbDialog.setWellknownSymbolServers(createFakeWellKnowns());
configPdbDialog.pushAddLocationButton();
});
waitForSwing();
captureMenu();
}
List<WellKnownSymbolServerLocation> createFakeWellKnowns() {
// due to module dependencies, this screen shot test can't see the contents of the normal
// PDBURL file loaded from the 'z public release' module.
return List.of( // should be same as the PDB_SYMBOL_SERVERS.PDBURL file
new WellKnownSymbolServerLocation("", "https://msdl.microsoft.com/download/symbols/",
"WARNING: Check your organization's security policy before downloading files from the internet.",
"screen shot"),
new WellKnownSymbolServerLocation("",
"https://chromium-browser-symsrv.commondatastorage.googleapis.com",
"WARNING: Check your organization's security policy before downloading files from the internet.",
"screen shot"),
new WellKnownSymbolServerLocation("", "https://symbols.mozilla.org/",
"WARNING: Check your organization's security policy before downloading files from the internet.",
"screen shot"));
}
@Test
public void testLoadPdb_Advanced_NeedsConfig() {
PdbPlugin.saveSymbolServerServiceConfig(null);
@ -158,7 +174,7 @@ public class PdbScreenShots extends GhidraScreenShotGenerator {
localSymbolStore1, SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 2)),
new SymbolFileLocation("HelloWorld.pdb", sameDirSymbolStoreWithFakePath,
SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 1)));
Set<FindOption> findOptions = FindOption.of(FindOption.ALLOW_REMOTE, FindOption.ANY_AGE);
Set<FindOption> findOptions = FindOption.of(FindOption.ALLOW_UNTRUSTED, FindOption.ANY_AGE);
runSwing(() -> {
loadPdbDialog.setSearchOptions(findOptions);
loadPdbDialog.setSearchResults(symbolFileLocations, findOptions);

View File

@ -4,9 +4,9 @@
* 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.
@ -99,7 +99,7 @@ public class SymbolServerService2Test extends AbstractGhidraHeadedIntegrationTes
List<SymbolFileLocation> results =
symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1),
FindOption.of(FindOption.ALLOW_REMOTE), TaskMonitor.DUMMY);
FindOption.of(FindOption.ALLOW_UNTRUSTED), TaskMonitor.DUMMY);
assertEquals(1, results.size());
System.out.println(results.get(0).getLocationStr());
@ -118,7 +118,7 @@ public class SymbolServerService2Test extends AbstractGhidraHeadedIntegrationTes
List<SymbolFileLocation> results =
symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1),
FindOption.of(FindOption.ALLOW_REMOTE), TaskMonitor.DUMMY);
FindOption.of(FindOption.ALLOW_UNTRUSTED), TaskMonitor.DUMMY);
assertEquals(1, results.size());
System.out.println(results.get(0).getLocationStr());