diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelConfig.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..93212e3f19432d0b2d140c15aec0582dd9d19b81
--- /dev/null
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelConfig.java
@@ -0,0 +1,715 @@
+package net.i2p.i2ptunnel;
+
+import java.security.GeneralSecurityException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.i2p.I2PAppContext;
+import net.i2p.client.I2PClient;
+import net.i2p.crypto.KeyGenerator;
+import net.i2p.crypto.SigType;
+import net.i2p.data.Base64;
+import net.i2p.data.Destination;
+import net.i2p.data.SimpleDataStructure;
+import net.i2p.util.ConcurrentHashSet;
+import net.i2p.util.PasswordManager;
+
+/**
+ * Helper class to generate a valid TunnelController configuration from provided
+ * settings.
+ *
+ * @since 0.9.19 logic moved from IndexBean
+ */
+public class TunnelConfig {
+    public static final String SHARED_CLIENT_NICKNAME = "shared clients";
+
+    private static final String OPT = TunnelController.PFX_OPTION;
+
+    protected final I2PAppContext _context;
+
+    private String _type;
+    private String _name;
+    private String _description;
+    private String _i2cpHost;
+    private String _i2cpPort;
+    private int _tunnelDepth = -1;
+    private int _tunnelQuantity = -1;
+    private int _tunnelVariance = -1;
+    private int _tunnelBackupQuantity = -1;
+    private boolean _connectDelay;
+    private String _customOptions;
+    private String _proxyList;
+    private int _port = -1;
+    private String _reachableBy;
+    private String _targetDestination;
+    private String _targetHost;
+    private int _targetPort = -1;
+    private String _spoofedHost;
+    private String _privKeyFile;
+    private String _profile;
+    private boolean _startOnLoad;
+    private boolean _sharedClient;
+    private final Set<String> _booleanOptions;
+    private final Map<String, String> _otherOptions;
+    private String _newProxyUser;
+    private String _newProxyPW;
+    private Destination _dest;
+
+    public TunnelConfig() {
+        _context = I2PAppContext.getGlobalContext();
+        _booleanOptions = new ConcurrentHashSet<String>(4);
+        _otherOptions = new ConcurrentHashMap<String, String>(4);
+    }
+
+    /**
+     * What type of tunnel (httpclient, ircclient, client, or server).  This is 
+     * required when adding a new tunnel.
+     *
+     */
+    public void setType(String type) { 
+        _type = (type != null ? type.trim() : null);   
+    }
+    public String getType() {
+        return _type;
+    }
+
+    /** Short name of the tunnel */
+    public void setName(String name) { 
+        _name = (name != null ? name.trim() : null);
+    }
+    /** one line description */
+    public void setDescription(String description) { 
+        // '#' will blow up DataHelper.storeProps()
+        _description = (description != null ? description.replace('#', ' ').trim() : null);
+    }
+    /** I2CP host the router is on, ignored when in router context */
+    public void setClientHost(String host) {
+        _i2cpHost = (host != null ? host.trim() : null);
+    }
+    /** I2CP port the router is on, ignored when in router context */
+    public void setClientPort(String port) {
+        _i2cpPort = (port != null ? port.trim() : null);
+    }
+    /** how many hops to use for inbound tunnels */
+    public void setTunnelDepth(int tunnelDepth) { 
+        _tunnelDepth = tunnelDepth;
+    }
+    /** how many parallel inbound tunnels to use */
+    public void setTunnelQuantity(int tunnelQuantity) { 
+        _tunnelQuantity = tunnelQuantity;
+    }
+    /** how much randomisation to apply to the depth of tunnels */
+    public void setTunnelVariance(int tunnelVariance) { 
+        _tunnelVariance = tunnelVariance;
+    }
+    /** how many tunnels to hold in reserve to guard against failures */
+    public void setTunnelBackupQuantity(int tunnelBackupQuantity) { 
+        _tunnelBackupQuantity = tunnelBackupQuantity;
+    }
+    /** what I2P session overrides should be used */
+    public void setCustomOptions(String customOptions) { 
+        _customOptions = (customOptions != null ? customOptions.trim() : null);
+    }
+    /** what HTTP outproxies should be used (httpclient specific) */
+    public void setProxyList(String proxyList) { 
+        _proxyList = (proxyList != null ? proxyList.trim() : null);
+    }
+    /** what port should this client/httpclient/ircclient listen on */
+    public void setPort(int port) { 
+        _port = port;
+    }
+    /** 
+     * what interface should this client/httpclient/ircclient listen on
+     */
+    public void setReachableBy(String reachableBy) { 
+        _reachableBy = (reachableBy != null ? reachableBy.trim() : null);
+    }
+    /** What peer does this client tunnel point at */
+    public void setTargetDestination(String dest) { 
+        _targetDestination = (dest != null ? dest.trim() : null);
+    }
+    /** What host does this server tunnel point at */
+    public void setTargetHost(String host) { 
+        _targetHost = (host != null ? host.trim() : null);
+    }
+    /** What port does this server tunnel point at */
+    public void setTargetPort(int port) { 
+        _targetPort = port;
+    }
+    /** What host does this http server tunnel spoof */
+    public void setSpoofedHost(String host) { 
+        _spoofedHost = (host != null ? host.trim() : null);
+    }
+    /** What filename is this server tunnel's private keys stored in */
+    public void setPrivKeyFile(String file) { 
+        _privKeyFile = (file != null ? file.trim() : null);
+    }
+    public String getPrivKeyFile() {
+        return _privKeyFile;
+    }
+    /**
+     * If called with any value, we want this tunnel to start whenever it is
+     * loaded (aka right now and whenever the router is started up)
+     */
+    public void setStartOnLoad(boolean val) {
+        _startOnLoad = val;
+    }
+    public void setShared(boolean val) {
+        _sharedClient = val;
+    }
+    public void setConnectDelay(boolean val) {
+        _connectDelay = val;
+    }
+    public void setProfile(String profile) { 
+        _profile = profile; 
+    }
+
+    public void setReduce() {
+        _booleanOptions.add("i2cp.reduceOnIdle");
+    }
+    public void setClose() {
+        _booleanOptions.add("i2cp.closeOnIdle");
+    }
+    public void setEncrypt() {
+        _booleanOptions.add("i2cp.encryptLeaseSet");
+    }
+    public void setDCC() {
+        _booleanOptions.add(I2PTunnelIRCClient.PROP_DCC);
+    }
+    public void setUseSSL() {
+        _booleanOptions.add(I2PTunnelServer.PROP_USE_SSL);
+    }
+    public void setRejectInproxy() {
+        _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_INPROXY);
+    }
+    public void setUniqueLocal() {
+        _booleanOptions.add(I2PTunnelServer.PROP_UNIQUE_LOCAL);
+    }
+
+    protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList";
+    protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList";
+
+    public void setAccessMode(String val) {
+        if ("1".equals(val))
+            _booleanOptions.add(PROP_ENABLE_ACCESS_LIST);
+        else if ("2".equals(val))
+            _booleanOptions.add(PROP_ENABLE_BLACKLIST);
+    }
+
+    public void setDelayOpen() {
+        _booleanOptions.add("i2cp.delayOpen");
+    }
+    public void setNewDest(String val) {
+        if ("1".equals(val))
+            _booleanOptions.add("i2cp.newDestOnResume");
+        else if ("2".equals(val))
+            _booleanOptions.add("persistentClientKey");
+    }
+
+    public void setReduceTime(int val) {
+        _otherOptions.put("i2cp.reduceIdleTime", Integer.toString(val * 60*1000));
+    }
+    public void setReduceCount(int val) {
+        _otherOptions.put("i2cp.reduceQuantity", Integer.toString(val));
+    }
+    public void setEncryptKey(String val) {
+        if (val != null)
+            _otherOptions.put("i2cp.leaseSetKey", val.trim());
+    }
+
+    public void setAccessList(String val) {
+        if (val != null)
+            _otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
+    }
+
+    public void setJumpList(String val) {
+        if (val != null)
+            _otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS, val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
+    }
+
+    public void setCloseTime(int val) {
+        _otherOptions.put("i2cp.closeIdleTime", Integer.toString(val * 60*1000));
+    }
+
+    public void setAllowUserAgent() {
+        _booleanOptions.add(I2PTunnelHTTPClient.PROP_USER_AGENT);
+    }
+    public void setAllowReferer() {
+        _booleanOptions.add(I2PTunnelHTTPClient.PROP_REFERER);
+    }
+    public void setAllowAccept() {
+        _booleanOptions.add(I2PTunnelHTTPClient.PROP_ACCEPT);
+    }
+    public void setAllowInternalSSL() {
+        _booleanOptions.add(I2PTunnelHTTPClient.PROP_INTERNAL_SSL);
+    }
+
+    public void setMultihome() {
+        _booleanOptions.add("shouldBundleReplyInfo");
+    }
+
+    /** all proxy auth */
+    public void setProxyAuth(String s) {
+        if (s != null)
+            _otherOptions.put(I2PTunnelHTTPClientBase.PROP_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH);
+    }
+    
+    public void setProxyUsername(String s) {
+        if (s != null)
+            _newProxyUser = s.trim();
+    }
+    
+    public void setProxyPassword(String s) {
+        if (s != null)
+            _newProxyPW = s.trim();
+    }
+    
+    public void setOutproxyAuth(String s) {
+        _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH);
+    }
+    
+    public void setOutproxyUsername(String s) {
+        if (s != null)
+            _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER, s.trim());
+    }
+    
+    public void setOutproxyPassword(String s) {
+        if (s != null)
+            _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, s.trim());
+    }
+
+    public void setSslProxies(String s) {
+        if (s != null)
+            _otherOptions.put(I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES, s.trim().replace(" ", ","));
+    }
+
+    public void setUseOutproxyPlugin() {
+        _booleanOptions.add(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN);
+    }
+    
+    /**
+     * all of these are @since 0.8.3 (moved from IndexBean)
+     */
+    public static final String PROP_MAX_CONNS_MIN = "i2p.streaming.maxConnsPerMinute";
+    public static final String PROP_MAX_CONNS_HOUR = "i2p.streaming.maxConnsPerHour";
+    public static final String PROP_MAX_CONNS_DAY = "i2p.streaming.maxConnsPerDay";
+    public static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute";
+    public static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour";
+    public static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay";
+    public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
+
+    public void setLimitMinute(int val) {
+        _otherOptions.put(PROP_MAX_CONNS_MIN, Integer.toString(val));
+    }
+
+    public void setLimitHour(int val) {
+        _otherOptions.put(PROP_MAX_CONNS_HOUR, Integer.toString(val));
+    }
+
+    public void setLimitDay(int val) {
+        _otherOptions.put(PROP_MAX_CONNS_DAY, Integer.toString(val));
+    }
+
+    public void setTotalMinute(int val) {
+        _otherOptions.put(PROP_MAX_TOTAL_CONNS_MIN, Integer.toString(val));
+    }
+
+    public void setTotalHour(int val) {
+        _otherOptions.put(PROP_MAX_TOTAL_CONNS_HOUR, Integer.toString(val));
+    }
+
+    public void setTotalDay(int val) {
+        _otherOptions.put(PROP_MAX_TOTAL_CONNS_DAY, Integer.toString(val));
+    }
+
+    public void setMaxStreams(int val) {
+        _otherOptions.put(PROP_MAX_STREAMS, Integer.toString(val));
+    }
+
+    /**
+     * POST limits
+     */
+    public void setPostMax(int val) {
+        _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_MAX, Integer.toString(val));
+    }
+
+    public void setPostTotalMax(int val) {
+        _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, Integer.toString(val));
+    }
+
+    public void setPostCheckTime(int val) {
+        _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_WINDOW, Integer.toString(val * 60));
+    }
+
+    public void setPostBanTime(int val) {
+        _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_BAN_TIME, Integer.toString(val * 60));
+    }
+
+    public void setPostTotalBanTime(int val) {
+        _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, Integer.toString(val * 60));
+    }
+
+    public void setSigType(String val) {
+        if (val != null)
+            _otherOptions.put(I2PClient.PROP_SIGTYPE, val);
+    }
+
+    /**
+     * Random keys
+     */
+    public void setInboundRandomKey(String s) {
+        if (s != null)
+            _otherOptions.put("inbound.randomKey", s.trim());
+    }
+
+    public void setOutboundRandomKey(String s) {
+        if (s != null)
+            _otherOptions.put("outbound.randomKey", s.trim());
+    }
+
+    public void setLeaseSetSigningPrivateKey(String s) {
+        if (s != null)
+            _otherOptions.put("i2cp.leaseSetSigningPrivateKey", s.trim());
+    }
+
+    public void setLeaseSetPrivateKey(String s) {
+        if (s != null)
+            _otherOptions.put("i2cp.leaseSetPrivateKey", s.trim());
+    }
+
+    /**
+     * This is easier than requiring TunnelConfig to talk to
+     * TunnelControllerGroup and TunnelController.
+     *
+     * @param dest the current Destination for this tunnel.
+     */
+    public void setDestination(Destination dest) {
+        _dest = dest;
+    }
+
+    /**
+     * Based on all provided data, create a set of configuration parameters 
+     * suitable for use in a TunnelController.  This will replace (not add to)
+     * any existing parameters, so this should return a comprehensive mapping.
+     *
+     */
+    public Properties getConfig() {
+        Properties config = new Properties();
+        updateConfigGeneric(config);
+        
+        if ((TunnelController.isClient(_type) && !TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) ||
+            TunnelController.TYPE_STREAMR_SERVER.equals(_type)) {
+            // streamrserver uses interface
+            if (_reachableBy != null)
+                config.setProperty(TunnelController.PROP_INTFC, _reachableBy);
+            else
+                config.setProperty(TunnelController.PROP_INTFC, "");
+        } else {
+            // streamrclient uses targetHost
+            if (_targetHost != null)
+                config.setProperty(TunnelController.PROP_TARGET_HOST, _targetHost);
+        }
+
+        if (TunnelController.isClient(_type)) {
+            // generic client stuff
+            if (_port >= 0)
+                config.setProperty(TunnelController.PROP_LISTEN_PORT, Integer.toString(_port));
+            config.setProperty(TunnelController.PROP_SHARED, _sharedClient + "");
+            for (String p : _booleanClientOpts)
+                config.setProperty(OPT + p, "" + _booleanOptions.contains(p));
+            for (String p : _otherClientOpts) {
+                if (_otherOptions.containsKey(p))
+                    config.setProperty(OPT + p, _otherOptions.get(p));
+            }
+        } else {
+            // generic server stuff
+            if (_targetPort >= 0)
+                config.setProperty(TunnelController.PROP_TARGET_PORT, Integer.toString(_targetPort));
+            for (String p : _booleanServerOpts)
+                config.setProperty(OPT + p, "" + _booleanOptions.contains(p));
+            for (String p : _otherServerOpts) {
+                if (_otherOptions.containsKey(p))
+                    config.setProperty(OPT + p, _otherOptions.get(p));
+            }
+        }
+
+        // generic proxy stuff
+        if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type) || 
+            TunnelController.TYPE_SOCKS.equals(_type) ||TunnelController.TYPE_SOCKS_IRC.equals(_type)) {
+            for (String p : _booleanProxyOpts)
+                config.setProperty(OPT + p, "" + _booleanOptions.contains(p));
+            if (_proxyList != null)
+                config.setProperty(TunnelController.PROP_PROXIES, _proxyList);
+        }
+
+        // Proxy auth including migration to MD5
+        if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type)) {
+            // Migrate even if auth is disabled
+            // go get the old from custom options that updateConfigGeneric() put in there
+            String puser = OPT + I2PTunnelHTTPClientBase.PROP_USER;
+            String user = config.getProperty(puser);
+            String ppw = OPT + I2PTunnelHTTPClientBase.PROP_PW;
+            String pw = config.getProperty(ppw);
+            if (user != null && pw != null && user.length() > 0 && pw.length() > 0) {
+                String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX +
+                              user + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX;
+                if (config.getProperty(pmd5) == null) {
+                    // not in there, migrate
+                    String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
+                                                              : I2PTunnelConnectClient.AUTH_REALM;
+                    String hex = PasswordManager.md5Hex(realm, user, pw);
+                    if (hex != null) {
+                        config.setProperty(pmd5, hex);
+                        config.remove(puser);
+                        config.remove(ppw);
+                    }
+                }
+            }
+            // New user/password
+            String auth = _otherOptions.get(I2PTunnelHTTPClientBase.PROP_AUTH);
+            if (auth != null && !auth.equals("false")) {
+                if (_newProxyUser != null && _newProxyPW != null &&
+                    _newProxyUser.length() > 0 && _newProxyPW.length() > 0) {
+                    String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX +
+                                  _newProxyUser + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX;
+                    String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
+                                                              : I2PTunnelConnectClient.AUTH_REALM;
+                    String hex = PasswordManager.md5Hex(realm, _newProxyUser, _newProxyPW);
+                    if (hex != null)
+                        config.setProperty(pmd5, hex);
+                }
+            }
+        }
+
+        if (TunnelController.TYPE_IRC_CLIENT.equals(_type) ||
+            TunnelController.TYPE_STD_CLIENT.equals(_type) ||
+            TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) {
+            if (_targetDestination != null)
+                config.setProperty(TunnelController.PROP_DEST, _targetDestination);
+        } else if (TunnelController.TYPE_HTTP_SERVER.equals(_type) ||
+                   TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) {
+            if (_spoofedHost != null)
+                config.setProperty(TunnelController.PROP_SPOOFED_HOST, _spoofedHost);
+            for (String p : _httpServerOpts)
+                if (_otherOptions.containsKey(p))
+                    config.setProperty(OPT + p, _otherOptions.get(p));
+        }
+        if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) {
+            if (_port >= 0)
+                config.setProperty(TunnelController.PROP_LISTEN_PORT, Integer.toString(_port));
+            if (_reachableBy != null)
+                config.setProperty(TunnelController.PROP_INTFC, _reachableBy);
+            else if (_targetHost != null)
+                config.setProperty(TunnelController.PROP_INTFC, _targetHost);
+            else
+                config.setProperty(TunnelController.PROP_INTFC, "");
+        }
+
+        if (TunnelController.TYPE_IRC_CLIENT.equals(_type)) {
+            boolean dcc = _booleanOptions.contains(I2PTunnelIRCClient.PROP_DCC);
+            config.setProperty(OPT + I2PTunnelIRCClient.PROP_DCC,
+                               "" + dcc);
+            // add some sane server options since they aren't in the GUI (yet)
+            if (dcc) {
+                config.setProperty(OPT + PROP_MAX_CONNS_MIN, "3");
+                config.setProperty(OPT + PROP_MAX_CONNS_HOUR, "10");
+                config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_MIN, "5");
+                config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_HOUR, "25");
+            }
+        }
+
+        if (!TunnelController.isClient(_type) || _booleanOptions.contains("persistentClientKey")) {
+            // As of 0.9.17, add a persistent random key if not present
+            String p = OPT + "inbound.randomKey";
+            if (!config.containsKey(p)) {
+                byte[] rk = new byte[32];
+                _context.random().nextBytes(rk);
+                config.setProperty(p, Base64.encode(rk));
+                p = OPT + "outbound.randomKey";
+                _context.random().nextBytes(rk);
+                config.setProperty(p, Base64.encode(rk));
+            }
+            // As of 0.9.18, add persistent leaseset keys if not present
+            // but only if we know the sigtype
+            p = OPT + "i2cp.leaseSetSigningPrivateKey";
+            if (_dest != null && !config.containsKey(p)) {
+                try {
+                    SigType type = _dest.getSigType();
+                    SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
+                    config.setProperty(p, type.name() + ':' + keys[1].toBase64());
+                    p = OPT + "i2cp.leaseSetPrivateKey";
+                    keys = KeyGenerator.getInstance().generatePKIKeys();
+                    config.setProperty(p, "ELGAMAL_2048:" + keys[1].toBase64());
+                } catch (GeneralSecurityException gse) {
+                    // so much for that
+                }
+            }
+        }
+
+        return config;
+    }
+    
+    private static final String _noShowOpts[] = {
+        "inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance",
+        "inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity",
+        "inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize",
+        I2PTunnelIRCClient.PROP_DCC
+        };
+    private static final String _booleanClientOpts[] = {
+        "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen",
+        I2PTunnelClientBase.PROP_USE_SSL,
+        };
+    private static final String _booleanProxyOpts[] = {
+        I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH,
+        I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN,
+        I2PTunnelHTTPClient.PROP_USER_AGENT,
+        I2PTunnelHTTPClient.PROP_REFERER,
+        I2PTunnelHTTPClient.PROP_ACCEPT,
+        I2PTunnelHTTPClient.PROP_INTERNAL_SSL
+        };
+    private static final String _booleanServerOpts[] = {
+        "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST,
+        I2PTunnelServer.PROP_USE_SSL,
+        I2PTunnelHTTPServer.OPT_REJECT_INPROXY,
+        I2PTunnelServer.PROP_UNIQUE_LOCAL,
+        "shouldBundleReplyInfo"
+        };
+    private static final String _otherClientOpts[] = {
+        "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
+        "outproxyUsername", "outproxyPassword",
+        I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
+        I2PTunnelHTTPClientBase.PROP_AUTH,
+        I2PClient.PROP_SIGTYPE,
+        I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES,
+        // following are mostly server but could also be persistent client
+        "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
+        };
+    private static final String _otherServerOpts[] = {
+        "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList",
+         PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY,
+         PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
+         PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE,
+        "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
+        };
+    private static final String _httpServerOpts[] = {
+        I2PTunnelHTTPServer.OPT_POST_WINDOW,
+        I2PTunnelHTTPServer.OPT_POST_BAN_TIME,
+        I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME,
+        I2PTunnelHTTPServer.OPT_POST_MAX,
+        I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX
+        };
+
+    /**
+     *  do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient
+     *  so they will get migrated to MD5
+     *  TODO migrate socks to MD5
+     */
+    private static final String _otherProxyOpts[] = {
+        "proxyUsername", "proxyPassword"
+        };
+
+    public static final Set<String> _noShowSet = new HashSet<String>(128);
+    public static final Set<String> _nonProxyNoShowSet = new HashSet<String>(4);
+    static {
+        _noShowSet.addAll(Arrays.asList(_noShowOpts));
+        _noShowSet.addAll(Arrays.asList(_booleanClientOpts));
+        _noShowSet.addAll(Arrays.asList(_booleanProxyOpts));
+        _noShowSet.addAll(Arrays.asList(_booleanServerOpts));
+        _noShowSet.addAll(Arrays.asList(_otherClientOpts));
+        _noShowSet.addAll(Arrays.asList(_otherServerOpts));
+        _noShowSet.addAll(Arrays.asList(_httpServerOpts));
+        _nonProxyNoShowSet.addAll(Arrays.asList(_otherProxyOpts));
+    }
+
+    private void updateConfigGeneric(Properties config) {
+        config.setProperty(TunnelController.PROP_TYPE, _type);
+        if (_name != null)
+            config.setProperty(TunnelController.PROP_NAME, _name);
+        if (_description != null)
+            config.setProperty(TunnelController.PROP_DESCR, _description);
+        if (!_context.isRouterContext()) {
+            if (_i2cpHost != null)
+                config.setProperty(TunnelController.PROP_I2CP_HOST, _i2cpHost);
+            if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) {
+                config.setProperty(TunnelController.PROP_I2CP_PORT, _i2cpPort);
+            } else {
+                config.setProperty(TunnelController.PROP_I2CP_PORT, "7654");
+            }
+        }
+        if (_privKeyFile != null)
+            config.setProperty(TunnelController.PROP_FILE, _privKeyFile);
+        
+        if (_customOptions != null) {
+            StringTokenizer tok = new StringTokenizer(_customOptions);
+            while (tok.hasMoreTokens()) {
+                String pair = tok.nextToken();
+                int eq = pair.indexOf('=');
+                if ( (eq <= 0) || (eq >= pair.length()) )
+                    continue;
+                String key = pair.substring(0, eq);
+                if (_noShowSet.contains(key))
+                    continue;
+                // leave in for HTTP and Connect so it can get migrated to MD5
+                // hide for SOCKS until migrated to MD5
+                if ((!TunnelController.TYPE_HTTP_CLIENT.equals(_type)) &&
+                    (!TunnelController.TYPE_CONNECT.equals(_type)) &&
+                    _nonProxyNoShowSet.contains(key))
+                    continue;
+                String val = pair.substring(eq+1);
+                config.setProperty(OPT + key, val);
+            }
+        }
+
+        config.setProperty(TunnelController.PROP_START, _startOnLoad + "");
+
+        updateTunnelQuantities(config);
+        if (_connectDelay)
+            config.setProperty("option.i2p.streaming.connectDelay", "1000");
+        else
+            config.setProperty("option.i2p.streaming.connectDelay", "0");
+        if (TunnelController.isClient(_type) && _sharedClient) {
+            config.setProperty("option.inbound.nickname", SHARED_CLIENT_NICKNAME);
+            config.setProperty("option.outbound.nickname", SHARED_CLIENT_NICKNAME);
+        } else if (_name != null) {
+            config.setProperty("option.inbound.nickname", _name);
+            config.setProperty("option.outbound.nickname", _name);
+        }
+        if ("interactive".equals(_profile))
+            // This was 1 which doesn't make much sense
+            // The real way to make it interactive is to make the streaming lib
+            // MessageInputStream flush faster but there's no option for that yet,
+            // Setting it to 16 instead of the default but not sure what good that is either.
+            config.setProperty("option.i2p.streaming.maxWindowSize", "16");
+        else
+            config.remove("option.i2p.streaming.maxWindowSize");
+    }
+
+    /**
+     * Update tunnel quantities for the provided config from this TunnelConfig.
+     *
+     * @param config the config to update.
+     */
+    public void updateTunnelQuantities(Properties config) {
+        if (_tunnelQuantity >= 0) {
+            config.setProperty("option.inbound.quantity", Integer.toString(_tunnelQuantity));
+            config.setProperty("option.outbound.quantity", Integer.toString(_tunnelQuantity));
+        }
+        if (_tunnelDepth >= 0) {
+            config.setProperty("option.inbound.length", Integer.toString(_tunnelDepth));
+            config.setProperty("option.outbound.length", Integer.toString(_tunnelDepth));
+        }
+        if (_tunnelVariance >= 0) {
+            config.setProperty("option.inbound.lengthVariance", Integer.toString(_tunnelVariance));
+            config.setProperty("option.outbound.lengthVariance", Integer.toString(_tunnelVariance));
+        }
+        if (_tunnelBackupQuantity >= 0) {
+            config.setProperty("option.inbound.backupQuantity", Integer.toString(_tunnelBackupQuantity));
+            config.setProperty("option.outbound.backupQuantity", Integer.toString(_tunnelBackupQuantity));
+        }
+    }
+}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
index cd4c406c2579da164e749e489e0cbd539d0e45ad..d75b418f4a8c1470fb1501028abff98c7e6a1038 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
@@ -27,6 +27,7 @@ import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
 import net.i2p.i2ptunnel.I2PTunnelHTTPServer;
 import net.i2p.i2ptunnel.I2PTunnelIRCClient;
 import net.i2p.i2ptunnel.I2PTunnelServer;
+import net.i2p.i2ptunnel.TunnelConfig;
 import net.i2p.i2ptunnel.TunnelController;
 import net.i2p.i2ptunnel.TunnelControllerGroup;
 import net.i2p.util.Addresses;
@@ -359,31 +360,31 @@ public class EditBean extends IndexBean {
 
     /** all of these are @since 0.8.3 */
     public int getLimitMinute(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_CONNS_MIN, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_CONNS_MIN, 0);
     }
 
     public int getLimitHour(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_CONNS_HOUR, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_CONNS_HOUR, 0);
     }
 
     public int getLimitDay(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_CONNS_DAY, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_CONNS_DAY, 0);
     }
 
     public int getTotalMinute(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_MIN, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_TOTAL_CONNS_MIN, 0);
     }
 
     public int getTotalHour(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_HOUR, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_TOTAL_CONNS_HOUR, 0);
     }
 
     public int getTotalDay(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_TOTAL_CONNS_DAY, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_TOTAL_CONNS_DAY, 0);
     }
 
     public int getMaxStreams(int tunnel) {
-        return getProperty(tunnel, PROP_MAX_STREAMS, 0);
+        return getProperty(tunnel, TunnelConfig.PROP_MAX_STREAMS, 0);
     }
 
     /**
@@ -499,12 +500,12 @@ public class EditBean extends IndexBean {
             Map<String, String> sorted = new TreeMap<String, String>();
             for (Map.Entry<Object, Object> e : opts.entrySet()) {
                 String key = (String)e.getKey();
-                if (_noShowSet.contains(key))
+                if (TunnelConfig._noShowSet.contains(key))
                     continue;
                 // leave in for HTTP and Connect so it can get migrated to MD5
                 // hide for SOCKS until migrated to MD5
                 if ((!isMD5Proxy) &&
-                    _nonProxyNoShowSet.contains(key))
+                    TunnelConfig._nonProxyNoShowSet.contains(key))
                     continue;
                 sorted.put(key, (String)e.getValue());
             }
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
index 3b6e7cdb9537f2ed90facdd375141fa025eed771..f16d64b5963b01f2dcfdcc21e8275beffa1b7822 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java
@@ -10,46 +10,30 @@ package net.i2p.i2ptunnel.web;
 
 import java.io.File;
 import java.io.IOException;
-import java.security.GeneralSecurityException;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.concurrent.ConcurrentHashMap;
 
 import net.i2p.I2PAppContext;
 import net.i2p.app.ClientAppManager;
 import net.i2p.app.Outproxy;
-import net.i2p.client.I2PClient;
-import net.i2p.crypto.KeyGenerator;
-import net.i2p.crypto.SigType;
-import net.i2p.data.Base64;
 import net.i2p.data.Certificate;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Destination;
 import net.i2p.data.PrivateKeyFile;
 import net.i2p.data.SessionKey;
-import net.i2p.data.SimpleDataStructure;
 import net.i2p.i2ptunnel.I2PTunnelClientBase;
-import net.i2p.i2ptunnel.I2PTunnelConnectClient;
 import net.i2p.i2ptunnel.I2PTunnelHTTPClient;
-import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase;
 import net.i2p.i2ptunnel.I2PTunnelHTTPServer;
-import net.i2p.i2ptunnel.I2PTunnelIRCClient;
 import net.i2p.i2ptunnel.I2PTunnelServer;
 import net.i2p.i2ptunnel.SSLClientUtil;
+import net.i2p.i2ptunnel.TunnelConfig;
 import net.i2p.i2ptunnel.TunnelController;
 import net.i2p.i2ptunnel.TunnelControllerGroup;
 import net.i2p.util.Addresses;
-import net.i2p.util.ConcurrentHashSet;
 import net.i2p.util.FileUtil;
 import net.i2p.util.Log;
-import net.i2p.util.PasswordManager;
 import net.i2p.util.SecureFile;
 
 /**
@@ -71,36 +55,11 @@ public class IndexBean {
     private String _curNonce;
     //private long _nextNonce;
 
-    private String _type;
-    private String _name;
-    private String _description;
-    private String _i2cpHost;
-    private String _i2cpPort;
-    private String _tunnelDepth;
-    private String _tunnelQuantity;
-    private String _tunnelVariance;
-    private String _tunnelBackupQuantity;
-    private boolean _connectDelay;
-    private String _customOptions;
-    private String _proxyList;
-    private String _port;
-    private String _reachableBy;
-    private String _targetDestination;
-    private String _targetHost;
-    private String _targetPort;
-    private String _spoofedHost;
-    private String _privKeyFile;
-    private String _profile;
-    private boolean _startOnLoad;
-    private boolean _sharedClient;
+    private TunnelConfig _config;
     private boolean _removeConfirmed;
-    private final Set<String> _booleanOptions;
-    private final Map<String, String> _otherOptions;
     private int _hashCashValue;
     private int _certType;
     private String _certSigner;
-    private String _newProxyUser;
-    private String _newProxyPW;
     
     public static final int RUNNING = 1;
     public static final int STARTING = 2;
@@ -114,7 +73,6 @@ public class IndexBean {
     /** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */
     private static final List<String> _nonces = new ArrayList<String>(MAX_NONCES + 1);
 
-    static final String CLIENT_NICKNAME = "shared clients";
     public static final String PROP_THEME_NAME = "routerconsole.theme";
     public static final String DEFAULT_THEME = "light";
     public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled";
@@ -140,8 +98,7 @@ public class IndexBean {
         _tunnel = -1;
         _curNonce = "-1";
         addNonce();
-        _booleanOptions = new ConcurrentHashSet<String>(4);
-        _otherOptions = new ConcurrentHashMap<String, String>(4);
+        _config = new TunnelConfig();
     }
     
     /**
@@ -332,24 +289,9 @@ public class IndexBean {
                 // if it belongs to a shared destination, and is of supported type
                 if (Boolean.parseBoolean(c.getSharedClient()) && isClient(c.getType())) {
                     Properties cOpt = c.getConfig("");
-                    if (_tunnelQuantity != null) {
-                        cOpt.setProperty("option.inbound.quantity", _tunnelQuantity);
-                        cOpt.setProperty("option.outbound.quantity", _tunnelQuantity);
-                    }
-                    if (_tunnelDepth != null) {
-                        cOpt.setProperty("option.inbound.length", _tunnelDepth);
-                        cOpt.setProperty("option.outbound.length", _tunnelDepth);
-                    }
-                    if (_tunnelVariance != null) {
-                        cOpt.setProperty("option.inbound.lengthVariance", _tunnelVariance);
-                        cOpt.setProperty("option.outbound.lengthVariance", _tunnelVariance);
-                    }
-                    if (_tunnelBackupQuantity != null) {
-                        cOpt.setProperty("option.inbound.backupQuantity", _tunnelBackupQuantity);
-                        cOpt.setProperty("option.outbound.backupQuantity", _tunnelBackupQuantity);
-                    }
-                    cOpt.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
-                    cOpt.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
+                    _config.updateTunnelQuantities(config);
+                    cOpt.setProperty("option.inbound.nickname", TunnelConfig.SHARED_CLIENT_NICKNAME);
+                    cOpt.setProperty("option.outbound.nickname", TunnelConfig.SHARED_CLIENT_NICKNAME);
                     
                     c.setConfig(cOpt, "");
                 }
@@ -396,7 +338,7 @@ public class IndexBean {
         // Use configured file name if available, not the one from the form.
         String pk = cur.getPrivKeyFile();
         if (pk == null)
-            pk = _privKeyFile;
+            pk = _config.getPrivKeyFile();
         if (pk != null && pk.startsWith("i2ptunnel") && pk.endsWith("-privKeys.dat") &&
             ((!isClient(cur.getType())) || cur.getPersistentClientKey())) {
             File pkf = new File(_context.getConfigDir(), pk);
@@ -743,80 +685,103 @@ public class IndexBean {
      *
      */
     public void setType(String type) { 
-        _type = (type != null ? type.trim() : null);   
+        _config.setType(type);
     }
-    String getType() { return _type; }
+    String getType() { return _config.getType(); }
     
     /** Short name of the tunnel */
     public void setName(String name) { 
-        _name = (name != null ? name.trim() : null);
+        _config.setName(name);
     }
     /** one line description */
     public void setNofilter_description(String description) { 
-        // '#' will blow up DataHelper.storeProps()
-        _description = (description != null ? description.replace('#', ' ').trim() : null);
+        _config.setDescription(description);
     }
     /** I2CP host the router is on, ignored when in router context */
     public void setClientHost(String host) {
-        _i2cpHost = (host != null ? host.trim() : null);
+        _config.setClientHost(host);
     }
     /** I2CP port the router is on, ignored when in router context */
     public void setClientport(String port) {
-        _i2cpPort = (port != null ? port.trim() : null);
+        _config.setClientPort(port);
     }
     /** how many hops to use for inbound tunnels */
-    public void setTunnelDepth(String tunnelDepth) { 
-        _tunnelDepth = (tunnelDepth != null ? tunnelDepth.trim() : null);
+    public void setTunnelDepth(String tunnelDepth) {
+        if (tunnelDepth != null) {
+            try {
+                _config.setTunnelDepth(Integer.parseInt(tunnelDepth.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     /** how many parallel inbound tunnels to use */
-    public void setTunnelQuantity(String tunnelQuantity) { 
-        _tunnelQuantity = (tunnelQuantity != null ? tunnelQuantity.trim() : null);
+    public void setTunnelQuantity(String tunnelQuantity) {
+        if (tunnelQuantity != null) {
+            try {
+                _config.setTunnelQuantity(Integer.parseInt(tunnelQuantity.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     /** how much randomisation to apply to the depth of tunnels */
-    public void setTunnelVariance(String tunnelVariance) { 
-        _tunnelVariance = (tunnelVariance != null ? tunnelVariance.trim() : null);
+    public void setTunnelVariance(String tunnelVariance) {
+        if (tunnelVariance != null) {
+            try {
+                _config.setTunnelVariance(Integer.parseInt(tunnelVariance.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     /** how many tunnels to hold in reserve to guard against failures */
-    public void setTunnelBackupQuantity(String tunnelBackupQuantity) { 
-        _tunnelBackupQuantity = (tunnelBackupQuantity != null ? tunnelBackupQuantity.trim() : null);
+    public void setTunnelBackupQuantity(String tunnelBackupQuantity) {
+        if (tunnelBackupQuantity != null) {
+            try {
+                _config.setTunnelBackupQuantity(Integer.parseInt(tunnelBackupQuantity.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     /** what I2P session overrides should be used */
     public void setNofilter_customOptions(String customOptions) { 
-        _customOptions = (customOptions != null ? customOptions.trim() : null);
+        _config.setCustomOptions(customOptions);
     }
     /** what HTTP outproxies should be used (httpclient specific) */
     public void setProxyList(String proxyList) { 
-        _proxyList = (proxyList != null ? proxyList.trim() : null);
+        _config.setProxyList(proxyList);
     }
     /** what port should this client/httpclient/ircclient listen on */
     public void setPort(String port) { 
-        _port = (port != null ? port.trim() : null);
+        if (port != null) {
+            try {
+                _config.setPort(Integer.parseInt(port.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     /** 
      * what interface should this client/httpclient/ircclient listen on
      */
     public void setReachableBy(String reachableBy) { 
-        _reachableBy = (reachableBy != null ? reachableBy.trim() : null);
+        _config.setReachableBy(reachableBy);
     }
     /** What peer does this client tunnel point at */
     public void setTargetDestination(String dest) { 
-        _targetDestination = (dest != null ? dest.trim() : null);
+        _config.setTargetDestination(dest);
     }
     /** What host does this server tunnel point at */
     public void setTargetHost(String host) { 
-        _targetHost = (host != null ? host.trim() : null);
+        _config.setTargetHost(host);
     }
     /** What port does this server tunnel point at */
-    public void setTargetPort(String port) { 
-        _targetPort = (port != null ? port.trim() : null);
+    public void setTargetPort(String port) {
+        if (port != null) {
+            try {
+                _config.setTargetPort(Integer.parseInt(port.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     /** What host does this http server tunnel spoof */
     public void setSpoofedHost(String host) { 
-        _spoofedHost = (host != null ? host.trim() : null);
+        _config.setSpoofedHost(host);
     }
     /** What filename is this server tunnel's private keys stored in */
     public void setPrivKeyFile(String file) { 
-        _privKeyFile = (file != null ? file.trim() : null);
+        _config.setPrivKeyFile(file);
     }
     /**
      * If called with any value (and the form submitted with action=Remove),
@@ -830,39 +795,39 @@ public class IndexBean {
      * loaded (aka right now and whenever the router is started up)
      */
     public void setStartOnLoad(String moo) {
-        _startOnLoad = true;
+        _config.setStartOnLoad(true);
     }
     public void setShared(String moo) {
-    	_sharedClient=true;
+    	_config.setShared(true);
     }
     public void setShared(boolean val) {
-    	_sharedClient=val;
+    	_config.setShared(val);
     }
     public void setConnectDelay(String moo) {
-        _connectDelay = true;
+        _config.setConnectDelay(true);
     }
     public void setProfile(String profile) { 
-        _profile = profile; 
+        _config.setProfile(profile);
     }
 
     public void setReduce(String moo) {
-        _booleanOptions.add("i2cp.reduceOnIdle");
+        _config.setReduce();
     }
     public void setClose(String moo) {
-        _booleanOptions.add("i2cp.closeOnIdle");
+        _config.setClose();
     }
     public void setEncrypt(String moo) {
-        _booleanOptions.add("i2cp.encryptLeaseSet");
+        _config.setEncrypt();
     }
 
     /** @since 0.8.9 */
     public void setDCC(String moo) {
-        _booleanOptions.add(I2PTunnelIRCClient.PROP_DCC);
+        _config.setDCC();
     }
 
     /** @since 0.9.9 */
     public void setUseSSL(String moo) {
-        _booleanOptions.add(I2PTunnelServer.PROP_USE_SSL);
+        _config.setUseSSL();
     }
 
     /** @since 0.9.9 */
@@ -877,7 +842,7 @@ public class IndexBean {
 
     /** @since 0.9.12 */
     public void setRejectInproxy(String moo) {
-        _booleanOptions.add(I2PTunnelHTTPServer.OPT_REJECT_INPROXY);
+        _config.setRejectInproxy();
     }
 
     /** @since 0.9.12 */
@@ -892,171 +857,171 @@ public class IndexBean {
 
     /** @since 0.9.13 */
     public void setUniqueLocal(String moo) {
-        _booleanOptions.add(I2PTunnelServer.PROP_UNIQUE_LOCAL);
+        _config.setUniqueLocal();
     }
 
     protected static final String PROP_ENABLE_ACCESS_LIST = "i2cp.enableAccessList";
     protected static final String PROP_ENABLE_BLACKLIST = "i2cp.enableBlackList";
 
     public void setAccessMode(String val) {
-        if ("1".equals(val))
-            _booleanOptions.add(PROP_ENABLE_ACCESS_LIST);
-        else if ("2".equals(val))
-            _booleanOptions.add(PROP_ENABLE_BLACKLIST);
+        _config.setAccessMode(val);
     }
 
     public void setDelayOpen(String moo) {
-        _booleanOptions.add("i2cp.delayOpen");
+        _config.setDelayOpen();
     }
     public void setNewDest(String val) {
-        if ("1".equals(val))
-            _booleanOptions.add("i2cp.newDestOnResume");
-        else if ("2".equals(val))
-            _booleanOptions.add("persistentClientKey");
+        _config.setNewDest(val);
     }
 
     public void setReduceTime(String val) {
         if (val != null) {
             try {
-                _otherOptions.put("i2cp.reduceIdleTime", Integer.toString(Integer.parseInt(val.trim()) * 60*1000));
+                _config.setReduceTime(Integer.parseInt(val.trim()));
             } catch (NumberFormatException nfe) {}
         }
     }
     public void setReduceCount(String val) {
-        if (val != null)
-            _otherOptions.put("i2cp.reduceQuantity", val.trim());
+        if (val != null) {
+            try {
+                _config.setReduceCount(Integer.parseInt(val.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
     public void setEncryptKey(String val) {
-        if (val != null)
-            _otherOptions.put("i2cp.leaseSetKey", val.trim());
+        _config.setEncryptKey(val);
     }
 
     public void setAccessList(String val) {
-        if (val != null)
-            _otherOptions.put("i2cp.accessList", val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
+        _config.setAccessList(val);
     }
 
     public void setJumpList(String val) {
-        if (val != null)
-            _otherOptions.put(I2PTunnelHTTPClient.PROP_JUMP_SERVERS, val.trim().replace("\r\n", ",").replace("\n", ",").replace(" ", ","));
+        _config.setJumpList(val);
     }
 
     public void setCloseTime(String val) {
         if (val != null) {
             try {
-                _otherOptions.put("i2cp.closeIdleTime", Integer.toString(Integer.parseInt(val.trim()) * 60*1000));
+                _config.setCloseTime(Integer.parseInt(val.trim()));
             } catch (NumberFormatException nfe) {}
         }
     }
 
     /** @since 0.9.14 */
     public void setAllowUserAgent(String moo) {
-        _booleanOptions.add(I2PTunnelHTTPClient.PROP_USER_AGENT);
+        _config.setAllowUserAgent();
     }
 
     /** @since 0.9.14 */
     public void setAllowReferer(String moo) {
-        _booleanOptions.add(I2PTunnelHTTPClient.PROP_REFERER);
+        _config.setAllowReferer();
     }
 
     /** @since 0.9.14 */
     public void setAllowAccept(String moo) {
-        _booleanOptions.add(I2PTunnelHTTPClient.PROP_ACCEPT);
+        _config.setAllowAccept();
     }
 
     /** @since 0.9.14 */
     public void setAllowInternalSSL(String moo) {
-        _booleanOptions.add(I2PTunnelHTTPClient.PROP_INTERNAL_SSL);
+        _config.setAllowInternalSSL();
     }
 
     /** @since 0.9.18 */
     public void setMultihome(String moo) {
-        _booleanOptions.add("shouldBundleReplyInfo");
+        _config.setMultihome();
     }
 
     /** all proxy auth @since 0.8.2 */
     public void setProxyAuth(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPClientBase.PROP_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH);
+        _config.setProxyAuth(s);
     }
     
     public void setProxyUsername(String s) {
-        if (s != null)
-            _newProxyUser = s.trim();
+        _config.setProxyUsername(s);
     }
     
     public void setNofilter_proxyPassword(String s) {
-        if (s != null)
-            _newProxyPW = s.trim();
+        _config.setProxyPassword(s);
     }
     
     public void setOutproxyAuth(String s) {
-        _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, I2PTunnelHTTPClientBase.DIGEST_AUTH);
+        _config.setOutproxyAuth(s);
     }
     
     public void setOutproxyUsername(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_USER, s.trim());
+        _config.setOutproxyUsername(s);
     }
     
     public void setNofilter_outproxyPassword(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPClientBase.PROP_OUTPROXY_PW, s.trim());
+        _config.setOutproxyPassword(s);
     }
 
     /** @since 0.9.11 */
     public void setSslProxies(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES, s.trim().replace(" ", ","));
+        _config.setSslProxies(s);
     }
 
     /** @since 0.9.11 */
     public void setUseOutproxyPlugin(String moo) {
-        _booleanOptions.add(I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN);
+        _config.setUseOutproxyPlugin();
     }
-    
-    /** all of these are @since 0.8.3 */
-    protected static final String PROP_MAX_CONNS_MIN = "i2p.streaming.maxConnsPerMinute";
-    protected static final String PROP_MAX_CONNS_HOUR = "i2p.streaming.maxConnsPerHour";
-    protected static final String PROP_MAX_CONNS_DAY = "i2p.streaming.maxConnsPerDay";
-    protected static final String PROP_MAX_TOTAL_CONNS_MIN = "i2p.streaming.maxTotalConnsPerMinute";
-    protected static final String PROP_MAX_TOTAL_CONNS_HOUR = "i2p.streaming.maxTotalConnsPerHour";
-    protected static final String PROP_MAX_TOTAL_CONNS_DAY = "i2p.streaming.maxTotalConnsPerDay";
-    protected static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams";
 
     public void setLimitMinute(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_CONNS_MIN, s.trim());
+        if (s != null) {
+            try {
+                _config.setLimitMinute(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setLimitHour(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_CONNS_HOUR, s.trim());
+        if (s != null) {
+            try {
+                _config.setLimitHour(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setLimitDay(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_CONNS_DAY, s.trim());
+        if (s != null) {
+            try {
+                _config.setLimitDay(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setTotalMinute(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_TOTAL_CONNS_MIN, s.trim());
+        if (s != null) {
+            try {
+                _config.setTotalMinute(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setTotalHour(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_TOTAL_CONNS_HOUR, s.trim());
+        if (s != null) {
+            try {
+                _config.setTotalHour(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setTotalDay(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_TOTAL_CONNS_DAY, s.trim());
+        if (s != null) {
+            try {
+                _config.setTotalDay(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setMaxStreams(String s) {
-        if (s != null)
-            _otherOptions.put(PROP_MAX_STREAMS, s.trim());
+        if (s != null) {
+            try {
+                _config.setMaxStreams(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     /**
@@ -1064,28 +1029,43 @@ public class IndexBean {
      * @since 0.9.9
      */
     public void setPostMax(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_MAX, s.trim());
+        if (s != null) {
+            try {
+                _config.setPostMax(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setPostTotalMax(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX, s.trim());
+        if (s != null) {
+            try {
+                _config.setPostTotalMax(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setPostCheckTime(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_WINDOW, Integer.toString(Integer.parseInt(s.trim()) * 60));
+        if (s != null) {
+            try {
+                _config.setPostCheckTime(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setPostBanTime(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_BAN_TIME, Integer.toString(Integer.parseInt(s.trim()) * 60));
+        if (s != null) {
+            try {
+                _config.setPostBanTime(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     public void setPostTotalBanTime(String s) {
-        if (s != null)
-            _otherOptions.put(I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME, Integer.toString(Integer.parseInt(s.trim()) * 60));
+        if (s != null) {
+            try {
+                _config.setPostTotalBanTime(Integer.parseInt(s.trim()));
+            } catch (NumberFormatException nfe) {}
+        }
     }
 
     /** params needed for hashcash and dest modification */
@@ -1112,7 +1092,7 @@ public class IndexBean {
     /** @since 0.9.12 */
     public void setSigType(String val) {
         if (val != null) {
-            _otherOptions.put(I2PClient.PROP_SIGTYPE, val);
+            _config.setSigType(val);
             if (val.equals("0"))
                 _certType = 0;
             else
@@ -1127,28 +1107,25 @@ public class IndexBean {
      *  @since 0.9.18
      */
     public void setKey1(String s) {
-        if (s != null)
-            _otherOptions.put("inbound.randomKey", s.trim());
+        _config.setInboundRandomKey(s);
     }
 
     public void setKey2(String s) {
-        if (s != null)
-            _otherOptions.put("outbound.randomKey", s.trim());
+        _config.setOutboundRandomKey(s);
     }
 
     public void setKey3(String s) {
-        if (s != null)
-            _otherOptions.put("i2cp.leaseSetSigningPrivateKey", s.trim());
+        _config.setLeaseSetSigningPrivateKey(s);
     }
 
     public void setKey4(String s) {
-        if (s != null)
-            _otherOptions.put("i2cp.leaseSetPrivateKey", s.trim());
+        _config.setLeaseSetPrivateKey(s);
     }
 
     /** Modify or create a destination */
     private String modifyDestination() {
-        if (_privKeyFile == null || _privKeyFile.trim().length() <= 0)
+        String privKeyFile = _config.getPrivKeyFile();
+        if (privKeyFile == null)
             return "Private Key File not specified";
 
         TunnelController tun = getController(_tunnel);
@@ -1162,9 +1139,9 @@ public class IndexBean {
             return "Tunnel must be stopped before modifying destination";
         }
 
-        File keyFile = new File(_privKeyFile);
+        File keyFile = new File(privKeyFile);
         if (!keyFile.isAbsolute())
-            keyFile = new File(_context.getConfigDir(), _privKeyFile);
+            keyFile = new File(_context.getConfigDir(), privKeyFile);
         PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
         try {
             pkf.createIfAbsent();
@@ -1194,7 +1171,7 @@ public class IndexBean {
                 }
                 if (signerPKF == null || signerPKF.length() <= 0)
                     return "Signing destination " + _certSigner + " not found";
-                if (_privKeyFile.equals(signerPKF))
+                if (privKeyFile.equals(signerPKF))
                     return "Self-signed destinations not allowed";
                 Certificate c = pkf.setSignedCert(new PrivateKeyFile(signerPKF));
                 if (c == null)
@@ -1243,310 +1220,10 @@ public class IndexBean {
      *
      */
     private Properties getConfig() {
-        Properties config = new Properties();
-        updateConfigGeneric(config);
-        
-        if ((isClient(_type) && !TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) ||
-            TunnelController.TYPE_STREAMR_SERVER.equals(_type)) {
-            // streamrserver uses interface
-            if (_reachableBy != null)
-                config.setProperty(TunnelController.PROP_INTFC, _reachableBy);
-            else
-                config.setProperty(TunnelController.PROP_INTFC, "");
-        } else {
-            // streamrclient uses targetHost
-            if (_targetHost != null)
-                config.setProperty(TunnelController.PROP_TARGET_HOST, _targetHost);
-        }
-
-        if (isClient(_type)) {
-            // generic client stuff
-            if (_port != null)
-                config.setProperty(TunnelController.PROP_LISTEN_PORT, _port);
-            config.setProperty(TunnelController.PROP_SHARED, _sharedClient + "");
-            for (String p : _booleanClientOpts)
-                config.setProperty(OPT + p, "" + _booleanOptions.contains(p));
-            for (String p : _otherClientOpts) {
-                if (_otherOptions.containsKey(p))
-                    config.setProperty(OPT + p, _otherOptions.get(p));
-            }
-        } else {
-            // generic server stuff
-            if (_targetPort != null)
-                config.setProperty(TunnelController.PROP_TARGET_PORT, _targetPort);
-            for (String p : _booleanServerOpts)
-                config.setProperty(OPT + p, "" + _booleanOptions.contains(p));
-            for (String p : _otherServerOpts) {
-                if (_otherOptions.containsKey(p))
-                    config.setProperty(OPT + p, _otherOptions.get(p));
-            }
-        }
-
-        // generic proxy stuff
-        if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type) || 
-            TunnelController.TYPE_SOCKS.equals(_type) ||TunnelController.TYPE_SOCKS_IRC.equals(_type)) {
-            for (String p : _booleanProxyOpts)
-                config.setProperty(OPT + p, "" + _booleanOptions.contains(p));
-            if (_proxyList != null)
-                config.setProperty(TunnelController.PROP_PROXIES, _proxyList);
-        }
-
-        // Proxy auth including migration to MD5
-        if (TunnelController.TYPE_HTTP_CLIENT.equals(_type) || TunnelController.TYPE_CONNECT.equals(_type)) {
-            // Migrate even if auth is disabled
-            // go get the old from custom options that updateConfigGeneric() put in there
-            String puser = OPT + I2PTunnelHTTPClientBase.PROP_USER;
-            String user = config.getProperty(puser);
-            String ppw = OPT + I2PTunnelHTTPClientBase.PROP_PW;
-            String pw = config.getProperty(ppw);
-            if (user != null && pw != null && user.length() > 0 && pw.length() > 0) {
-                String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX +
-                              user + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX;
-                if (config.getProperty(pmd5) == null) {
-                    // not in there, migrate
-                    String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
-                                                              : I2PTunnelConnectClient.AUTH_REALM;
-                    String hex = PasswordManager.md5Hex(realm, user, pw);
-                    if (hex != null) {
-                        config.setProperty(pmd5, hex);
-                        config.remove(puser);
-                        config.remove(ppw);
-                    }
-                }
-            }
-            // New user/password
-            String auth = _otherOptions.get(I2PTunnelHTTPClientBase.PROP_AUTH);
-            if (auth != null && !auth.equals("false")) {
-                if (_newProxyUser != null && _newProxyPW != null &&
-                    _newProxyUser.length() > 0 && _newProxyPW.length() > 0) {
-                    String pmd5 = OPT + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_PREFIX +
-                                  _newProxyUser + I2PTunnelHTTPClientBase.PROP_PROXY_DIGEST_SUFFIX;
-                    String realm = _type.equals(TunnelController.TYPE_HTTP_CLIENT) ? I2PTunnelHTTPClient.AUTH_REALM
-                                                              : I2PTunnelConnectClient.AUTH_REALM;
-                    String hex = PasswordManager.md5Hex(realm, _newProxyUser, _newProxyPW);
-                    if (hex != null)
-                        config.setProperty(pmd5, hex);
-                }
-            }
-        }
-
-        if (TunnelController.TYPE_IRC_CLIENT.equals(_type) ||
-            TunnelController.TYPE_STD_CLIENT.equals(_type) ||
-            TunnelController.TYPE_STREAMR_CLIENT.equals(_type)) {
-            if (_targetDestination != null)
-                config.setProperty(TunnelController.PROP_DEST, _targetDestination);
-        } else if (TunnelController.TYPE_HTTP_SERVER.equals(_type) ||
-                   TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) {
-            if (_spoofedHost != null)
-                config.setProperty(TunnelController.PROP_SPOOFED_HOST, _spoofedHost);
-            for (String p : _httpServerOpts)
-                if (_otherOptions.containsKey(p))
-                    config.setProperty(OPT + p, _otherOptions.get(p));
-        }
-        if (TunnelController.TYPE_HTTP_BIDIR_SERVER.equals(_type)) {
-            if (_port != null)
-                config.setProperty(TunnelController.PROP_LISTEN_PORT, _port);
-            if (_reachableBy != null)
-                config.setProperty(TunnelController.PROP_INTFC, _reachableBy);
-            else if (_targetHost != null)
-                config.setProperty(TunnelController.PROP_INTFC, _targetHost);
-            else
-                config.setProperty(TunnelController.PROP_INTFC, "");
-        }
-
-        if (TunnelController.TYPE_IRC_CLIENT.equals(_type)) {
-            boolean dcc = _booleanOptions.contains(I2PTunnelIRCClient.PROP_DCC);
-            config.setProperty(OPT + I2PTunnelIRCClient.PROP_DCC,
-                               "" + dcc);
-            // add some sane server options since they aren't in the GUI (yet)
-            if (dcc) {
-                config.setProperty(OPT + PROP_MAX_CONNS_MIN, "3");
-                config.setProperty(OPT + PROP_MAX_CONNS_HOUR, "10");
-                config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_MIN, "5");
-                config.setProperty(OPT + PROP_MAX_TOTAL_CONNS_HOUR, "25");
-            }
-        }
-
-        if (!isClient(_type) || _booleanOptions.contains("persistentClientKey")) {
-            // As of 0.9.17, add a persistent random key if not present
-            String p = OPT + "inbound.randomKey";
-            if (!config.containsKey(p)) {
-                byte[] rk = new byte[32];
-                _context.random().nextBytes(rk);
-                config.setProperty(p, Base64.encode(rk));
-                p = OPT + "outbound.randomKey";
-                _context.random().nextBytes(rk);
-                config.setProperty(p, Base64.encode(rk));
-            }
-            // As of 0.9.18, add persistent leaseset keys if not present
-            // but only if we know the sigtype
-            p = OPT + "i2cp.leaseSetSigningPrivateKey";
-            Destination dest = getDestination(_tunnel);
-            if (dest != null && !config.containsKey(p)) {
-                try {
-                    SigType type = dest.getSigType();
-                    SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
-                    config.setProperty(p, type.name() + ':' + keys[1].toBase64());
-                    p = OPT + "i2cp.leaseSetPrivateKey";
-                    keys = KeyGenerator.getInstance().generatePKIKeys();
-                    config.setProperty(p, "ELGAMAL_2048:" + keys[1].toBase64());
-                } catch (GeneralSecurityException gse) {
-                    // so much for that
-                }
-            }
-        }
-
-        return config;
-    }
-    
-    private static final String _noShowOpts[] = {
-        "inbound.length", "outbound.length", "inbound.lengthVariance", "outbound.lengthVariance",
-        "inbound.backupQuantity", "outbound.backupQuantity", "inbound.quantity", "outbound.quantity",
-        "inbound.nickname", "outbound.nickname", "i2p.streaming.connectDelay", "i2p.streaming.maxWindowSize",
-        I2PTunnelIRCClient.PROP_DCC
-        };
-    private static final String _booleanClientOpts[] = {
-        "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen",
-        I2PTunnelClientBase.PROP_USE_SSL,
-        };
-    private static final String _booleanProxyOpts[] = {
-        I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH,
-        I2PTunnelHTTPClient.PROP_USE_OUTPROXY_PLUGIN,
-        I2PTunnelHTTPClient.PROP_USER_AGENT,
-        I2PTunnelHTTPClient.PROP_REFERER,
-        I2PTunnelHTTPClient.PROP_ACCEPT,
-        I2PTunnelHTTPClient.PROP_INTERNAL_SSL
-        };
-    private static final String _booleanServerOpts[] = {
-        "i2cp.reduceOnIdle", "i2cp.encryptLeaseSet", PROP_ENABLE_ACCESS_LIST, PROP_ENABLE_BLACKLIST,
-        I2PTunnelServer.PROP_USE_SSL,
-        I2PTunnelHTTPServer.OPT_REJECT_INPROXY,
-        I2PTunnelServer.PROP_UNIQUE_LOCAL,
-        "shouldBundleReplyInfo"
-        };
-    private static final String _otherClientOpts[] = {
-        "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.closeIdleTime",
-        "outproxyUsername", "outproxyPassword",
-        I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
-        I2PTunnelHTTPClientBase.PROP_AUTH,
-        I2PClient.PROP_SIGTYPE,
-        I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES,
-        // following are mostly server but could also be persistent client
-        "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
-        };
-    private static final String _otherServerOpts[] = {
-        "i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList",
-         PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY,
-         PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
-         PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE,
-        "inbound.randomKey", "outbound.randomKey", "i2cp.leaseSetSigningPrivateKey", "i2cp.leaseSetPrivateKey"
-        };
-    private static final String _httpServerOpts[] = {
-        I2PTunnelHTTPServer.OPT_POST_WINDOW,
-        I2PTunnelHTTPServer.OPT_POST_BAN_TIME,
-        I2PTunnelHTTPServer.OPT_POST_TOTAL_BAN_TIME,
-        I2PTunnelHTTPServer.OPT_POST_MAX,
-        I2PTunnelHTTPServer.OPT_POST_TOTAL_MAX
-        };
-
-    /**
-     *  do NOT add these to noShoOpts, we must leave them in for HTTPClient and ConnectCLient
-     *  so they will get migrated to MD5
-     *  TODO migrate socks to MD5
-     */
-    private static final String _otherProxyOpts[] = {
-        "proxyUsername", "proxyPassword"
-        };
-
-    protected static final Set<String> _noShowSet = new HashSet<String>(128);
-    protected static final Set<String> _nonProxyNoShowSet = new HashSet<String>(4);
-    static {
-        _noShowSet.addAll(Arrays.asList(_noShowOpts));
-        _noShowSet.addAll(Arrays.asList(_booleanClientOpts));
-        _noShowSet.addAll(Arrays.asList(_booleanProxyOpts));
-        _noShowSet.addAll(Arrays.asList(_booleanServerOpts));
-        _noShowSet.addAll(Arrays.asList(_otherClientOpts));
-        _noShowSet.addAll(Arrays.asList(_otherServerOpts));
-        _noShowSet.addAll(Arrays.asList(_httpServerOpts));
-        _nonProxyNoShowSet.addAll(Arrays.asList(_otherProxyOpts));
-    }
-
-    private void updateConfigGeneric(Properties config) {
-        config.setProperty(TunnelController.PROP_TYPE, _type);
-        if (_name != null)
-            config.setProperty(TunnelController.PROP_NAME, _name);
-        if (_description != null)
-            config.setProperty(TunnelController.PROP_DESCR, _description);
-        if (!_context.isRouterContext()) {
-            if (_i2cpHost != null)
-                config.setProperty(TunnelController.PROP_I2CP_HOST, _i2cpHost);
-            if ( (_i2cpPort != null) && (_i2cpPort.trim().length() > 0) ) {
-                config.setProperty(TunnelController.PROP_I2CP_PORT, _i2cpPort);
-            } else {
-                config.setProperty(TunnelController.PROP_I2CP_PORT, "7654");
-            }
-        }
-        if (_privKeyFile != null)
-            config.setProperty(TunnelController.PROP_FILE, _privKeyFile);
-        
-        if (_customOptions != null) {
-            StringTokenizer tok = new StringTokenizer(_customOptions);
-            while (tok.hasMoreTokens()) {
-                String pair = tok.nextToken();
-                int eq = pair.indexOf('=');
-                if ( (eq <= 0) || (eq >= pair.length()) )
-                    continue;
-                String key = pair.substring(0, eq);
-                if (_noShowSet.contains(key))
-                    continue;
-                // leave in for HTTP and Connect so it can get migrated to MD5
-                // hide for SOCKS until migrated to MD5
-                if ((!TunnelController.TYPE_HTTP_CLIENT.equals(_type)) &&
-                    (!TunnelController.TYPE_CONNECT.equals(_type)) &&
-                    _nonProxyNoShowSet.contains(key))
-                    continue;
-                String val = pair.substring(eq+1);
-                config.setProperty(OPT + key, val);
-            }
-        }
-
-        config.setProperty(TunnelController.PROP_START, _startOnLoad + "");
-
-        if (_tunnelQuantity != null) {
-            config.setProperty("option.inbound.quantity", _tunnelQuantity);
-            config.setProperty("option.outbound.quantity", _tunnelQuantity);
-        }
-        if (_tunnelDepth != null) {
-            config.setProperty("option.inbound.length", _tunnelDepth);
-            config.setProperty("option.outbound.length", _tunnelDepth);
-        }
-        if (_tunnelVariance != null) {
-            config.setProperty("option.inbound.lengthVariance", _tunnelVariance);
-            config.setProperty("option.outbound.lengthVariance", _tunnelVariance);
-        }
-        if (_tunnelBackupQuantity != null) {
-            config.setProperty("option.inbound.backupQuantity", _tunnelBackupQuantity);
-            config.setProperty("option.outbound.backupQuantity", _tunnelBackupQuantity);
-        }
-        if (_connectDelay)
-            config.setProperty("option.i2p.streaming.connectDelay", "1000");
-        else
-            config.setProperty("option.i2p.streaming.connectDelay", "0");
-        if (isClient(_type) && _sharedClient) {
-            config.setProperty("option.inbound.nickname", CLIENT_NICKNAME);
-            config.setProperty("option.outbound.nickname", CLIENT_NICKNAME);
-        } else if (_name != null) {
-            config.setProperty("option.inbound.nickname", _name);
-            config.setProperty("option.outbound.nickname", _name);
-        }
-        if ("interactive".equals(_profile))
-            // This was 1 which doesn't make much sense
-            // The real way to make it interactive is to make the streaming lib
-            // MessageInputStream flush faster but there's no option for that yet,
-            // Setting it to 16 instead of the default but not sure what good that is either.
-            config.setProperty("option.i2p.streaming.maxWindowSize", "16");
-        else
-            config.remove("option.i2p.streaming.maxWindowSize");
+        // This is easier than requiring TunnelConfig to talk to
+        // TunnelControllerGroup and TunnelController
+        _config.setDestination(getDestination(_tunnel));
+        return _config.getConfig();
     }
 
     ///