diff --git a/resources/Labels.txt b/resources/Labels.txt index 4d54dff0..c18f55f0 100755 --- a/resources/Labels.txt +++ b/resources/Labels.txt @@ -22,7 +22,6 @@ menu.commands.delete=&Delete IP(s) menu.commands.copy=&Copy IP menu.commands.copyDetails=Co&py details menu.commands.show=Show -menu.commands.editComment=S&et IP comment... menu.commands.open=Open menu.commands.open.edit=Edit openers... menu.favorites=Fa&vorites @@ -77,7 +76,6 @@ title.openers.edit=Edit Openers title.fetchers.select=Select Fetchers title.rename=Rename title.find=Find -title.editComment=Set comment text.error=Error text.userError=Problem text.ip=IP @@ -92,7 +90,7 @@ text.favorite.edit=Below you can rearrange or delete favorites text.find=Enter the text to search for text.find.notFound=Nothing was found. text.find.restart=Would you like to start from the beginning? -text.editComment=Specify comment for the IP address +text.comment.edit=Enter IP comment here text.configDetect=We will try to detect the number of threads that works reliably on this machine by connecting to a known host many times simultaneously using the configured port timeout.\n\nPlease provide host and port that is 100% open and works, e.g. a proxy or web server on your network. text.configDetect.host=Host: text.configDetect.port=Port: @@ -202,7 +200,7 @@ fetcher.ports.info=Shows the list of open ports from the ones that were scanned. fetcher.ports.filtered=Filtered Ports fetcher.ports.filtered.info=Shows the list of filtered ports from the ones that were scanned.\n\nA port is filtered when no response is being received to the connection attempt within specified amount of time. If a port is filtered, then it is probably blocked by a firewall. fetcher.comment=Comments -fetcher.comment.info=Allows writing of comments for each host.\nThe comments are persisted and always shown then the host is scanned. +fetcher.comment.info=Shows the comments entered for the address using the IP Details window.\nThe comments are persisted and are shown when this host is scanned again. fetcher.webDetect=Web detect fetcher.webDetect.info=Detects the web server software name and version, if possible.\n\nWorks by sending a HEAD request and reading the Server HTTP header from the response. fetcher.httpSender=HTTP Sender diff --git a/src/net/azib/ipscan/core/ScanningResult.java b/src/net/azib/ipscan/core/ScanningResult.java index c27b073b..9341c447 100755 --- a/src/net/azib/ipscan/core/ScanningResult.java +++ b/src/net/azib/ipscan/core/ScanningResult.java @@ -7,8 +7,11 @@ package net.azib.ipscan.core; import java.net.InetAddress; import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import net.azib.ipscan.fetchers.Fetcher; + /** * The holder of scanning result for a single IP address. * @@ -33,6 +36,9 @@ public class ScanningResult { /** Scanning result type */ private ResultType type; + /** reference to the containing list */ + ScanningResultList resultList; + /** * Creates a new instance, initializing the first value to the * provided address @@ -97,4 +103,29 @@ public class ScanningResult { values[fetcherIndex] = value; } + /** + * Returns all results for this IP address as a String. + * This is used in showing the IP Details dialog box. + * + * @param index + * @return human-friendly text representation of results + */ + public String toString() { + // cross-platform newline :-) + String newLine = System.getProperty("line.separator"); + + StringBuffer details = new StringBuffer(1024); + Iterator iterator = getValues().iterator(); + List fetchers = resultList.getFetchers(); + for (int i = 0; iterator.hasNext(); i++) { + String fetcherName = fetchers.get(i).getName(); + details.append(fetcherName).append(":\t"); + Object value = iterator.next(); + details.append(value != null ? value : ""); + details.append(newLine); + } + return details.toString(); + } + + } diff --git a/src/net/azib/ipscan/core/ScanningResultList.java b/src/net/azib/ipscan/core/ScanningResultList.java index 40810b0d..b6883653 100755 --- a/src/net/azib/ipscan/core/ScanningResultList.java +++ b/src/net/azib/ipscan/core/ScanningResultList.java @@ -118,6 +118,7 @@ public class ScanningResultList implements Iterable { if (resultIndexes.put(result.getAddress(), index) != null) throw new IllegalStateException(result.getAddress() + " is already registered in the list"); + result.resultList = this; resultList.add(index, result); // if the result is already ready, then update statistics right away @@ -145,30 +146,6 @@ public class ScanningResultList implements Iterable { return resultIndexes.get(result.getAddress()); } - /** - * Returns all results for a particular IP address as a String. - * This is used in showing the IP Details dialog box. - * - * @param index - * @return human-friendly text representation of results - */ - public synchronized String getResultAsString(int index) { - // cross-platform newline :-) - String newLine = System.getProperty("line.separator"); - - ScanningResult scanningResult = resultList.get(index); - StringBuffer details = new StringBuffer(1024); - Iterator iterator = scanningResult.getValues().iterator(); - for (int i = 0; iterator.hasNext(); i++) { - String fetcherName = selectedFetchers.get(i).getName(); - details.append(fetcherName).append(":\t"); - Object value = iterator.next(); - details.append(value != null ? value : ""); - details.append(newLine); - } - return details.toString(); - } - /** * Clears previous scanning results, prepares for a new scan. */ diff --git a/src/net/azib/ipscan/gui/DetailsWindow.java b/src/net/azib/ipscan/gui/DetailsWindow.java index 4a417e00..bd15f96e 100755 --- a/src/net/azib/ipscan/gui/DetailsWindow.java +++ b/src/net/azib/ipscan/gui/DetailsWindow.java @@ -3,11 +3,19 @@ */ package net.azib.ipscan.gui; +import net.azib.ipscan.config.CommentsConfig; import net.azib.ipscan.config.GUIConfig; import net.azib.ipscan.config.Labels; +import net.azib.ipscan.core.ScanningResult; +import net.azib.ipscan.fetchers.CommentFetcher; +import net.azib.ipscan.gui.util.LayoutHelper; import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; @@ -21,10 +29,14 @@ import org.eclipse.swt.widgets.Text; public class DetailsWindow extends AbstractModalDialog { private GUIConfig guiConfig; + private CommentsConfig commentsConfig; private ResultTable resultTable; + + private Text commentsText; - public DetailsWindow(GUIConfig guiConfig, ResultTable resultTable) { + public DetailsWindow(GUIConfig guiConfig, CommentsConfig commentsConfig, ResultTable resultTable) { this.guiConfig = guiConfig; + this.commentsConfig = commentsConfig; this.resultTable = resultTable; } @@ -40,31 +52,74 @@ public class DetailsWindow extends AbstractModalDialog { private void createShell(Shell parent) { shell = new Shell(parent, SWT.TOOL | SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE); shell.setText(Labels.getLabel("title.details")); - shell.setSize(guiConfig.detailsWindowSize); shell.setImage(parent.getImage()); - FillLayout fillLayout = new FillLayout(); - fillLayout.spacing = 3; - fillLayout.marginHeight = 3; - fillLayout.marginWidth = 3; - shell.setLayout(fillLayout); + shell.setLayout(LayoutHelper.formLayout(3, 3, 3)); + shell.setSize(guiConfig.detailsWindowSize); + ScanningResult result = resultTable.getSelectedResult(); + + commentsText = new Text(shell, SWT.BORDER); + commentsText.pack(); + commentsText.setLayoutData(LayoutHelper.formData(new FormAttachment(0), new FormAttachment(100), null, new FormAttachment(100))); + CommentsTextListener commentsTextListener = new CommentsTextListener(); + commentsText.addFocusListener(commentsTextListener); + commentsText.addModifyListener(commentsTextListener); + String comment = commentsConfig.getComment(result.getAddress()); + if (comment != null) { + commentsText.setText(comment); + } + else { + commentsTextListener.focusLost(null); + } + Text detailsText = new Text(shell, SWT.BORDER | SWT.READ_ONLY | SWT.MULTI | SWT.V_SCROLL | SWT.WRAP); - detailsText.setText(resultTable.getIPDetails()); + detailsText.setText(result.toString()); detailsText.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); detailsText.setTabs(32); - - shell.addListener(SWT.Close, new Listener() { - public void handleEvent(Event event) { - guiConfig.detailsWindowSize = shell.getSize(); - } - }); - detailsText.addListener(SWT.Traverse, new Listener() { - public void handleEvent(Event e) { - if (e.detail == SWT.TRAVERSE_RETURN) { - shell.close(); - } - } - }); + detailsText.setLayoutData(LayoutHelper.formData(new FormAttachment(0), new FormAttachment(100), new FormAttachment(0), new FormAttachment(commentsText))); + + Listener traverseListener = new TraverseListener(); + detailsText.addListener(SWT.Traverse, traverseListener); + commentsText.addListener(SWT.Traverse, traverseListener); + + shell.layout(); + detailsText.forceFocus(); } + class CommentsTextListener implements FocusListener, ModifyListener { + String defaultText = Labels.getLabel("text.comment.edit"); + + public void focusGained(FocusEvent e) { + if (commentsText.getText().equals(defaultText)) { + commentsText.setText(""); + commentsText.setForeground(commentsText.getDisplay().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND)); + } + } + + public void focusLost(FocusEvent e) { + if (commentsText.getText().isEmpty()) { + commentsText.setText(defaultText); + commentsText.setForeground(commentsText.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW)); + } + } + + public void modifyText(ModifyEvent e) { + String newComment = commentsText.getText(); + if (!defaultText.equals(newComment)) { + // store the new comment + commentsConfig.setComment(resultTable.getSelectedResult().getAddress(), newComment); + // now update the result table for user to immediately see the change + resultTable.updateResult(resultTable.getSelectionIndex(), CommentFetcher.ID, newComment); + } + } + } + + class TraverseListener implements Listener { + public void handleEvent(Event e) { + if (e.detail == SWT.TRAVERSE_RETURN) { + guiConfig.detailsWindowSize = shell.getSize(); + shell.close(); + } + } + } } diff --git a/src/net/azib/ipscan/gui/MainMenu.java b/src/net/azib/ipscan/gui/MainMenu.java index cbb14f60..6adfbf91 100755 --- a/src/net/azib/ipscan/gui/MainMenu.java +++ b/src/net/azib/ipscan/gui/MainMenu.java @@ -145,8 +145,6 @@ public class MainMenu implements Startable { initMenuItem(menu, null, null, null, null); initMenuItem(menu, "menu.commands.copy", Platform.MAC_OS ? "⌘C" : "Ctrl+C", /* this is not a global key binding */ null, initListener(CommandsActions.CopyIP.class)); initMenuItem(menu, "menu.commands.copyDetails", null, null, initListener(CommandsActions.CopyIPDetails.class)); - initMenuItem(menu, null, null, null, null); - initMenuItem(menu, "menu.commands.editComment", "Ctrl+E", new Integer(SWT.MOD1 | 'E'), initListener(CommandsActions.EditComment.class)); initMenuItem(menu, null, null, null, null); createOpenersMenu(menu); // initMenuItem(subMenu, "menu.commands.show", null, initListener()); diff --git a/src/net/azib/ipscan/gui/ResultTable.java b/src/net/azib/ipscan/gui/ResultTable.java index c083bd66..f4f153f2 100755 --- a/src/net/azib/ipscan/gui/ResultTable.java +++ b/src/net/azib/ipscan/gui/ResultTable.java @@ -15,6 +15,7 @@ import net.azib.ipscan.core.ScanningResult.ResultType; import net.azib.ipscan.core.state.ScanningState; import net.azib.ipscan.core.state.StateMachine; import net.azib.ipscan.core.state.StateTransitionListener; +import net.azib.ipscan.fetchers.CommentFetcher; import net.azib.ipscan.fetchers.Fetcher; import net.azib.ipscan.fetchers.FetcherRegistry; import net.azib.ipscan.fetchers.FetcherRegistryUpdateListener; @@ -159,12 +160,27 @@ public class ResultTable extends Table implements FetcherRegistryUpdateListener, } /** - * Returns the details about the currently selected IP address + * Changes the specified value + * @param fetcherId + * @param newValue + */ + public void updateResult(int index, String fetcherId, Object newValue) { + int fetcherIndex = fetcherRegistry.getSelectedFetcherIndex(CommentFetcher.ID); + if (fetcherIndex >= 0) { + // update the value in the results + scanningResults.getResult(index).setValue(fetcherIndex, newValue); + // update visual representation + clear(index); + } + } + + /** + * Returns the currently seelcted resusult * @return */ - public String getIPDetails() { + public ScanningResult getSelectedResult() { int selectedIndex = getSelectionIndex(); - return scanningResults.getResultAsString(selectedIndex); + return scanningResults.getResult(selectedIndex); } public void remove(int[] indices) { diff --git a/src/net/azib/ipscan/gui/actions/CommandsActions.java b/src/net/azib/ipscan/gui/actions/CommandsActions.java index 66511b91..c3c79e9a 100755 --- a/src/net/azib/ipscan/gui/actions/CommandsActions.java +++ b/src/net/azib/ipscan/gui/actions/CommandsActions.java @@ -5,22 +5,15 @@ */ package net.azib.ipscan.gui.actions; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import net.azib.ipscan.config.CommentsConfig; import net.azib.ipscan.config.Labels; import net.azib.ipscan.config.OpenersConfig; import net.azib.ipscan.config.OpenersConfig.Opener; -import net.azib.ipscan.core.ScanningResultList; import net.azib.ipscan.core.UserErrorException; import net.azib.ipscan.core.state.ScanningState; import net.azib.ipscan.core.state.StateMachine; -import net.azib.ipscan.fetchers.CommentFetcher; import net.azib.ipscan.fetchers.FetcherRegistry; import net.azib.ipscan.gui.DetailsWindow; import net.azib.ipscan.gui.EditOpenersDialog; -import net.azib.ipscan.gui.InputDialog; import net.azib.ipscan.gui.ResultTable; import net.azib.ipscan.gui.StatusBar; import net.azib.ipscan.gui.actions.ToolsActions.TableSelection; @@ -159,7 +152,7 @@ public class CommandsActions { public void handleEvent(Event event) { checkSelection(resultTable); Clipboard clipboard = new Clipboard(event.display); - clipboard.setContents(new Object[] {resultTable.getIPDetails()}, new Transfer[] {TextTransfer.getInstance()}); + clipboard.setContents(new Object[] {resultTable.getSelectedResult().toString()}, new Transfer[] {TextTransfer.getInstance()}); clipboard.dispose(); } } @@ -199,45 +192,7 @@ public class CommandsActions { } } - - public static final class EditComment implements Listener { - private final ResultTable resultTable; - private final ScanningResultList results; - private final CommentsConfig commentsConfig; - private final FetcherRegistry fetcherRegistry; - public EditComment(ResultTable resultTable, ScanningResultList results, CommentsConfig commentsConfig, FetcherRegistry fetcherRegistry) { - this.resultTable = resultTable; - this.results = results; - this.commentsConfig = commentsConfig; - this.fetcherRegistry = fetcherRegistry; - } - - public void handleEvent(Event event) { - checkSelection(resultTable); - try { - int index = resultTable.getSelectionIndex(); - InetAddress address = InetAddress.getByName(resultTable.getItem(index).getText()); - String comment = commentsConfig.getComment(address); - String newComment = new InputDialog(Labels.getLabel("title.editComment"), Labels.getLabel("text.editComment")).open(comment); - if (newComment != null) { - commentsConfig.setComment(address, newComment); - // now update the result table for user to immediately see the change - int fetcherIndex = fetcherRegistry.getSelectedFetcherIndex(CommentFetcher.ID); - if (fetcherIndex >= 0) { - // update the value in the results - results.getResult(index).setValue(fetcherIndex, newComment); - // update visual representation - resultTable.clear(index); - } - } - } - catch (UnknownHostException e) { - // should not happen - } - } - } - public static final class EditOpeners implements Listener { private final FetcherRegistry fetcherRegistry; diff --git a/test/net/azib/ipscan/core/ScanningResultListTest.java b/test/net/azib/ipscan/core/ScanningResultListTest.java index f4eabdc9..b55728a9 100755 --- a/test/net/azib/ipscan/core/ScanningResultListTest.java +++ b/test/net/azib/ipscan/core/ScanningResultListTest.java @@ -297,7 +297,7 @@ public class ScanningResultListTest { result.setValue(2, "xxxxx"); result.setValue(3, null); - String s = scanningResults.getResultAsString(0); + String s = scanningResults.getResult(0).toString(); String ln = System.getProperty("line.separator"); assertTrue(s.endsWith(ln)); assertTrue(s.indexOf(fetchers.get(0).getName() + ":\t172.28.43.55" + ln) >= 0);