From bfde324f8cc09acffccedee82921d3b45d2df911 Mon Sep 17 00:00:00 2001 From: angryziber Date: Mon, 20 Aug 2007 21:59:08 +0000 Subject: [PATCH] * ConfigDetector introduced to help users check their machine suitability for scanning (especially Windows users) * ConfigDetectorDialog and GettingStartedDialog are now shown on first run * OptionsDialog now offers the Check button for running the ConfigDetector * TODO updated git-svn-id: https://ipscan.svn.sourceforge.net/svnroot/ipscan/trunk@194 375186e5-ef17-0410-b0b6-91563547dcda --- TODO | 4 +- resources/Labels.txt | 7 +- .../azib/ipscan/config/ComponentRegistry.java | 5 + .../azib/ipscan/config/ConfigDetector.java | 166 +++++++++++++++++ src/net/azib/ipscan/config/GlobalConfig.java | 3 + .../azib/ipscan/gui/ConfigDetectorDialog.java | 169 ++++++++++++++++++ .../azib/ipscan/gui/GettingStartedDialog.java | 7 +- src/net/azib/ipscan/gui/MainWindow.java | 13 +- src/net/azib/ipscan/gui/OptionsDialog.java | 22 ++- .../ipscan/gui/feeders/RangeFeederGUI.java | 2 +- 10 files changed, 385 insertions(+), 13 deletions(-) create mode 100644 src/net/azib/ipscan/config/ConfigDetector.java create mode 100644 src/net/azib/ipscan/gui/ConfigDetectorDialog.java diff --git a/TODO b/TODO index b7409668..d8eb90bb 100644 --- a/TODO +++ b/TODO @@ -1,15 +1,13 @@ Before 3.0 beta: * sorting by columns -* check all exporters -* tooltips * export/import of settings * disabling of buttons/menus during scanning * tools->delete menus +* Mac build Before 3.0: -* getting started message * fetcher-specific options * multiple port support web-detect * add new fetchers by configuration of PortTextFetcher diff --git a/resources/Labels.txt b/resources/Labels.txt index 4547b5af..4491798c 100755 --- a/resources/Labels.txt +++ b/resources/Labels.txt @@ -113,9 +113,9 @@ text.fetchers.info.notAvailable=Unfortunately, no additional information about t text.about=%NAME\n\nVersion: %VERSION\nBuild: %BUILD\n\n%COPYLEFT\nVisit %WEBSITE\n\nJRE: %JAVA button.OK=OK button.cancel=Cancel -button.close=Close -button.next=Next -> -button.start=Start +button.close=&Close +button.next=&Next -> +button.start=&Start button.start.img=images/buttons/start.png button.stop=Stop button.stop.img=images/buttons/stop.png @@ -131,6 +131,7 @@ button.insert=&Insert button.add=&Add button.left=<< button.right=>> +button.check=Chec&k... combobox.feeder.tooltip=IP Feeder selection. Change this if you need another source for IP addresses to scan list.unknown.img=images/list/unknown.png list.dead.img=images/list/dead.png diff --git a/src/net/azib/ipscan/config/ComponentRegistry.java b/src/net/azib/ipscan/config/ComponentRegistry.java index 572eaee0..3e10f65b 100755 --- a/src/net/azib/ipscan/config/ComponentRegistry.java +++ b/src/net/azib/ipscan/config/ComponentRegistry.java @@ -25,6 +25,7 @@ import net.azib.ipscan.fetchers.PingFetcher; import net.azib.ipscan.fetchers.PingTTLFetcher; import net.azib.ipscan.fetchers.PortsFetcher; import net.azib.ipscan.fetchers.WebDetectFetcher; +import net.azib.ipscan.gui.ConfigDetectorDialog; import net.azib.ipscan.gui.MainMenu; import net.azib.ipscan.gui.MainWindow; import net.azib.ipscan.gui.OptionsDialog; @@ -77,6 +78,7 @@ public class ComponentRegistry { container.registerComponentInstance(Config.getOpenersConfig()); container.registerComponentInstance(Config.getFavoritesConfig()); container.registerComponentInstance(Labels.getInstance()); + container.registerComponentImplementation(ConfigDetector.class); container.registerComponentImplementation(ExporterRegistry.class); container.registerComponentImplementation(TXTExporter.class); @@ -131,6 +133,7 @@ public class ComponentRegistry { container.registerComponentImplementation(OpenerLauncher.class); container.registerComponentImplementation(MainWindow.class, MainWindow.class, new Parameter[] { new ComponentParameter("mainShell"), + anyComponentParameter, new ComponentParameter("feederArea"), new ComponentParameter("controlsArea"), new ComponentParameter("feederSelectionCombo"), @@ -139,6 +142,7 @@ public class ComponentRegistry { anyComponentParameter, anyComponentParameter, anyComponentParameter, + anyComponentParameter, anyComponentParameter}); container.registerComponentImplementation(ResultTable.class, ResultTable.class, new Parameter[] { new ComponentParameter("mainShell"), @@ -162,6 +166,7 @@ public class ComponentRegistry { anyComponentParameter}); container.registerComponentImplementation(OptionsDialog.class); + container.registerComponentImplementation(ConfigDetectorDialog.class); container.registerComponentImplementation(SelectFetchersDialog.class); container.registerComponentImplementation(StatisticsDialog.class); diff --git a/src/net/azib/ipscan/config/ConfigDetector.java b/src/net/azib/ipscan/config/ConfigDetector.java new file mode 100644 index 00000000..bb09c90c --- /dev/null +++ b/src/net/azib/ipscan/config/ConfigDetector.java @@ -0,0 +1,166 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ +package net.azib.ipscan.config; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class detects some important config parameter values + * that work reliably on the given machine, like maximum number of threads. + * + * @author Anton Keks + */ +public class ConfigDetector { + + private static Logger logger; + private GlobalConfig config; + private DetectorCallback callback; + private int numConnects; + private int numCorrectReads; + + public ConfigDetector(GlobalConfig config) { + this.config = config; + } + + public void setCallback(DetectorCallback callback) { + this.callback = callback; + } + + public void detectMaxThreads() { + // init it here to reduce stuff needed to do in constructor + logger = LoggerFactory.getLogger(); + + numConnects = 0; + numCorrectReads = 0; + + final DetectionServerThread serverThread = new DetectionServerThread(); + serverThread.setDaemon(true); + serverThread.start(); + synchronized (serverThread) { + try { + // wait for port value to become available + serverThread.wait(10000); + } + catch (InterruptedException e) { + } + } + + List threads = new LinkedList(); + + try { + final InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getLocalHost(), serverThread.port); + + for (int i = 0; i < config.maxThreads; i++) { + if (callback != null) + callback.onDetectorTry(); + Thread t = new Thread() { + public void run() { + Socket s = new Socket(); + try { + s.setSoTimeout(config.portTimeout); + s.setTcpNoDelay(true); + s.setSoLinger(true, 0); + s.connect(socketAddress, config.portTimeout); + numConnects++; + byte[] buf = new byte[64]; + s.getInputStream().read(buf); + if (buf[0] == 'H') { + numCorrectReads++; + if (callback != null) + callback.onDetectorSuccess(); + } + s.close(); + } + catch (Exception e) { + logger.log(Level.FINE, "Failure on local port " + s.getLocalPort()); + } + } + }; + threads.add(t); + t.start(); + } + } + catch (UnknownHostException e1) { + // getLocalHost should not fail + LoggerFactory.getLogger().log(Level.WARNING, "", e1); + } + + // by this time, all the thread must be finished + try { + for (Thread t : threads) { + t.join(); + } + } + catch (InterruptedException e) { + } + serverThread.interrupt(); + } + + public int getSuccessfulConnectCount() { + return numConnects; + } + + public int getSuccessfulDataReadCount() { + return numCorrectReads; + } + + static class DetectionServerThread extends Thread { + int port; + + public void run() { + try { + ServerSocket server = new ServerSocket(0); + //server.setSoTimeout(); + port = server.getLocalPort(); + synchronized (this) { + // tell the other thread that port value is now available + this.notify(); + } + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "Started fake server on port " + port); + } + while (!interrupted()) { + final Socket s = server.accept(); + new Thread() { + public void run() { + try { + s.setTcpNoDelay(true); + s.setSoLinger(true, 0); + OutputStream stream = s.getOutputStream(); + stream.write(("Hello " + s.getPort()).getBytes()); + stream.flush(); + sleep(10000); + s.close(); + } + catch (Exception e) { + logger.log(Level.FINER, "On port " + s.getPort(), e); + } + } + }.start(); + } + logger.log(Level.FINE, "Stopped fake server"); + } + catch (IOException e) { + logger.log(Level.FINE, null, e); + } + } + } + + public interface DetectorCallback { + void onDetectorTry(); + void onDetectorSuccess(); + } +} diff --git a/src/net/azib/ipscan/config/GlobalConfig.java b/src/net/azib/ipscan/config/GlobalConfig.java index 092acac3..e51c4240 100755 --- a/src/net/azib/ipscan/config/GlobalConfig.java +++ b/src/net/azib/ipscan/config/GlobalConfig.java @@ -16,6 +16,7 @@ public final class GlobalConfig { private Preferences preferences; + public boolean isFirstRun; public int maxThreads; public int threadDelay; public int activeFeeder; @@ -43,6 +44,7 @@ public final class GlobalConfig { GlobalConfig(Preferences preferences) { this.preferences = preferences; + isFirstRun = preferences.getBoolean("firstRun", true); maxThreads = preferences.getInt("maxThreads", 100); threadDelay = preferences.getInt("threadDelay", 20); activeFeeder = preferences.getInt("activeFeeder", 0); @@ -65,6 +67,7 @@ public final class GlobalConfig { * Stores all the internal properties to the storage media */ public void store() { + preferences.putBoolean("firstRun", isFirstRun); preferences.putInt("maxThreads", maxThreads); preferences.putInt("threadDelay", threadDelay); preferences.putInt("activeFeeder", activeFeeder); diff --git a/src/net/azib/ipscan/gui/ConfigDetectorDialog.java b/src/net/azib/ipscan/gui/ConfigDetectorDialog.java new file mode 100644 index 00000000..c1179255 --- /dev/null +++ b/src/net/azib/ipscan/gui/ConfigDetectorDialog.java @@ -0,0 +1,169 @@ +/** + * This file is a part of Angry IP Scanner source code, + * see http://www.azib.net/ for more information. + * Licensed under GPLv2. + */ +package net.azib.ipscan.gui; + +import net.azib.ipscan.config.ConfigDetector; +import net.azib.ipscan.config.GlobalConfig; +import net.azib.ipscan.config.Labels; +import net.azib.ipscan.gui.util.LayoutHelper; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.MessageBox; +import org.eclipse.swt.widgets.ProgressBar; +import org.eclipse.swt.widgets.Shell; + +/** + * ConfigDetectorDialog - a GUI for {@link ConfigDetector} + * + * @author Anton Keks + */ +public class ConfigDetectorDialog extends AbstractModalDialog implements ConfigDetector.DetectorCallback { + + private GlobalConfig config; + private ConfigDetector configDetector; + private ProgressBar tryProgressBar; + private int tryCount; + private ProgressBar successProgressBar; + private int successCount; + private Button startButton; + private Button closeButton; + private Label tryCountLabel; + private Label successCountLabel; + + public ConfigDetectorDialog(GlobalConfig config, ConfigDetector configDetector) { + this.config = config; + this.configDetector = configDetector; + this.configDetector.setCallback(this); + } + + @Override + public void open() { + createShell(); + super.open(); + } + + /** + * This method initializes shell + */ + private void createShell() { + Display currentDisplay = Display.getCurrent(); + Shell parent = currentDisplay != null ? currentDisplay.getActiveShell() : null; + shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM); + shell.setLayout(LayoutHelper.formLayout(10, 10, 10)); + + shell.setText(Labels.getLabel("title.configDetect")); + + Label infoLabel = new Label(shell, SWT.WRAP); + infoLabel.setText((config.isFirstRun ? Labels.getLabel("text.configDetect.firstRun") : "") + Labels.getLabel("text.configDetect")); + infoLabel.setLayoutData(LayoutHelper.formData(340, SWT.DEFAULT, new FormAttachment(0), new FormAttachment(100), new FormAttachment(0), null)); + + Label tryLabel = new Label(shell, SWT.NONE); + tryLabel.setText(Labels.getLabel("text.configDetect.tries")); + tryLabel.setLayoutData(LayoutHelper.formData(new FormAttachment(0), null, new FormAttachment(infoLabel, 10), null)); + tryCountLabel = new Label(shell, SWT.NONE); + tryCountLabel.setLayoutData(LayoutHelper.formData(new FormAttachment(tryLabel, -5), new FormAttachment(100), new FormAttachment(infoLabel, 10), null)); + tryProgressBar = new ProgressBar(shell, SWT.NONE); + tryProgressBar.setLayoutData(LayoutHelper.formData(new FormAttachment(0), new FormAttachment(100), new FormAttachment(tryLabel), null)); + + Label successLabel = new Label(shell, SWT.NONE); + successLabel.setText(Labels.getLabel("text.configDetect.successes")); + successLabel.setLayoutData(LayoutHelper.formData(new FormAttachment(0), null, new FormAttachment(tryProgressBar, 10), null)); + successCountLabel = new Label(shell, SWT.NONE); + successCountLabel.setLayoutData(LayoutHelper.formData(new FormAttachment(successLabel, -5), new FormAttachment(100), new FormAttachment(tryProgressBar, 10), null)); + successProgressBar = new ProgressBar(shell, SWT.NONE); + successProgressBar.setLayoutData(LayoutHelper.formData(new FormAttachment(0), new FormAttachment(100), new FormAttachment(successLabel), null)); + + startButton = new Button(shell, SWT.NONE); + startButton.setText(Labels.getLabel("button.start")); + startButton.addListener(SWT.Selection, new StartButtonListener()); + + closeButton = new Button(shell, SWT.NONE); + closeButton.setText(Labels.getLabel("button.close")); + closeButton.addListener(SWT.Selection, new Listener() { + public void handleEvent(Event event) { + shell.close(); + } + }); + + positionButtonsInFormLayout(startButton, closeButton, successProgressBar); + + shell.pack(); + } + + public void onDetectorTry() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + tryCount++; + tryCountLabel.setText(Integer.toString(tryCount)); + tryProgressBar.setSelection(tryCount); + } + }); + } + + public void onDetectorSuccess() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + successCount++; + successCountLabel.setText(Integer.toString(successCount)); + successProgressBar.setSelection(successCount); + } + }); + } + + private void onStart() { + startButton.setEnabled(false); + closeButton.setEnabled(false); + tryCount = 0; + tryCountLabel.setText("0"); + tryProgressBar.setMaximum(config.maxThreads); + tryProgressBar.setSelection(0); + successCount = 0; + successCountLabel.setText("0"); + successProgressBar.setMaximum(config.maxThreads); + successProgressBar.setSelection(0); + } + + private void onFinish() { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + startButton.setEnabled(true); + closeButton.setEnabled(true); + + // ask the user if they want to use the detected value + if (config.isFirstRun || config.maxThreads != successCount) { + MessageBox box = new MessageBox(shell, SWT.YES | SWT.NO | SWT.ICON_QUESTION); + box.setText(Labels.getLabel("title.configDetect")); + box.setMessage(String.format(Labels.getLabel("text.configDetect.setMaxThreads"), successCount) + + (config.isFirstRun ? Labels.getLabel("text.configDetect.setMaxThreads.firstRun") : "")); + if (box.open() == SWT.YES) { + config.maxThreads = successCount; + shell.close(); + } + } + } + }); + } + + private class StartButtonListener implements Listener { + public void handleEvent(Event event) { + onStart(); + // start detection in separate thread + new Thread() { + public void run() { + configDetector.detectMaxThreads(); + onFinish(); + } + }.start(); + } + } + +} diff --git a/src/net/azib/ipscan/gui/GettingStartedDialog.java b/src/net/azib/ipscan/gui/GettingStartedDialog.java index 5a62ac9b..16cbe4ba 100755 --- a/src/net/azib/ipscan/gui/GettingStartedDialog.java +++ b/src/net/azib/ipscan/gui/GettingStartedDialog.java @@ -28,9 +28,14 @@ public class GettingStartedDialog extends AbstractModalDialog { private Button nextButton; public GettingStartedDialog() { - createShell(); } + @Override + public void open() { + createShell(); + super.open(); + } + /** * This method initializes shell */ diff --git a/src/net/azib/ipscan/gui/MainWindow.java b/src/net/azib/ipscan/gui/MainWindow.java index e8b23ace..3833b5f5 100755 --- a/src/net/azib/ipscan/gui/MainWindow.java +++ b/src/net/azib/ipscan/gui/MainWindow.java @@ -6,6 +6,7 @@ package net.azib.ipscan.gui; import net.azib.ipscan.config.Config; +import net.azib.ipscan.config.GlobalConfig; import net.azib.ipscan.config.Labels; import net.azib.ipscan.config.Version; import net.azib.ipscan.gui.MainMenu.CommandsMenu; @@ -42,6 +43,7 @@ import org.eclipse.swt.widgets.Shell; public class MainWindow { private Shell shell; + private GlobalConfig globalConfig; private Composite feederArea; @@ -51,7 +53,8 @@ public class MainWindow { /** * Creates and initializes the main window. */ - public MainWindow(Shell shell, Composite feederArea, Composite controlsArea, Combo feederSelectionCombo, Button startStopButton, StartStopScanningAction startStopScanningAction, ResultTable resultTable, StatusBar statusBar, CommandsMenu resultsContextMenu, FeederGUIRegistry feederGUIRegistry) { + public MainWindow(Shell shell, GlobalConfig globalConfig, Composite feederArea, Composite controlsArea, Combo feederSelectionCombo, Button startStopButton, StartStopScanningAction startStopScanningAction, ResultTable resultTable, StatusBar statusBar, CommandsMenu resultsContextMenu, FeederGUIRegistry feederGUIRegistry, ConfigDetectorDialog configDetectorDialog) { + this.globalConfig = globalConfig; initShell(shell); @@ -71,6 +74,12 @@ public class MainWindow { // set bounds twice - a workaround for a bug in SWT GTK + Compiz (otherwise window gets smaller and smaller each time) shell.setBounds(Config.getDimensionsConfig().getWindowBounds()); } + + if (globalConfig.isFirstRun) { + configDetectorDialog.open(); + new GettingStartedDialog().open(); + globalConfig.isFirstRun = false; + } } /** @@ -153,7 +162,7 @@ public class MainWindow { IPFeederSelectionListener feederSelectionListener = new IPFeederSelectionListener(); feederSelectionCombo.addSelectionListener(feederSelectionListener); // initialize the selected feeder GUI - feederSelectionCombo.select(Config.getGlobal().activeFeeder); + feederSelectionCombo.select(globalConfig.activeFeeder); feederSelectionCombo.setToolTipText(Labels.getLabel("combobox.feeder.tooltip")); feederSelectionListener.widgetSelected(null); diff --git a/src/net/azib/ipscan/gui/OptionsDialog.java b/src/net/azib/ipscan/gui/OptionsDialog.java index 500b20ec..d82a7cc4 100755 --- a/src/net/azib/ipscan/gui/OptionsDialog.java +++ b/src/net/azib/ipscan/gui/OptionsDialog.java @@ -24,8 +24,10 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; @@ -40,6 +42,7 @@ public class OptionsDialog extends AbstractModalDialog { private PingerRegistry pingerRegistry; private GlobalConfig globalConfig; + private ConfigDetectorDialog configDetectorDialog; private Button okButton; private Button cancelButton; @@ -67,9 +70,10 @@ public class OptionsDialog extends AbstractModalDialog { private Button showInfoCheckbox; private Button askConfirmationCheckbox; - public OptionsDialog(PingerRegistry pingerRegistry, GlobalConfig globalConfig) { + public OptionsDialog(PingerRegistry pingerRegistry, GlobalConfig globalConfig, ConfigDetectorDialog configDetectorDialog) { this.pingerRegistry = pingerRegistry; this.globalConfig = globalConfig; + this.configDetectorDialog = configDetectorDialog; } @Override @@ -177,8 +181,7 @@ public class OptionsDialog extends AbstractModalDialog { threadsGroup.setText(Labels.getLabel("options.threads")); threadsGroup.setLayout(groupLayout); - GridData gridData = new GridData(); - gridData.widthHint = 80; + GridData gridData = new GridData(80, SWT.DEFAULT); Label label; @@ -191,6 +194,11 @@ public class OptionsDialog extends AbstractModalDialog { label.setText(Labels.getLabel("options.threads.maxThreads")); maxThreadsText = new Text(threadsGroup, SWT.BORDER); maxThreadsText.setLayoutData(gridData); + new Label(threadsGroup, SWT.NONE); + Button detectButton = new Button(threadsGroup, SWT.NONE); + detectButton.setText(Labels.getLabel("button.check")); + detectButton.setLayoutData(gridData); + detectButton.addListener(SWT.Selection, new CheckButtonListener()); Group pingingGroup = new Group(scanningTab, SWT.NONE); pingingGroup.setLayout(groupLayout); @@ -467,4 +475,12 @@ public class OptionsDialog extends AbstractModalDialog { public void keyReleased(KeyEvent e) { } } + + class CheckButtonListener implements Listener { + public void handleEvent(Event event) { + globalConfig.maxThreads = Integer.parseInt(maxThreadsText.getText()); + configDetectorDialog.open(); + loadOptions(); + } + } } diff --git a/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java b/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java index eb5d8da9..773ed6b3 100755 --- a/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java +++ b/src/net/azib/ipscan/gui/feeders/RangeFeederGUI.java @@ -142,7 +142,7 @@ public class RangeFeederGUI extends AbstractFeederGUI { netmaskCombo.setVisibleItemCount(10); netmaskCombo.add("/16"); netmaskCombo.add("/24"); - netmaskCombo.add("/28"); + netmaskCombo.add("/26"); // Warning: IPv4 specific netmasks netmaskCombo.add("255...192"); netmaskCombo.add("255...128");