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");