mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 12:09:28 +08:00
GP-5333 Added repo connection status/action to root project data tree node
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 20 KiB |
+28
-17
@@ -40,27 +40,38 @@
|
|||||||
<H2><A name="ConnectToServer"></A>Connect to the Server</H2>
|
<H2><A name="ConnectToServer"></A>Connect to the Server</H2>
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>When you open a shared project, Ghidra attempts to connect to the server that is
|
<P>When you open or view a shared project, Ghidra attempts to connect to the corresponding server.
|
||||||
associated with the shared project. Depending on what user authentication mode the server is
|
Depending on which user authentication mode the server is using, you may have to enter a password.
|
||||||
using, you may have to enter a password. If the server is not running, you are still able to
|
If you choose not to connect or lose the connection to the repository server after opening
|
||||||
work with your checked out files while you are offline. Other versioned files not checked out
|
or viewing a project, shared files not
|
||||||
are not accessible. When the server comes up, Ghidra will reconnect as necessary. You can
|
checked out will not be shown within the Ghidra Project Window, as they are unavailable for use.
|
||||||
also attempt to connect "manually" by selecting the connection status button <IMG alt="" src=
|
Local project private files are not affected by the repository server connection and will always
|
||||||
"images/disconnected.gif" border="0"> on the Ghidra Project Window or on the <I><A href=
|
be shown. If you subsequently connect to the repository server, Ghidra will refresh
|
||||||
"help/topics/FrontEndPlugin/Project_Info.htm">Project Info</A></I> dialog. When the
|
the project views to reflect the current state. </P>
|
||||||
connection is successful, the connection status button changes to <IMG alt="" src=
|
|
||||||
"images/connected.gif" border="0">. </P>
|
|
||||||
|
|
||||||
<P>If you lose the connection to the server after having started Ghidra, shared files not
|
|
||||||
checked out "disappear" from the Ghidra Project Window, as they are unavailable. Private
|
|
||||||
files remain intact and are not affected by the server connection.</P>
|
|
||||||
|
|
||||||
<BLOCKQUOTE>
|
<BLOCKQUOTE>
|
||||||
<P>
|
<P>
|
||||||
<IMG SRC="help/shared/tip.png" />
|
<IMG SRC="help/shared/tip.png" />
|
||||||
You are authenticated only once per
|
The root folder node of the Project Data Tree view of a shared project will convey the
|
||||||
Ghidra session; so if you open other project repositories managed by the same Ghidra Server,
|
current connection status with green (connected) or red (disconnected) indicator. </P>
|
||||||
you will be prompted only once for a password, as required. </P>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
|
<P>If applicable, and
|
||||||
|
not currently connected to the shared repository server, a manual connection may be re-attempted by
|
||||||
|
clicking the <B>Connect Shared Repository</B> popup action on the root node of a shared project.
|
||||||
|
For the active project there is also a Connect status button in the lower-right corner of the
|
||||||
|
project window. When this button shows the disconnected state
|
||||||
|
<IMG alt="" src="images/disconnected.gif" border="0"> it may be clicked to attempt a connection.
|
||||||
|
This may also be done from the <I><A href="help/topics/FrontEndPlugin/Project_Info.htm">Project
|
||||||
|
Info</A></I> dialog. When the active project repository connection is successful, the connection
|
||||||
|
status button changes to <IMG alt="" src="images/connected.gif" border="0">. </P>
|
||||||
|
|
||||||
|
<BLOCKQUOTE>
|
||||||
|
<P>
|
||||||
|
<IMG SRC="help/shared/tip.png" />
|
||||||
|
Successfully connecting to a Ghidra Server which corresponds to multiple named repositories will cause
|
||||||
|
all associated viewed projects within Ghidra to become connected or automatically connect
|
||||||
|
when subsequently opened. </P>
|
||||||
</BLOCKQUOTE>
|
</BLOCKQUOTE>
|
||||||
|
|
||||||
<H3><A name="Troubleshooting"></A>Troubleshooting a Failed Connection</H3>
|
<H3><A name="Troubleshooting"></A>Troubleshooting a Failed Connection</H3>
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ src/main/resources/images/checkNotLatest.gif||GHIDRA||reviewed||END|
|
|||||||
src/main/resources/images/checkex.png||GHIDRA||reviewed||END|
|
src/main/resources/images/checkex.png||GHIDRA||reviewed||END|
|
||||||
src/main/resources/images/connected.gif||GHIDRA||reviewed||END|
|
src/main/resources/images/connected.gif||GHIDRA||reviewed||END|
|
||||||
src/main/resources/images/disconnected.gif||GHIDRA||reviewed||END|
|
src/main/resources/images/disconnected.gif||GHIDRA||reviewed||END|
|
||||||
|
src/main/resources/images/green_can.png||GHIDRA||||END|
|
||||||
src/main/resources/images/link.png||Crystal Clear Icons - LGPL 2.1||||END|
|
src/main/resources/images/link.png||Crystal Clear Icons - LGPL 2.1||||END|
|
||||||
src/main/resources/images/lock.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
src/main/resources/images/lock.png||FAMFAMFAM Icons - CC 2.5|||famfamfam silk icon set|END|
|
||||||
src/main/resources/images/monitor.png||FAMFAMFAM Icons - CC 2.5|||silk|END|
|
src/main/resources/images/monitor.png||FAMFAMFAM Icons - CC 2.5|||silk|END|
|
||||||
@@ -34,6 +35,7 @@ src/main/resources/images/page_delete.png||FAMFAMFAM Icons - CC 2.5||||END|
|
|||||||
src/main/resources/images/page_edit.png||FAMFAMFAM Icons - CC 2.5||||END|
|
src/main/resources/images/page_edit.png||FAMFAMFAM Icons - CC 2.5||||END|
|
||||||
src/main/resources/images/plasma.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
src/main/resources/images/plasma.png||Oxygen Icons - LGPL 3.0|||Oxygen icon theme (dual license; LGPL or CC-SA-3.0)|END|
|
||||||
src/main/resources/images/plugin.png||GHIDRA||reviewed||END|
|
src/main/resources/images/plugin.png||GHIDRA||reviewed||END|
|
||||||
|
src/main/resources/images/red_can.png||GHIDRA||||END|
|
||||||
src/main/resources/images/small_hijack.gif||GHIDRA||reviewed||END|
|
src/main/resources/images/small_hijack.gif||GHIDRA||reviewed||END|
|
||||||
src/main/resources/images/undo_hijack.png||GHIDRA||reviewed||END|
|
src/main/resources/images/undo_hijack.png||GHIDRA||reviewed||END|
|
||||||
src/main/resources/images/unknownFile.gif||GHIDRA||reviewed||END|
|
src/main/resources/images/unknownFile.gif||GHIDRA||reviewed||END|
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ icon.project.data.file.ghidra.hijacked = small_hijack.gif
|
|||||||
icon.project.data.file.ghidra.read.only = user-busy.png [size(8,8)]
|
icon.project.data.file.ghidra.read.only = user-busy.png [size(8,8)]
|
||||||
icon.project.data.file.ghidra.not.latest = checkNotLatest.gif
|
icon.project.data.file.ghidra.not.latest = checkNotLatest.gif
|
||||||
|
|
||||||
|
icon.project.root.repo.connected = green_can.png
|
||||||
|
icon.project.root.repo.connected.overlay = EMPTY_ICON[size(18,16)]{icon.project.root.repo.connected[move(12,8)]} // lower-left of 16x16 icon
|
||||||
|
|
||||||
|
icon.project.root.repo.disconnected = red_can.png
|
||||||
|
icon.project.root.repo.disconnected.overlay = EMPTY_ICON[size(18,16)]{icon.project.root.repo.disconnected[move(12,8)]} // lower-left of 16x16 icon
|
||||||
|
|
||||||
icon.content.handler.link = link.png
|
icon.content.handler.link = link.png
|
||||||
icon.content.handler.link.overlay = EMPTY_ICON[size(16,16)]{icon.content.handler.link[move(0,8)]} // lower-left of 16x16 icon
|
icon.content.handler.link.overlay = EMPTY_ICON[size(16,16)]{icon.content.handler.link[move(0,8)]} // lower-left of 16x16 icon
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,7 @@ public class FrontEndPlugin extends Plugin
|
|||||||
|
|
||||||
private FrontEndProvider frontEndProvider;
|
private FrontEndProvider frontEndProvider;
|
||||||
|
|
||||||
|
private ProjectRepoConnectAction repoConnectAction;
|
||||||
private ProjectDataCutAction cutAction;
|
private ProjectDataCutAction cutAction;
|
||||||
private ClearCutAction clearCutAction;
|
private ClearCutAction clearCutAction;
|
||||||
private ProjectDataCopyAction copyAction;
|
private ProjectDataCopyAction copyAction;
|
||||||
@@ -221,6 +222,7 @@ public class FrontEndPlugin extends Plugin
|
|||||||
String owner = getName();
|
String owner = getName();
|
||||||
|
|
||||||
// Top of popup menu actions - no group
|
// Top of popup menu actions - no group
|
||||||
|
repoConnectAction = new ProjectRepoConnectAction(this, null);
|
||||||
openAction = new ProjectDataOpenDefaultToolAction(owner, null);
|
openAction = new ProjectDataOpenDefaultToolAction(owner, null);
|
||||||
followLinkAction = new ProjectDataFollowLinkAction(this, null);
|
followLinkAction = new ProjectDataFollowLinkAction(this, null);
|
||||||
selectRealFileOrFolderAction = new ProjectDataSelectRealFileOrFolderAction(this, null);
|
selectRealFileOrFolderAction = new ProjectDataSelectRealFileOrFolderAction(this, null);
|
||||||
@@ -251,6 +253,7 @@ public class FrontEndPlugin extends Plugin
|
|||||||
groupName = "XRefresh";
|
groupName = "XRefresh";
|
||||||
refreshAction = new ProjectDataRefreshAction(owner, groupName);
|
refreshAction = new ProjectDataRefreshAction(owner, groupName);
|
||||||
|
|
||||||
|
tool.addAction(repoConnectAction);
|
||||||
tool.addAction(newFolderAction);
|
tool.addAction(newFolderAction);
|
||||||
tool.addAction(cutAction);
|
tool.addAction(cutAction);
|
||||||
tool.addAction(clearCutAction);
|
tool.addAction(clearCutAction);
|
||||||
|
|||||||
+93
@@ -0,0 +1,93 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package ghidra.framework.main;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
|
||||||
|
import docking.action.MenuData;
|
||||||
|
import generic.theme.GIcon;
|
||||||
|
import ghidra.framework.client.*;
|
||||||
|
import ghidra.framework.main.datatable.FrontendProjectTreeAction;
|
||||||
|
import ghidra.framework.main.datatable.ProjectDataContext;
|
||||||
|
import ghidra.framework.main.datatree.DataTree;
|
||||||
|
import ghidra.framework.model.DomainFolder;
|
||||||
|
import ghidra.framework.model.ProjectData;
|
||||||
|
import ghidra.util.HelpLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ProjectRepoConnectAction} action allows the user to initiate a shared repository
|
||||||
|
* connection for a root shared project data tree node that is not currently connected.
|
||||||
|
*/
|
||||||
|
public class ProjectRepoConnectAction extends FrontendProjectTreeAction {
|
||||||
|
|
||||||
|
private static final Icon CONNECT_ICON = new GIcon("icon.frontend.project.connected");
|
||||||
|
|
||||||
|
private FrontEndPlugin plugin;
|
||||||
|
|
||||||
|
public ProjectRepoConnectAction(FrontEndPlugin plugin, String group) {
|
||||||
|
super("Connect Shared Repository", plugin.getName());
|
||||||
|
this.plugin = plugin;
|
||||||
|
setPopupMenuData(
|
||||||
|
new MenuData(new String[] { "Connect Shared Repository" }, CONNECT_ICON, group));
|
||||||
|
setHelpLocation(new HelpLocation("VersionControl", "Connect_Shared_Repository"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void actionPerformed(ProjectDataContext context) {
|
||||||
|
RepositoryAdapter repository = getDisconnectedRepository(context);
|
||||||
|
if (repository != null) {
|
||||||
|
try {
|
||||||
|
repository.connect();
|
||||||
|
}
|
||||||
|
catch (NotConnectedException e) {
|
||||||
|
// don't think this can happen
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
ClientUtil.handleException(repository, e, "Repository Connection",
|
||||||
|
plugin.getTool().getToolFrame());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnabledForContext(ProjectDataContext context) {
|
||||||
|
return getDisconnectedRepository(context) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RepositoryAdapter getDisconnectedRepository(ProjectDataContext context) {
|
||||||
|
if (!(context.getComponent() instanceof DataTree)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (context.getFolderCount() != 1 || context.getFileCount() != 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
DomainFolder domainFolder = context.getSelectedFolders().get(0);
|
||||||
|
if (domainFolder.getParent() != null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ProjectData projectData = domainFolder.getProjectData();
|
||||||
|
if (projectData.getProjectLocator().isTransient()) {
|
||||||
|
return null; // Transient projects are always connected
|
||||||
|
}
|
||||||
|
RepositoryAdapter repository = projectData.getRepository();
|
||||||
|
if (repository != null && !repository.isConnected()) {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
-10
@@ -21,23 +21,81 @@ import javax.swing.Icon;
|
|||||||
|
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
import generic.theme.GIcon;
|
import generic.theme.GIcon;
|
||||||
|
import ghidra.framework.client.RemoteAdapterListener;
|
||||||
import ghidra.framework.client.RepositoryAdapter;
|
import ghidra.framework.client.RepositoryAdapter;
|
||||||
import ghidra.framework.model.*;
|
import ghidra.framework.model.*;
|
||||||
|
import resources.MultiIcon;
|
||||||
|
|
||||||
|
public class DomainFolderRootNode extends DomainFolderNode implements RemoteAdapterListener {
|
||||||
|
|
||||||
public class DomainFolderRootNode extends DomainFolderNode {
|
|
||||||
private static final Icon CLOSED_PROJECT = new GIcon("icon.datatree.node.domain.folder.closed");
|
private static final Icon CLOSED_PROJECT = new GIcon("icon.datatree.node.domain.folder.closed");
|
||||||
private static final Icon OPEN_PROJECT = new GIcon("icon.datatree.node.domain.folder.open");
|
private static final Icon OPEN_PROJECT = new GIcon("icon.datatree.node.domain.folder.open");
|
||||||
|
|
||||||
|
private static final Icon CONNECTED_OVERLAY =
|
||||||
|
new GIcon("icon.project.root.repo.connected.overlay");
|
||||||
|
private static final Icon DISCONNECTED_OVERLAY =
|
||||||
|
new GIcon("icon.project.root.repo.disconnected.overlay");
|
||||||
|
|
||||||
|
private static enum Status {
|
||||||
|
OPEN(true),
|
||||||
|
CLOSED(false),
|
||||||
|
OPEN_CONNECTED(true, true),
|
||||||
|
CLOSED_CONNECTED(false, true),
|
||||||
|
OPEN_DISCONNECTED(true, false),
|
||||||
|
CLOSED_DISCONNECTED(false, false);
|
||||||
|
|
||||||
|
final Icon icon;
|
||||||
|
|
||||||
|
private Status(boolean isOpen) {
|
||||||
|
icon = isOpen ? OPEN_PROJECT : CLOSED_PROJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Status(boolean isOpen, boolean isConnected) {
|
||||||
|
MultiIcon multiIcon = new MultiIcon(isOpen ? OPEN_PROJECT : CLOSED_PROJECT);
|
||||||
|
multiIcon.addIcon(isConnected ? CONNECTED_OVERLAY : DISCONNECTED_OVERLAY);
|
||||||
|
icon = multiIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Status getStatus(boolean isOpen, RepositoryAdapter repository) {
|
||||||
|
if (isOpen) {
|
||||||
|
if (repository == null) {
|
||||||
|
return OPEN;
|
||||||
|
}
|
||||||
|
return repository.isConnected() ? OPEN_CONNECTED : OPEN_DISCONNECTED;
|
||||||
|
}
|
||||||
|
if (repository == null) {
|
||||||
|
return CLOSED;
|
||||||
|
}
|
||||||
|
return repository.isConnected() ? CLOSED_CONNECTED : CLOSED_DISCONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String projectName;
|
private String projectName;
|
||||||
|
private RepositoryAdapter repository;
|
||||||
|
|
||||||
|
private Status status;
|
||||||
private String toolTipText;
|
private String toolTipText;
|
||||||
|
|
||||||
DomainFolderRootNode(String projectName, DomainFolder rootFolder, ProjectData projectData,
|
DomainFolderRootNode(String projectName, DomainFolder rootFolder, ProjectData projectData,
|
||||||
DomainFileFilter filter) {
|
DomainFileFilter filter) {
|
||||||
super(rootFolder, filter);
|
super(rootFolder, filter);
|
||||||
this.projectName = projectName;
|
this.projectName = projectName;
|
||||||
|
this.repository = getProjectData().getRepository();
|
||||||
|
if (repository != null) {
|
||||||
|
repository.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
toolTipText = getToolTip(projectData);
|
toolTipText = getToolTip(projectData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
if (repository != null) {
|
||||||
|
repository.removeListener(this);
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
if (projectName == null) {
|
if (projectName == null) {
|
||||||
@@ -58,21 +116,25 @@ public class DomainFolderRootNode extends DomainFolderNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon(boolean expanded) {
|
public Icon getIcon(boolean expanded) {
|
||||||
return expanded ? OPEN_PROJECT : CLOSED_PROJECT;
|
status = Status.getStatus(expanded, repository);
|
||||||
|
return status.icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getToolTip(ProjectData projectData) {
|
private String getToolTip(ProjectData projectData) {
|
||||||
RepositoryAdapter repository = projectData.getRepository();
|
ProjectLocator projectLocator = projectData.getProjectLocator();
|
||||||
File dir = projectData.getProjectLocator().getProjectDir();
|
File dir = projectLocator.getProjectDir();
|
||||||
String toolTip = dir.getAbsolutePath();
|
String toolTip = dir.getAbsolutePath();
|
||||||
if (!getDomainFolder().isInWritableProject() && repository != null) {
|
if (!projectLocator.isTransient() && repository != null) {
|
||||||
ServerInfo info = repository.getServerInfo();
|
ServerInfo info = repository.getServerInfo();
|
||||||
String serverName = "";
|
String serverName = info.getServerName() + ":";
|
||||||
if (info != null) {
|
String statusText = repository.isConnected() ? "connected" : "disconnected";
|
||||||
serverName = info.getServerName() + ", ";
|
toolTip += " [" + serverName + repository.getName() + ", " + statusText + "]";
|
||||||
}
|
|
||||||
toolTip += " [" + serverName + repository.getName() + "]";
|
|
||||||
}
|
}
|
||||||
return toolTip;
|
return toolTip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectionStateChanged(Object adapter) {
|
||||||
|
toolTipText = getToolTip(getProjectData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 249 B |
Binary file not shown.
|
After Width: | Height: | Size: 262 B |
+2
@@ -76,6 +76,7 @@ public class VersionControlScreenShots extends GhidraScreenShotGenerator {
|
|||||||
|
|
||||||
UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class);
|
UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class);
|
||||||
captureDialog(d);
|
captureDialog(d);
|
||||||
|
close(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -95,6 +96,7 @@ public class VersionControlScreenShots extends GhidraScreenShotGenerator {
|
|||||||
|
|
||||||
UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class);
|
UndoActionDialog d = waitForDialogComponent(UndoActionDialog.class);
|
||||||
captureDialog(d);
|
captureDialog(d);
|
||||||
|
close(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user