diff --git a/build.xml b/build.xml
index 403e90c6a..346c55ce0 100644
--- a/build.xml
+++ b/build.xml
@@ -32,6 +32,15 @@
+
+
+
+
+
+
+
+
+
@@ -190,4 +199,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugin-src/build.xml b/plugin-src/build.xml
new file mode 100644
index 000000000..c72c6f403
--- /dev/null
+++ b/plugin-src/build.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugin-src/src/net/i2p/itoopie/plugin/Itoopie.java b/plugin-src/src/net/i2p/itoopie/plugin/Itoopie.java
new file mode 100644
index 000000000..4b183138a
--- /dev/null
+++ b/plugin-src/src/net/i2p/itoopie/plugin/Itoopie.java
@@ -0,0 +1,108 @@
+package net.i2p.itoopie.plugin;
+/*
+ * 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.
+ *
+ */
+
+import javax.swing.UIManager;
+
+import net.i2p.I2PAppContext;
+import net.i2p.app.*;
+import static net.i2p.app.ClientAppState.*;
+import net.i2p.util.Log;
+
+import net.i2p.itoopie.gui.GUIHelper;
+import net.i2p.itoopie.gui.TrayManager;
+import net.i2p.itoopie.gui.WindowHandler;
+
+/**
+ *
+ * @author zzz
+ */
+public class Itoopie implements ClientApp {
+ private final I2PAppContext _context;
+ private final Log _log;
+ private final ClientAppManager _mgr;
+ private TrayManager trayManager;
+ private ClientAppState _state = UNINITIALIZED;
+
+ public Itoopie(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
+ _context = ctx;
+ _log = ctx.logManager().getLog(Itoopie.class);
+ _mgr = mgr;
+ _state = INITIALIZED;
+ }
+
+ /**
+ * Not supported
+ */
+ public synchronized static void main(String args[]) {
+ throw new UnsupportedOperationException("Must use ClientApp interface");
+ }
+
+ /////// ClientApp methods
+
+ public synchronized void startup() throws Exception {
+ if (_state != STOPPED && _state != INITIALIZED && _state != START_FAILED) {
+ _log.error("Start while state = " + _state);
+ return;
+ }
+ trayManager = TrayManager.getInstance();
+ trayManager.startManager();
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ GUIHelper.setDefaultStyle();
+ } catch (Exception ex) {}
+ // Popup Main window.
+ WindowHandler.toggleFrames();
+ changeState(RUNNING);
+ }
+
+ public synchronized void shutdown(String[] args) {
+ if (_state == STOPPED)
+ return;
+ changeState(STOPPING);
+ trayManager.stopManager();
+ // TODO stop it
+ trayManager = null;
+ changeState(STOPPED);
+ }
+
+ public ClientAppState getState() {
+ return _state;
+ }
+
+ public String getName() {
+ return "itoopie";
+ }
+
+ public String getDisplayName() {
+ return "Itoopie";
+ }
+
+ /////// end ClientApp methods
+
+ private synchronized void changeState(ClientAppState state) {
+ if (state == _state)
+ return;
+ _state = state;
+ _mgr.notify(this, state, null, null);
+ }
+
+ private synchronized void changeState(ClientAppState state, String msg, Exception e) {
+ if (state == _state)
+ return;
+ _state = state;
+ _mgr.notify(this, state, msg, e);
+ }
+}
diff --git a/plugin/clients.config b/plugin/clients.config
new file mode 100644
index 000000000..bf23ced54
--- /dev/null
+++ b/plugin/clients.config
@@ -0,0 +1,5 @@
+clientApp.0.main=net.i2p.itoopie.plugin.Itoopie
+clientApp.0.name=itoopie
+clientApp.0.delay=0
+clientApp.0.startOnLoad=true
+clientApp.0.classpath=$PLUGIN/lib/itoopie-plugin.jar,$PLUGIN/lib/itoopie.jar,$PLUGIN/lib/commons-logging.jar,$PLUGIN/lib/jchart2d.jar,$PLUGIN/lib/json-smart.jar,$PLUGIN/lib/xmlgraphics-commons.jar
diff --git a/scripts/itoopie.conf b/scripts/itoopie.conf
new file mode 100644
index 000000000..0e49fe591
--- /dev/null
+++ b/scripts/itoopie.conf
@@ -0,0 +1,3 @@
+server.hostname=localhost
+server.password=itoopie
+server.port=7657
diff --git a/scripts/makeplugin.sh b/scripts/makeplugin.sh
new file mode 100755
index 000000000..ed055a608
--- /dev/null
+++ b/scripts/makeplugin.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#
+# basic packaging up of a plugin
+#
+# usage: makeplugin.sh plugindir
+#
+# zzz 2010-02
+# zzz 2014-08 added support for su3 files
+#
+
+if [ -z "$I2P" -a -d "$PWD/../i2p.i2p/pkg-temp" ]; then
+ export I2P=../i2p.i2p/pkg-temp
+fi
+
+if [ ! -d "$I2P" ]; then
+ echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value"
+ echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used."
+ exit 1
+fi
+
+PUBKEYDIR=$HOME/.i2p-plugin-keys
+PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
+PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
+B64KEYFILE=$PUBKEYDIR/plugin-public-signing.txt
+PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt
+PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks
+KEYTYPE=RSA_SHA512_4096
+
+PLUGINDIR=${1:-plugin}
+
+PC=plugin.config
+PCT=${PC}.tmp
+
+if [ ! -d $PLUGINDIR ]
+then
+ echo "You must have a $PLUGINDIR directory"
+ exit 1
+fi
+
+if [ ! -f $PLUGINDIR/$PC ]
+then
+ echo "You must have a $PLUGINDIR/$PC file"
+ exit 1
+fi
+
+SIGNER=`grep '^signer=' $PLUGINDIR/$PC`
+if [ "$?" -ne "0" ]
+then
+ echo "You must have a plugin name in $PC"
+ echo 'For example name=foo'
+ exit 1
+fi
+SIGNER=`echo $SIGNER | cut -f 2 -d '='`
+
+if [ ! -f $PRIVKEYFILE ]
+then
+ echo "Creating new XPI2P DSA keys"
+ mkdir -p $PUBKEYDIR || exit 1
+ java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate keygen $PUBKEYFILE $PRIVKEYFILE || exit 1
+ java -cp $I2P/lib/i2p.jar net.i2p.data.Base64 encode $PUBKEYFILE $B64KEYFILE || exit 1
+ rm -rf logs/
+ chmod 444 $PUBKEYFILE $B64KEYFILE
+ chmod 400 $PRIVKEYFILE
+ echo "Created new XPI2P keys: $PUBKEYFILE $PRIVKEYFILE"
+fi
+
+if [ ! -f $PRIVKEYSTORE ]
+then
+ echo "Creating new SU3 $KEYTYPE keys for $SIGNER"
+ java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File keygen -t $KEYTYPE $PUBKEYSTORE $PRIVKEYSTORE $SIGNER || exit 1
+ echo '*** Save your password in a safe place!!! ***'
+ rm -rf logs/
+ # copy to the router dir so verify will work
+ CDIR=$I2P/certificates/plugin
+ mkdir -p $CDIR || exit 1
+ CFILE=$CDIR/`echo $SIGNER | sed s/@/_at_/`.crt
+ cp $PUBKEYSTORE $CFILE
+ chmod 444 $PUBKEYSTORE
+ chmod 400 $PRIVKEYSTORE
+ chmod 644 $CFILE
+ echo "Created new SU3 keys: $PUBKEYSTORE $PRIVKEYSTORE"
+ echo "Copied public key to $CFILE for testing"
+fi
+
+rm -f plugin.zip
+
+OPWD=$PWD
+cd $PLUGINDIR
+
+grep -q '^name=' $PC
+if [ "$?" -ne "0" ]
+then
+ echo "You must have a plugin name in $PC"
+ echo 'For example name=foo'
+ exit 1
+fi
+
+grep -q '^version=' $PC
+if [ "$?" -ne "0" ]
+then
+ echo "You must have a version in $PC"
+ echo 'For example version=0.1.2'
+ exit 1
+fi
+
+# update the date
+grep -v '^date=' $PC > $PCT
+DATE=`date '+%s000'`
+echo "date=$DATE" >> $PCT
+mv $PCT $PC || exit 1
+
+# add our Base64 key
+grep -v '^key=' $PC > $PCT
+B64KEY=`cat $B64KEYFILE`
+echo "key=$B64KEY" >> $PCT || exit 1
+mv $PCT $PC || exit 1
+
+# zip it
+zip -r $OPWD/plugin.zip * || exit 1
+
+# get the version and use it for the sud header
+VERSION=`grep '^version=' $PC | cut -f 2 -d '='`
+# get the name and use it for the file name
+NAME=`grep '^name=' $PC | cut -f 2 -d '='`
+XPI2P=${NAME}.xpi2p
+SU3=${NAME}.su3
+cd $OPWD
+
+# sign it
+echo 'Signing. ...'
+java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate sign plugin.zip $XPI2P $PRIVKEYFILE $VERSION || exit 1
+java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip $SU3 $PRIVKEYSTORE $VERSION $SIGNER || exit 1
+rm -f plugin.zip
+
+# verify
+echo 'Verifying. ...'
+java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate showversion $XPI2P || exit 1
+java -cp $I2P/lib/i2p.jar -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig $XPI2P || exit 1
+java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File showversion $SU3 || exit 1
+java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File verifysig -k $PUBKEYSTORE $SU3 || exit 1
+rm -rf logs/
+
+echo 'Plugin files created: '
+wc -c $XPI2P
+wc -c $SU3
+
+exit 0
diff --git a/scripts/plugin.config b/scripts/plugin.config
new file mode 100644
index 000000000..5e2af31c0
--- /dev/null
+++ b/scripts/plugin.config
@@ -0,0 +1,8 @@
+name=itoopie
+signer=zzz-plugin@mail.i2p
+description=Router Control
+author=zzz
+updateURL.su3=http://stats.i2p/i2p/plugins/itoopie-update.su3
+websiteURL=http://zzz.i2p/forums/16
+license=Apache 2.0
+min-i2p-version=0.9.11
diff --git a/src/net/i2p/itoopie/gui/TrayManager.java b/src/net/i2p/itoopie/gui/TrayManager.java
index 2670407fb..62c0c8312 100644
--- a/src/net/i2p/itoopie/gui/TrayManager.java
+++ b/src/net/i2p/itoopie/gui/TrayManager.java
@@ -50,7 +50,7 @@ public class TrayManager {
/**
* Add the tray icon to the system tray and start everything up.
*/
- public void startManager() {
+ public synchronized void startManager() {
SwingUtilities.invokeLater(new Runnable(){
public void run(){
if(SystemTray.isSupported()) {
@@ -71,7 +71,20 @@ public class TrayManager {
}
});
}
-
+
+ /**
+ * @since 0.0.5 for plugin only
+ */
+ public synchronized void stopManager() {
+ try {
+ if (trayIcon != null && SystemTray.isSupported()) {
+ SystemTray.getSystemTray().remove(trayIcon);
+ // TODO stop it
+ trayIcon = null;
+ }
+ } catch (Exception e) {}
+ }
+
protected void languageChanged() {
trayIcon.setPopupMenu(getMainMenu());
}